[Java – S3] Biến

Xin chào các bạn, lại gặp lại mọi người trong số thứ 3 của chuyên mục Lập trình Java. Trong phần này, mình và các bạn  sẽ cùng tìm hiểu về các loại biến, cách khai báo cũng như cách sử dụng chúng như thế nào. Nội dung chính bao gồm:

  • Biến là gì?
  • Phân loại biến
  • Khai báo vào khởi tạo biến
  • Literals
  • Constant (hằng số)

Biến là gì?

Như ta đã biết, biến là một địa chỉ lưu trữ, được liên kết với một kiểu dữ liệu, nguyên thủy hoặc tham chiếu. Các biến nguyên thủy (primitive variable) lưu giá trị thực trên stack, trong khi đó các biến tham chiếu lưu địa chỉ  của đối tượng trong bộ nhớ heap mà nó tham chiếu đến. Vì vậy, các biến nguyên thủy thường được gán giá trị mặc định của kiểu dữ liệu khi khai báo không khởi tạo. Các biến tham chiếu nếu không được khởi tạo, sẽ không tham chiếu tới đối tượng nào trong bộ  nhớ và nhận giá trị null.

su-khac-nhau-giua-kieu-du-lieu-ngyen-thuy-va-kieu-du-lieu-tham-chieu-trong-java
Hình 3.1 Sự khác nhau giữa kiểu dữ liệu nguyên thủy và kiểu dữ liệu tham chiếu trong Java

Biến tham chiếu được ví như chiếc điều khiển từ xa, có khả năng truy cập từ bên ngoài (class, package khác) đến các phương thức hay các biến instance thông qua toán tử ‘.’ (tất nhiên còn phụ thuộc vào access modifier của các phương thức hoặc biến đó).

Mảng (tập hợp các phần tử cùng kiểu dữ liệu) là một biến tham chiếu (một đối tượng), mặc dù nó có thể được khai báo là chứa các phần tử nguyên thủy.

Phân loại biến

Ngôn ngữ lập trình Java định nghĩa 4 loại biến:

  • Biến Instance (Non-Static Fields)
    • Là các biến không chứa từ khóa static, được khai báo bên trong class nhưng bên ngoài các phương thức
    • Các biến instance có thể truy xuất được mọi nơi trong class (trừ các hàm static), và những nơi bên ngoài class mà kiểu access modifier của biến đó cho phép
  • Biến Class (Static Fields)
    • Là các biến có chứa từ khóa static, được khai báo bên trong class nhưng bên ngoài các phương thức
    • Các biến static được cấp phát bộ nhớ một lần duy nhất khi class được load. Một class được load bởi khối ClassLoader trong máy ảo Java (JVM) khi một trường hợp cụ thể (một object, một instance) của class đó được khởi tạo, hay có một truy xuất đến các biến (hàm) static của class đó
    • Có giá trị không phụ thuộc vào số đối tượng được tạo ra (có giá trị như nhau đối với các đối tượng khác nhau)
    • Bộ nhớ cấp phát cho các biến static chỉ được giải phóng khi kết thúc chương trình. Vì vậy, các biến static nên được hạn chế sử dụng để tránh memory leak
    • Có khả năng truy cập các biến bên ngoài class mà không cần khởi tạo một đối tượng của class đó

    Biến instance và biến class đại diện cho các trường trong class. Các biến class đại diện cho các trường tĩnh, không thể thay đổi được (về mặt đối tượng, không phải về mặt giá trị), luôn đi theo class nên được gọi là biến class. Các biến instance  đại diện cho các trường có thể thay đổi được, các thể hiện khác nhau của class (các object, các instance) thì giá trị của biến instance (thuộc tính) khác nhau, nên được gọi là biến instance.

  • Biến Local
    • Biến local là các biến non-static bên trong một phương thức hay một khối (không thể khai báo một biến trong một phương thức hay một khối)
    • Các biến local không thể truy xuất trực tiếp từ bên ngoài phương thức mà nó được khai báo (nếu muốn truy xuất giá trị của biến local, ta có thể truy xuất gián tiếp thong qua giá trị trả về của phương thúc đó)
    • Bộ nhớ cấp phát cho các biến local được giải phóng ngay khi phương thức kết thúc
    • Các biến local không được chỉ định giá trị mặc định. Vì vậy ta phải gán cho biến local một giá trị trước khi sử dung
  • Tham số (Parameters)
    • Nếu như khái niệm tham số trong phần Java – S1 chưa làm các bạn clear, hãy hiểu đơn giản tham số (nếu có) là một hay một vài biến đi kèm theo phương thức và luôn phải truyền vào cho nó một giá trị (cùng kiểu dữ liệu) khi phương thức được gọi đến
    • Giá trị truyền vào đó gọi là đối số. Trong nhiều trường hợp, đối số có thể là một literal, một hang số (xem bên dưới), giá trị của một biến, thậm chí là giá trị trả về của một hàm khác. Xin nhắc lại, trong trường hợp sử biến, cái truyền vào là giá trị của biến đó chứ không phải ô nhớ của biến đó, vì thế giá trị của biến truyền vào không bị thay đổi

Khai báo và khởi tạo biến

Cú pháp:

        access_modifier static (đối với các biến class) data_type variable_name; // khai báo không khởi tạo
hoặc access_modifier static (đối với các biến class) data_type variable_name = value; // khai báo đồng thời khởi tạo

  • access_modifier(public, protected, hoặc private) là các mức truy cập, đại diện cho khả năng truy xuất đến biến. Nếu không điền gì thì mức truy cập là default
  • static: Từ khóa được thêm vào các biến static, không thêm vào biến instance và biến local.
  • data_type: Kiểu dữ liệu nguyên thủy hoặc tham chiếu
  • variable_name: tên biến, được đặt theo các quy ước đặt tên của Java, không trùng với các từ khóa
  • = : Toán tử gán, chỉ định giá trị bên phải cho biến ở bên trái
  • value Một giá trị xác định, đại diện cho kiểu dữ liệu của biến, có thể là một literal, một contants hoặc một đối tượng…
  •  Ký tự kết thúc câu lệnh

public static int age = 15;
private String mColor = "blue"; // private String mColor = new String ("color"); is OK
Dog bitch = new Dog();
...

Literals

Trong ví dụ trên, chúng ta có thể thấy các biến tham chiếu có thể sử dụng từ khóa new để khởi tạo. Tuy nhiên điều này không được áp dụng với các biến kiểu nguyên thủy. Chúng ta có thể truyền trực tiếp một giá trị cố định, không yêu cầu tính toán, đại diện cho một kiểu dữ liệu, cho các biến này. Các giá trị đó, người ta gọi là Literals.

int num1 = 4; // 4 is a literal
String str = "Hello World"; // "Hello World" is s literal.
int num2 = 3 * 5; // 3*5 is not literal.(1)

Hằng số (Constant)

Hằng là một đại lượng có giá trị không thể thay đổi trong một vòng đời của chương trình. Để khai báo hằng, người ra thêm vào khai báo biến từ khóa final static. Vì vậy, hằng có tất cả các tính chất của một biến static. Các hằng số hay dùng thường được khai báo chung trong một  lớp Constants, và được truy xuất khi nào cần thiết như một biến static.

Ngoài ra, từ khóa final cũng có thể thêm vào các biến instance, biến local, thậm chí là các tham số, với ý nghĩa “không thay đổi giá trị được” (điều này có vẻ hơi mâu thuẫn với khái niệm biến nhưng nhiều khi người ta vẫn coi các biến final như một biến). Trong phần hướng đối tượng trong Java, chúng ra còn sử dụng final cho phương thức (với ý nghĩa không thể ghi đè phương thức đó) và class (với ý nghĩa không thể kế thừa class).


public static final String INPUT = "This is s String constant";

[Java – S2] Các kiểu dữ liệu trong Java

Như ở post Java-S1, chúng ta đã biết mỗi ngôn ngữ lập trình đều có nhiều kiểu dữ liệu. Trong Java có 2 kiểu dữ liệu cơ bản: Kiểu dữ liệu nguyên thủy (Primitive data type) và kiểu dữ liệu tham chiếu (Reference/Object data type). Mỗi kiểu dữ liệu này lại được chia ra các kiểu dữ liệu khác nhau:

cac-kieu-du-lieu-trong-java

8 kiểu dữ liệu nguyên thủy (cơ sở):

Các kiểu dữ liệu nguyên thủy trong java được phân ra thành 3 nhóm chính:

  • Kiểu số nguyên bao gồm: byte, short, int, long  và char
  • Kiểu số thực dấu phẩy động bao gồm: double và float
  • Kiểu logic chỉ có 2 giá trị: true, false

Chi tiết về các kiểu được trình bày ở bảng dưới đây:

Bảng 2.1: Chi tiết thông số của các kiểu dữ liệu nguyên thủy trong Java
Primitive Data Type Wrapper Type Size Default Value Range
byte Byte 1 byte 0 -128  to  127
short Short 2 byte 0 -215  to  215-1
int Integer 4 byte 0  -23  to  231-1
long Long 8 byte 0L -263  to  263-1
float Float 4 byte 0.0f (2)
double Double 8 byte 0.0d
char Character 2 byte ‘\u0000’ 0 to 215-1 (3)
boolean Boolean Not defined(1) false true , false

Bảng trên cho ta giới hạn của các kiểu dữ liệu nguyên thủy. Các thông số này đều có thể kiểm tra được bằng cách truy xuất các hằng số trong các lớp Wrapper type. Tuy nhiên, chúng ta nên nhớ các thông số này để có thể sử dụng chúng một cách hợp lý và linh hoạt. Ví dụ nếu không nhớ giới hạn của kiểu dữ liệu, bạn cũng có thể gán cho biến một giá trị vượt quá giới hạn (giống như việc nhét một con voi vào trong tủ lạnh vậy), hoặc sau hàng loạt các tính toán giá trị của biến sẽ vượt quá giới hạn…

Wrapper type không phải là kiểu dữ liệu nguyên thủy, nó là kiểu dữ liệu tham chiếu, là một class. Các class này là đóng gói của các kiểu dữ liệu nguyên thủy. Nói cách khác, các phương thức, các hằng và các biến được thêm vào để thao tác với các kiểu dữ liệu nguyên thủy được dễ hơn (như việc truy xuất các thông số ở trên). Các wrapper class được tìm thấy trong Java API.

	int i = Integer.MAX_VALUE; // 2147483647
	byte b = Byte.MIN_VALUE; // -128
	int c1 = Character.MAX_VALUE; // 65535
	char c2 = Character.MAX_VALUE; // ?
	short s = Short.MIN_VALUE; // -32768
	double d = Double.MAX_VALUE; // 1.7976931348623157E308
	

==============================

(1) Kiểu dữ liệu boolean trong Java chỉ nhận 2 giá trị true hoặc false, kích thước của nó là một cái gì đó được xác định chính xác. Tuy nhiên, vì nó yêu cầu ít nhất 1 bit, nên nhiều tài liệu lấy đó làm kích thước của kiểu boolean.
(2) float và double là các kiểu số thực dấu chấm động (floating-point type) tuân theo chuẩn IEEE 754. Số dấu chấm động không có phạm vi xác định, nó có thể nhận một trong các giá trị:

  • Vô cực âm(Negative infinity)
  • Vô cực dương (Positive infinity)
  • Số âm hữu hạn ( Negative, finite values)
  • Số dương hữu hạn (Positive, finite values)
  • Số 0 âm hoặc số 0 dương: Xem thêm tại đây.
  • NaN (Not a Number): Các giá trị không thể xác định trong khoảng vô cực âm đến vô cực dương (ví dụ phép toán chi cho 0). NaN được đưa ra để sử dụng trong một số trường hợp đặc biệt khi so sánh các giá trị thực với nhau và thường cho ra kết quả false.

(3) char là kiểu ký tự có giá trị trong khoảng ‘/u0000’ đến ‘/uffff’ hay từ 0 đến 65 535. Một biến kiểu char sẽ có giá trị là một ký tự Unicode.

Các kiểu dữ liệu tham chiếu
Kiểu dữ liệu tham chiếu là một class, một mảng, một interface hay một enum bất kì. Kiểu dữ liệu tham chiếu có kích thước phụ thuộc vào nhiều yếu tố đặc biệt kiến trúc của máy ảo Java (JVM).

[Java – S1] Các khái niệm cơ bản trong Java

Xin chào các bạn, lại là mình, thằng admin đây. Mở đầu cho chuyên mục lập trình Java, chúng ta sẽ đi tìm hiểu một số khái niệm cơ bản trước khi tạo một Project đầu tiên, gọi là cho đỡ bỡ ngỡ. Một số khái niệm rất đơn giản, mình chỉ dịch từ tiếng Anh sang tiếng Việt. Tuy nhiên, đây đều là các khái niệm rất quang trọng, cần phải hiểu đúng nghĩa, đúng bản chất. Đồng thời cũng là để phòng trường hợp khi có người đề cập đến (đi phỏng vấn chẳng hạn). Mình cũng khuyến khích các bạn nên đọc tài liệu tiếng Anh để làm quen với cách tìm hiểu và tìm kiếm trên Google bằng tiếng Anh. Và một lý do nữa là “hầu hết những thứ hay ho đều được viết bằng tiếng Anh” (trích dẫn ở đâu không nhớ,hehe).

  • IDE (Integrated Development Environment)
    IDE là một phần mềm ứng dụng cung cấp các công cụ để lập trình viên làm việc dễ dàng hơn. Một IDE thông thường phải đảm bảo 3 chức năng cơ bản là edit code, build code và debug. Ngoài ra, hầu hết các IDE hiện đại còn có cơ chế tự động hoàn thành code thông minh. Có nhiều IDE dùng để lập trình Java như Eclipse, Netbean, IntelliJ… nhưng phổ biến nhất vẫn là Eclipse và Netbean. S-lightning sẽ hướng dẫn các bạn cách cài đặt và lập trình trên môi trường Eclipse vì nó khá nhẹ và nhanh, rất phù hợp nếu bạn có một chiếc máy tính cấu hình không quá khỏe.
  • JDK (Java Development Kit)
    Java là ngôn ngữ lập trình mức cao (high – level programming language). Nhiều tài liệu dùng từ “bậc cao” không sai, nhưng rất dễ gây cho chúng ta sự hiểu lầm thành “cao cấp và đẳng cấp”. Ngôn ngữ lập trình mức cao được viết theo cách gần với ngôn ngữ tự nhiên nhất (ngôn ngữ mà chúng ta dùng hàng ngày), giúp quá trình phát triển phần mềm trở nên đơn giản và dễ hiểu hơn so với các ngôn ngữ lập trình mức thấp hơn như Assembly, C, C++. Tuy nhiên, càng tự nhiên bao nhiêu thì càng khó để cho máy tính hiểu bấy nhiêu, chắc chắn rồi. Vì vậy, để máy tính hiểu được, người ta tạo ra một trình biên dịch javac (java compiler) biên dịch mã nguồn(file .java) thành mã máy (java bytecode) và một máy ảo java JVM (Java Virtual Machine) chịu trách nhiệm đọc và chạy bytecode. Một trình thông dịch (interpreter) là một thành phần của máy ảo Java. Đây cũng là lý do các ngôn ngữ lập trình mức thấp hơn (được biên dịch trực tiếp vào mã nguồn gốc, mã mà máy tính có thể hiểu trực tiếp) có tốc độ chạy cao hơn.
    JDK là một bộ các công cụ phát triển phần mềm (Software Development Kit) trên nền tảng Java, trong đó có chứa java compiler và java interpreter được đề cập ở trên.
    Xem thêm về JDK tại đây.
  • Variable and Data type (Biến và các kiểu dữ liệu)
    Kiểu dữ liệu là một khái niệm đã quá quen thuộc vì nó được áp dụng phổ biến trong rất nhiều ngành khoa học kỹ thuật. Tuy nhiên, vì phần này rất quan trọng nên mình sẽ nhắc lại một chút. Kiểu dữ liệu được kiểu đơn giản là các kiểu khác nhau để phân biệt các loại dữ liệu được lưu trong máy vi tính. Giống như quần thì có quần dài, quần đùi, quần ngố… Vậy, quần như thế nào gọi nào gọi là quần dài, ngắn đến mức nào thì gọi là quần đùi, ngắn hết cỡ thì sao, hay thế nào thì gọi là quần, thế nào thì gọi là váy? Dĩ nhiên, các kiểu dữ liệu khác nhau phải được phân biệt với nhau giống như vậy, chúng phân biệt với nhau bởi kích thước và giá trị mà chúng đại diện.
    Ví dụ: Kiểu dữ liệu boolean đại diện cho các giá trị logic (true false); kiểu dữ liệu int đại diện cho các số nguyên có giá trị từ -231 đến 231-1 (ngoài khoảng này là một kiểu số khác)…
    Một điểm lưu ý khác là mỗi ngôn ngữ lập trình có thể sử dụng các thuật ngữ khác nhau để đặt tên cho các kiểu dữ liệu, thậm chí có thể có khoảng giá trị khác nhau cho cùng một kiểu dữ liệu.
    Biến (variable) là một vị trí vùng nhớ có chứa giá trị hoặc không chứa giá trị. Việc khai báo biến đồng nghĩa với việc ra lệnh cho bộ nhớ cấp phát không gian lưu trữ cho biến đó. Giá trị của biến được truy xuất bởi một tên định danh, thường gọi là tên biến. Mỗi biến có một kiểu dữ liệu khác nhau, được khai báo đồng thời với tên biến. Giá trị của biến có thể thay đổi trong khi chạy chương trình, nhưng kiểu dữ liệu của biến thì không thay đổi trong suốt chương trình.
  • Statement (Câu lệnh)
    Câu lệnh là một thành phần độc lập nhỏ nhất bảo chương trình phải làm gì. Một câu lệnh có thể là một khai báo biến, gán giá trị cho biến, gọi hàm… Ta nhận biết một câu lệnh được kết thúc khi gặp dấu ;.
  • Hàm
    Hay còn gọi là phương thức (method) là một khối lệnh được đặt trong cặp dấu { }. Một hàm thường thực hiện một chức năng nào đó trong chương trình nên còn được gọi là Function và có thể có hoặc không có kiểu trả về (trả về một kiểu dữ liệu hoặc trả về kiểu void).
  • API (Application Programming Interface)
    Tạm dịch là giao diện lập trình ứng dụng, là tập hợp các hàm, phương thức, giao thức , thủ tục, và công cụ, dùng để phần mềm có thể giao tiếp với hệ điều hành, hay một phần mềm, dịch vụ ứng dụng khác. Trong đó, truy xuất (hay cứ hiểu là gọi) đến một tập các hàm hay dùng là một mục đích chính của API.
    Một số API phổ biến: GoogleMap API. Facebook API, Youtube API…
  • Javadoc là gì?
    Bộ tài liệu tham khảo đi kèm với các API, được tạo ra bởi cha đẻ của Java (Sun Microsystems), nay thuộc Oracle. Khi lập trình, chúng ta thường sử dụng Javadoc để biết được các hàm có chức năng gì, tham số truyền vào, kiểu trả về là gì… Ngoài ra, trong quá trình phát triển phần mềm, việc đồng thời viết Javadoc cho phần source code mình viết ra rất được khuyến khích. Vì điều đó giúp cho việc phát triển tiếp ứng dụng bởi một người khác trở nên nhẹ nhàng hơn, hay chính bản thân các lập trình viên cũng cần đọc lại code của mình.
  • Tham số (parametter) và đối số (argument)
    Tham số là một dạng đặc biệt của biến, được sử dụng để truyền các giá trị như một đầu vào trong một phương thức. Giá trị truyền vào hàm qua tham số được gọi là đối số. Khi gọi đến một phương thức có tham số, ta buộc phải truyền vào đối số cho các tham số đó.
  • Access modifier (Mức truy cập)
    Là một keyword thiết lập khả năng truy xuất đến một class, một phương thức hay một biến…trong sourcecode. Có 4 mức access modifier: public, protected, private, default. Ý nghĩa và cách dùng của các modifier này sẽ được trình bày chi tiết ở các bài sau.
  • Class và main method
    Class là đơn vị đóng gói nhỏ nhất trong Java (xem thêm mục Hướng đối tượng trong Java). Mọi câu lệnh, hàm đều được đặt trong class. Một chương trình có thể chứa nhiều class. Class chứa hàm main() được thực thi trước tiên. Khởi chạy một chương trình đồng nghĩa với việc bạn đang thực thi class đó, tuần tự từ trên xuống dưới, bắt đầu từ hàm main() cho đến khi hàm main() kết thúc hoặc phát sinh lỗi trong quá trình compile time, runtime. Hàm main() có cấu trúc không đổi:

    public static void main(String[] args){
    // Your code here.
    }
  • Compile time và Runtime
    Vòng đời của một ứng dụng Java (Java Application Life Cycle) gồm 2 khoảng thời gian Compile time (thời gian compiler biên dịch mã nguồn thành mã máy) và Runtime (khoảng thời gian thực thi mã máy) được mô tả cụ thể ở hình dưới đây:

    *Các bài viết và source code trên blog thuộc sở hữu của S-lighting, các bạn coppy và sử dụng vui lòng ghi nguồn http://s-lightning.com/