Giải thích biến Java: Kiểu dữ liệu, phạm vi, var, final và các lỗi thường gặp

目次

1. Biến (Variables) là gì trong Java?

Khi bạn bắt đầu học Java, một trong những khái niệm quan trọng đầu tiên bạn gặp phải là “biến”.
Nói ngắn gọn, biến là một container dùng để tạm thời lưu trữ các giá trị (dữ liệu) trong chương trình để bạn có thể tái sử dụng chúng bao nhiêu lần tùy ý.

Tuy nhiên, trong Java, bạn sẽ nhanh chóng gặp khó khăn nếu chỉ ghi nhớ biến là “các hộp”.
Bởi vì các biến trong Java được xử lý cùng với một “kiểu” (type) (loại dữ liệu).

Trong phần này, chúng ta sẽ sắp xếp vai trò của biến và cách suy nghĩ đặc thù của Java (tầm quan trọng của kiểu) dành cho người mới bắt đầu.

1.1 Những gì bạn có thể làm với biến (Lưu giá trị và tái sử dụng chúng)

Với biến, một chương trình có thể chuyển từ “các phép tính một lần duy nhất” sang “xử lý có ý nghĩa”.

Ví dụ, bạn có thể viết số trực tiếp như sau:

System.out.println(100 + 20);

Cách này hoạt động, nhưng các giá trị không có ý nghĩa, vì vậy người đọc mã sau này (kể cả bạn trong tương lai) sẽ gặp khó khăn.
Bạn sẽ không biết “100 là gì?” hay “20 là gì?”

Bằng cách sử dụng biến, bạn có thể gán ý nghĩa cho các giá trị.

int price = 100;
int tax = 20;
System.out.println(price + tax);

Sử dụng biến như vậy mang lại các lợi ích sau:

  • Dễ hiểu ý định hơn (các tên như price và tax truyền tải ý nghĩa)
  • Bạn có thể tái sử dụng giá trị nhiều lần (không cần viết lại cùng một giá trị liên tục)
  • Dễ thay đổi sau này (nếu tỷ lệ thuế thay đổi, bạn chỉ cần thay đổi giá trị của biến)
  • Bạn có thể giữ các kết quả trung gian (chia logic phức tạp thành các bước nhỏ hơn)

Đặc biệt đối với người mới bắt đầu, việc nghĩ “biến = đặt tên cho giá trị” là hợp lý.
Tuy nhiên, trong Java, khái niệm tiếp theo—“kiểu”—luôn xuất hiện như một bộ.

1.2 Các biến Java được sử dụng cùng với “Kiểu” (Type Safety)

Trong Java, các biến thường được tạo ra theo dạng sau:

type variableName = value;

Ví dụ:

int age = 20;
String name = "Sagawa";

Điểm then chốt ở đây là kiểu.

  • int là số nguyên (ví dụ: 20, 100, -5)
  • String là chuỗi ký tự (ví dụ: “Hello”, “Java”)

Trong Java, khi bạn tạo một biến, bạn phải khai báo rõ ràng “kiểu giá trị nào mà biến này có thể chứa”.
Nhờ hệ thống này, Java có những ưu điểm như sau.

“Type‑Safe” có nghĩa là gì?

Vì bạn chỉ định kiểu, Java có thể ngăn bạn khi cố gắng gán giá trị sai kiểu vào một biến.

Ví dụ, nếu bạn cố gắng gán một chuỗi vào biến kiểu int, bạn sẽ nhận được lỗi biên dịch.

int age = "20"; // This is an error

Điều này không phải là phiền phức—ngược lại, nó thực sự hữu ích.
Thay vì “bị lỗi sau khi chạy chương trình”, bạn có thể “phát hiện lỗi trước khi chạy”.

Người mới bắt đầu đôi khi cảm thấy “Java có quá nhiều lỗi” vì điều này, nhưng nếu bạn thay đổi cách nhìn:

  • Java phát hiện sự không khớp kiểu sớm
  • Dự án càng lớn, các kiểu càng giúp giảm thiểu tai nạn

Ý tưởng này—“kiểu và biến luôn đi kèm”—là một trong những nền tảng cơ bản nhất của Java.

Lưu ý: Ngay cả với “Biến”, cũng có “Giá trị” và “Tham chiếu”

Các biến Java được xử lý chủ yếu theo hai cách:

  • Kiểu nguyên thủy : lưu trữ trực tiếp giá trị (ví dụ: int, double, boolean)
  • Kiểu tham chiếu : không lưu trữ giá trị trực tiếp, mà lưu trữ vị trí (tham chiếu) nơi giá trị được lưu (ví dụ: String, mảng, lớp)

Chúng ta sẽ không đi sâu vào đây, nhưng đây là một điểm gây nhầm lẫn phổ biến cho người mới, vì vậy cần đề cập ngay từ đầu.

Ví dụ, String là một kiểu tham chiếu.
Nó có vẻ như bạn đang “lưu một chuỗi”, nhưng thực tế, bạn đang giữ “một tham chiếu tới một đối tượng chuỗi”.

Sự khác biệt này cũng liên quan tới các chủ đề bạn sẽ học sau, như “== vs equals” và “cách final hoạt động”.

2. Những điều cơ bản về biến: Sự khác nhau giữa khai báo, gán và khởi tạo

Để hiểu đúng các biến trong Java,
cần sắp xếp rõ ràng sự khác nhau giữa “khai báo” (declaration), “gán” (assignment) và “khởi tạo” (initialization).

Beginners often use these three words as if they mean the same thing,
but in reality, they have clearly different roles.

Nếu bạn để điều này mơ hồ,
bạn sẽ dễ rơi vào trạng thái
“I don’t know why it’s an error” hoặc “I don’t know why I can’t use it.”

2.1 Khai báo: Chuẩn bị sử dụng một biến

Khai báo có nghĩa là nói với Java:
“Tôi sẽ sử dụng một biến với tên này,” và “kiểu của nó là như vậy.”

Cách khai báo cơ bản nhất trông như sau:

int count;

Tại thời điểm này, chỉ có hai điều đã xảy ra:

  • Tạo một biến có tên count
  • Quyết định rằng kiểu của nó là int (số nguyên)

Chưa có giá trị nào được lưu trữ.

Đây là một hiểu lầm phổ biến của người mới:
chỉ khai báo một biến không làm cho nó “sẵn sàng sử dụng.”

2.2 Gán giá trị: Đặt một giá trị vào biến

Gán giá trị có nghĩa là đặt một giá trị vào một biến đã được khai báo.

count = 10;

Dấu = này không có nghĩa là “bằng” trong toán học.
Nó có nghĩa là đặt giá trị ở bên phải vào biến ở bên trái.

Vì vậy luồng thực hiện sẽ như sau:

int count;   // declaration
count = 10;  // assignment

Đó là ý chính.

Lưu ý: Bạn không thể gán cho một biến chưa được khai báo

Mã sau sẽ gây ra lỗi:

count = 10; // Error: count has not been declared

Trong Java, thứ tự phải là khai báo → gán giá trị.

2.3 Khởi tạo: Gán giá trị ngay khi khai báo

Khởi tạo có nghĩa là thực hiện khai báo và gán giá trị đồng thời.

int count = 10;

Điều này về mặt ngữ nghĩa tương đương với hai dòng sau:

int count;
count = 10;

Trong Java—cả trong công việc thực tế và trong quá trình học—
trong hầu hết các trường hợp, bạn có thể yên tâm sử dụng “khởi tạo”.

Đó là vì nó mang lại các lợi ích sau:

  • Biến trở nên có thể sử dụng ngay khi nó được tạo
  • Nó ngăn ngừa lỗi do biến chưa được khởi tạo
  • Mã nguồn trở nên dễ đọc hơn

Vì vậy, với người mới, nên hình thành thói quen
“khởi tạo biến ngay khi khai báo chúng.”

2.4 Tại sao bạn không thể sử dụng một biến chỉ với khai báo (Quy tắc biến cục bộ)

Trong Java, có những quy tắc rất nghiêm ngặt đối với biến cục bộ (biến trong các phương thức).
Java đặc biệt nghiêm khắc ở đây.

Mã sau sẽ gây ra lỗi biên dịch:

int number;
System.out.println(number); // Error

Lý do rất rõ ràng:

Biến cục bộ không thể được sử dụng nếu chúng chưa được khởi tạo

Quy tắc đó tồn tại.

Java coi trạng thái “chúng ta không biết giá trị bên trong biến” là một trạng thái nguy hiểm và ngăn chặn nó như một lỗi trước khi thực thi.

Điều này có thể cảm thấy nghiêm khắc đối với người mới, nhưng trong phát triển quy mô lớn, nó là một cơ chế an toàn rất quan trọng.

2.5 Khai báo nhiều biến cùng lúc (Cẩn thận)

Trong Java, bạn có thể khai báo nhiều biến cùng kiểu cùng lúc.

int x, y, z;

Bạn cũng có thể viết như sau:

int a = 1, b = 2, c = 3;

Cú pháp này đúng, nhưng nó có thể làm giảm khả năng đọc, vì vậy hãy cẩn thận.

Đặc biệt đối với người mới và phát triển theo nhóm, an toàn hơn khi viết:

int a = 1;
int b = 2;
int c = 3;

với một biến mỗi dòng.

2.6 Tóm tắt phần

Các điểm chính cần nhớ từ phần này:

  • Khai báo : quyết định tên và kiểu của biến
  • Gán giá trị : đặt một giá trị vào biến
  • Khởi tạo : khai báo và gán giá trị đồng thời
  • Biến cục bộ không thể được sử dụng nếu chưa được khởi tạo
  • Đối với người mới, hãy sử dụng “khai báo + khởi tạo” làm mặc định

3. Hiểu về “Kiểu” của biến (Kiểu nguyên thủy và Kiểu tham chiếu)

Khi làm việc với các biến Java, bạn không thể tránh khỏi việc hiểu các kiểu. Điều thường làm người mới bối rối là:

  • Tại sao có quá nhiều kiểu
  • Tại sao các số có cả intdouble
  • String khác gì so với các kiểu số

Trong phần này, chúng ta sẽ sắp xếp các kiểu Java từ hai góc độ:
“kiểu nguyên thủy” và “kiểu tham chiếu.”

3.1 Kiểu Java Rơi Vào Hai Danh Mục Lớn

Các kiểu biến Java có thể được phân loại rộng rãi thành hai danh mục sau:

  1. Kiểu nguyên thủy
  2. Kiểu tham chiếu

Khi bạn hiểu được cách phân loại này, các chủ đề bạn sẽ học sau—như “== vs equals,” “cách final hoạt động,” và “cách bộ nhớ hoạt động”—sẽ trở nên dễ hiểu hơn rất nhiều.

3.2 Kiểu Nguyên Thủy Là Gì? (Các Kiểu Giữ Giá Trị Tự Mình)

Kiểu nguyên thủycác kiểu giữ giá trị trực tiếp.

Hãy xem một vài kiểu nguyên thủy phổ biến:

int count = 10;
double price = 19.99;
boolean isActive = true;

Các biến này lưu trữ
“giá trị tự nó” bên trong biến.

Các Kiểu Nguyên Thủy Thường Dùng

TypeMeaningExample Use
intIntegerCounts, age, number of times
longLarge integerAmounts of money, IDs
doubleDecimalPrices, ratios
booleanTrue/falseFlag checks
charSingle characterCharacter processing

Đối với người mới bắt đầu, chỉ cần nắm vững int, double, và boolean là đủ.

3.3 Đặc Điểm và Lưu Ý Của Kiểu Nguyên Thủy

Kiểu nguyên thủy có các đặc điểm sau:

  • Chúng giữ giá trị tự nó
  • Chúng xử lý nhanh
  • Bạn không thể gán null
  • Kích thước cố định

Ví dụ, trong đoạn mã dưới đây,
ab giữ các giá trị hoàn toàn độc lập.

int a = 5;
int b = a;
b = 10;

Trong trường hợp này:

  • a vẫn là 5
  • b thay đổi thành 10

Đó là kết quả.

Điều này xảy ra vì giá trị tự nó đã được sao chép.

3.4 Kiểu Tham Chiếu Là Gì? (Các Kiểu Xử Lý Vị Trí Lưu Trữ Giá Trị)

Ngược lại, kiểu tham chiếu
các kiểu không xử lý giá trị trực tiếp, mà xử lý vị trí (tham chiếu) nơi giá trị tồn tại.

Các kiểu tham chiếu điển hình bao gồm:

String name = "Java";
int[] numbers = {1, 2, 3};
  • String
  • Mảng
  • Lớp
  • Giao diện
  • Các collection như List / Map

Tất cả những thứ này đều là kiểu tham chiếu.

Hiểu Kiểu Tham Chiếu Bằng Một Hình Ảnh

Một biến kiểu tham chiếu giữ:

  • không phải đối tượng thực tế, mà
  • một “địa chỉ” chỉ tới nơi đối tượng thực tế được lưu trữ

Đó là mô hình tư duy.

3.5 Điều Gì Xảy Ra Khi Bạn Gán Kiểu Tham Chiếu?

Xem đoạn mã sau:

String a = "Hello";
String b = a;
b = "World";

Trong trường hợp này:

  • a là “Hello”
  • b là “World”

Đó là kết quả.

Nếu chỉ nhìn vào đây, bạn có thể nghĩ,
“Điều này không giống như kiểu nguyên thủy sao?”

Vậy còn ví dụ tiếp theo thì sao?

int[] array1 = {1, 2, 3};
int[] array2 = array1;

array2[0] = 100;

Kết quả là:

  • array1[0] → 100
  • array2[0] → 100

Điều này xảy ra vì array1 và array2 tham chiếu tới cùng một mảng.

Với kiểu tham chiếu:

  • Gán biến có thể làm cho chúng “trỏ tới cùng một đối tượng thực tế”
  • Thay đổi nội dung có thể ảnh hưởng đến mọi thứ tham chiếu tới nó

Đó là đặc điểm then chốt.

3.6 Các Điểm Nhầm Lẫn Thường Gặp Của Người Mới

Hãy tóm tắt các điểm gây bối rối phổ biến:

  • Kiểu nguyên thủy → giá trị được sao chép
  • Kiểu tham chiếu → tham chiếu (vị trí) được sao chép

Sự khác biệt này liên quan chặt chẽ tới các chủ đề sau:

  • Sự khác nhau giữa ==.equals()
  • Điều gì xảy ra khi bạn dùng final
  • Cách truyền giá trị làm đối số phương thức ảnh hưởng tới phạm vi và hành vi

Hiện tại, chỉ cần nhớ điều này là đủ:
kiểu tham chiếu đôi khi có thể chia sẻ cùng một đối tượng thực tế.

3.7 Tóm Tắt Phần

  • Kiểu Java được chia thành kiểu nguyên thủykiểu tham chiếu
  • Kiểu nguyên thủy xử lý “giá trị tự nó”
  • Kiểu tham chiếu xử lý “vị trí (tham chiếu) của giá trị”
  • Với kiểu tham chiếu, việc gán và thay đổi có thể ảnh hưởng tới các biến khác

4. Các Loại Biến (Nơi Chúng Sống) và Phạm Vi (Nơi Chúng Có Hiệu Lực)

Trong Java, các biến không chỉ khác nhau về “kiểu.”
Hành vi của chúng còn thay đổi tùy thuộc vào nơi chúng được khai báo (nơi chúng “sống”).

Nếu bạn không hiểu sự khác biệt này, bạn có thể bị nhầm lẫn bởi những thứ như:

.

  • “Tôi không thể sử dụng biến mà tôi đã khai báo trước đó”
  • “Nó có cùng tên, nhưng giá trị lại khác”
  • “Tôi không hiểu tại sao tôi không thể tham chiếu tới nó ở đây”

Trong phần này, chúng ta sẽ sắp xếp
các loại biếnphạm vi cho người mới bắt đầu.

4.1 Phạm vi là gì? (Nơi một biến có thể được sử dụng)

Phạm vi có nghĩa là
khoảng nơi một biến có thể được sử dụng.

Trong Java, phạm vi được xác định một cách chặt chẽ bởi:

  • nơi biến được khai báo
  • phạm vi của các dấu ngoặc nhọn { }

Quy tắc cơ bản rất đơn giản:

Một biến chỉ có thể được sử dụng bên trong khối mà nó được khai báo

4.2 Biến cục bộ (Biến trong phương thức)

Biến cục bộ là các biến được khai báo bên trong các phương thức hoặc khối (như iffor).

public void sample() {
    int x = 10;  // local variable
    System.out.println(x);
}

Biến x này có các đặc điểm sau:

  • Chỉ hợp lệ bên trong phương thức sample
  • Biến mất khi phương thức kết thúc

Ví dụ về Phạm vi với Các Khối

if (true) {
    int y = 5;
    System.out.println(y);
}

System.out.println(y); // Error

y được khai báo bên trong khối if, nên không thể sử dụng nó bên ngoài khối đó.

Các Quy tắc Quan trọng cho Biến Cục bộ

Biến cục bộ có các quy tắc nghiêm ngặt:

  • Bạn không thể sử dụng chúng nếu chưa được khởi tạo
  • Không có giá trị mặc định nào được gán tự động
    int value;
    System.out.println(value); // compile-time error
    

Đây là một phần của thiết kế an toàn của Java để ngăn việc sử dụng “giá trị chưa định nghĩa”.

4.3 Trường (Biến thể hiện)

Trường là các biến được khai báo bên trong một lớp nhưng ngoài các phương thức.

public class Sample {
    int count;  // field (instance variable)

    public void printCount() {
        System.out.println(count);
    }
}

Trường có các đặc điểm khác với biến cục bộ:

  • Mỗi đối tượng của lớp có một bản sao riêng
  • Chúng được khởi tạo tự động với các giá trị mặc định
  • Có thể được sử dụng từ bất kỳ phương thức nào trong lớp

Giá trị Mặc định cho Trường

Tùy thuộc vào kiểu dữ liệu, các giá trị mặc định sau sẽ được đặt tự động:

  • int → 0
  • double → 0.0
  • boolean → false
  • Kiểu tham chiếu → null

Vì vậy đoạn mã sau không gây lỗi:

public class Sample {
    int count;

    public void print() {
        System.out.println(count); // 0
    }
}

Tuy nhiên, phụ thuộc quá nhiều vào giá trị mặc định là không được khuyến khích.
Việc khởi tạo rõ ràng sẽ làm cho ý định của mã trở nên minh bạch hơn.

4.4 Biến static (Biến lớp)

Một biến có static trở thành
một biến được chia sẻ trên toàn bộ lớp.

public class Counter {
    static int total = 0;
}

Biến total này có các đặc điểm sau:

  • Chỉ có một thể hiện duy nhất cho mỗi lớp
  • Được chia sẻ bởi tất cả các đối tượng

Mô hình Tư duy cho Biến static

  • Biến thể hiện → ví tiền của mỗi người
  • Biến static → két sắt chung của công ty

Mô hình này giúp dễ hiểu hơn.

Lưu ý khi dùng Biến static

Biến static hữu ích, nhưng lưu trữ quá nhiều trạng thái có thể gây lỗi.

  • Khó theo dõi nơi các thay đổi đã xảy ra
  • Việc kiểm thử trở nên khó khăn hơn
  • Các vấn đề có khả năng xuất hiện trong xử lý đồng thời

Đối với người mới bắt đầu, nên hạn chế sử dụng cho các mục đích như:

  • Hằng số
  • Các giá trị cấu hình chung

và các mục đích tương tự.

4.5 Cẩn thận với các biến có cùng tên (Shadowing)

Xem đoạn mã sau:

public class Sample {
    int value = 10;

    public void test() {
        int value = 5;
        System.out.println(value);
    }
}

Trong trường hợp này, kết quả là 5.

Điều này xảy ra vì
biến cục bộ đang ẩn trường (shadowing).

Vì đây là một nguồn gây nhầm lẫn phổ biến cho người mới bắt đầu:

  • Tránh trùng tên biến
  • Không tái sử dụng cùng một tên trừ khi có mục đích rõ ràng

Kỷ luật này rất quan trọng.

4.6 Tóm tắt phần

  • Scope nghĩa là “phạm vi mà một biến có thể được sử dụng”
  • Các biến cục bộ chỉ hợp lệ bên trong các khối và phải được khởi tạo
  • Các trường được tự động gán giá trị mặc định
  • Các biến static được chia sẻ trên toàn lớp
  • Hãy cẩn thận với việc che khuất (shadowing) do các biến có cùng tên gây ra

5. Sử dụng var (Suy luận kiểu) Đúng cách trong Java 10+

Được giới thiệu trong Java 10, var
một cách để cho trình biên dịch suy luận kiểu của biến.

Nó có thể trông tiện lợi, nhưng nếu lạm dụng, nó có thể dễ dàng dẫn đến “mã khó đọc.”
Vì vậy, điều quan trọng là phải hiểu các quy tắc và khi nào nên sử dụng nó.

5.1 var Là Gì? (Cách Hoạt Động Mà Không Cần Viết Kiểu)

Trong Java thông thường, bạn khai báo một biến như thế này:

String message = "Hello";

Với var, bạn có thể viết:

var message = "Hello";

Tại thời điểm này, Java xem xét bên phải "Hello" và quyết định:

  • Biến này có kiểu String

Điểm quan trọng là var không phải là kiểu động.

  • Kiểu được xác định tại thời điểm biên dịch
  • Kiểu không thay đổi tại thời điểm chạy

Vì vậy, hãy nghĩ về var như “cách viết tắt cho phép bạn bỏ qua kiểu.”

5.2 Nơi Bạn Có Thể và Không Thể Sử Dụng var

var chỉ có thể được sử dụng ở những nơi được định nghĩa nghiêm ngặt.

Nơi Bạn Có Thể Sử Dụng Nó

  • Chỉ biến cục bộ
    public void sample() {
        var count = 10;
    }
    

Nơi Bạn Không Thể Sử Dụng Nó

Tất cả các cách sử dụng sau đây đều dẫn đến lỗi:

var x;                 // No initialization → error
var y = null;          // Cannot infer the type → error

class Sample {
    var value = 10;    // Cannot be used for fields → error
}

Lý do cho những hạn chế này là rõ ràng: để tránh giảm khả năng đọc và an toàn.

5.3 Lợi Ích Của Việc Sử Dụng var

var tỏa sáng khi
kiểu có xu hướng dài.

ArrayList<String> list = new ArrayList<String>();

Với var:

var list = new ArrayList<String>();

Nó trở nên sạch sẽ hơn nhiều.

Nói cách khác, var hữu ích khi:

  • Generics dài
  • Kiểu rõ ràng từ bên phải

Sử dụng theo cách này, nó có thể cải thiện khả năng đọc.

5.4 Những Sai Lầm Của var (Những Lỗi Thường Gặp Của Người Mới Bắt Đầu)

var tiện lợi, nhưng nếu người mới lạm dụng nó, các vấn đề sau có thể xảy ra.

Kiểu Trở Nên Khó Thấy

var data = getData();

Từ dòng này thôi, bạn không thể biết:

  • Dữ liệu có kiểu gì
  • Nó chứa gì

Trong những trường hợp như vậy, an toàn hơn là viết kiểu một cách rõ ràng.

Data data = getData();

Nó Có Thể Trở Thành Kiểu Không Mong Muốn

var number = 10;   // int

Người mới thường gặp phải các trường hợp như,
“Tôi nghĩ nó sẽ là long.”

Khi bạn không viết kiểu một cách rõ ràng, ý định của bạn và kiểu suy luận thực tế có thể khác biệt, vì vậy hãy cẩn thận.

5.5 Cách Quyết Định Có Nên Sử Dụng var Hay Không

Nếu bạn không chắc chắn, hãy sử dụng các tiêu chí sau:

  • Nếu kiểu rõ ràng từ bên phải → var là OK
  • Nếu kiểu mang ý nghĩa quan trọng → viết kiểu một cách rõ ràng
  • Thân thiện với người mới / mã học tập → viết kiểu càng nhiều càng tốt
  • Phát triển nhóm → tuân theo các tiêu chuẩn mã hóa

Đặc biệt trong quá trình học,
việc viết kiểu giúp làm sâu sắc thêm sự hiểu biết,
vì vậy bạn không cần ép buộc bản thân sử dụng var.

5.6 Tóm Tắt Phần

  • var là cách viết tắt cho suy luận kiểu
  • Nó không phải là kiểu động; kiểu được cố định tại thời điểm biên dịch
  • Nó chỉ dành cho biến cục bộ và yêu cầu khởi tạo
  • Điều quan trọng là phân biệt khi nào nó cải thiện so với giảm khả năng đọc

6. “Tôi Không Muốn Thay Đổi Giá Trị Này”—final và Ý Tưởng Về Hằng Số

Trong Java, ý tưởng thiết kế “một khi giá trị đã được quyết định, đừng để nó bị thay đổi sau đó” rất quan trọng.

Công cụ cho điều đó là final.

Người mới thường nghĩ “final là thứ bạn đặt lên hằng số,” nhưng thực tế, nó có ý nghĩa rộng hơn.

6.1 final Là Gì? (Cấm Gán Lại)

Một biến có final không thể được gán lại sau khi bạn đã gán một lần.

final int maxCount = 10;
maxCount = 20; // compile-time error

Quy tắc rất đơn giản:

  • Bạn có thể khởi tạo nó
  • Bạn không thể gán lại nó

Đó là tất cả những gì bạn cần nhớ.

6.2 Tại sao nên dùng final cho các biến cục bộ?

Thêm final vào một biến cục bộ làm cho nó rõ ràng rằng:

  • Biến này không thay đổi giữa chừng
  • Giá trị được cố định

Nó giúp diễn đạt ý định của bạn một cách rõ ràng trong mã.

final int basePrice = 100;
int total = basePrice + 20;

Viết như vậy:

  • Giúp ngăn ngừa các thay đổi vô tình
  • Khiến người đọc cảm thấy an toàn hơn

Đó là những lợi ích.

6.3 Sử dụng final cho các trường (fields)

Thêm final vào một trường làm cho nó
trở thành một giá trị bất biến cho instance đó.

public class User {
    final String name;

    public User(String name) {
        this.name = name;
    }
}

Trong trường hợp này:

  • name chỉ có thể được đặt một lần trong constructor
  • Nó không thể được thay đổi sau đó

Đây là một cách thiết kế quan trọng để ổn định trạng thái của đối tượng.

6.4 Lưu ý quan trọng cho final với các kiểu tham chiếu (Sự nhầm lẫn thường gặp của người mới bắt đầu)

Đây là một điểm rất quan trọng:

final int[] numbers = {1, 2, 3};
numbers[0] = 100;   // OK
numbers = new int[]{4, 5, 6}; // Error

Tại sao lại xảy ra như vậy?

Lý do là final hạn chế “việc gán lại tham chiếu”.

  • Thay đổi nội dung của mảng → OK
  • Thay đổi mảng mà biến tham chiếu tới → NG

Nói cách khác, final không làm cho nội dung bất biến.

Nếu bạn không hiểu điều này, bạn có thể bị nhầm lẫn và nghĩ:

  • “Tại sao nó vẫn có thể thay đổi dù đã được đánh dấu final?”
  • “Đây có phải là lỗi không?”

Đó là lý do tại sao sự phân biệt này quan trọng.

6.5 Hằng số là gì? (static final)

Trong Java, những gì mọi người thường gọi là “hằng số” thường được định nghĩa bằng static final theo quy ước.

public class Config {
    public static final int MAX_USERS = 100;
}

Loại hằng số này có các đặc điểm sau:

  • Được chia sẻ trong toàn bộ lớp
  • Giá trị không thể thay đổi
  • Bạn có thể đặt cho nó một tên có ý nghĩa

Quy ước đặt tên cho Hằng số

Hằng số thường được viết theo kiểu sau:

UPPER_SNAKE_CASE

Ví dụ:

static final double TAX_RATE = 0.1;

6.6 Cách quyết định khi nào nên dùng final

Là người mới bắt đầu, các tiêu chí sau rất dễ áp dụng:

  • Nếu giá trị không cần thay đổi → thêm final
  • Các giá trị cấu hình / giá trị cơ bản → dùng static final
  • Nếu thay đổi vô tình sẽ gây vấn đề → thêm final

Đặc biệt trong thực tế, một quan điểm phổ biến là:

“Nếu không chắc, hãy thêm final.”

Trong nhiều trường hợp, đó là một cân bằng tốt.

6.7 Tóm tắt phần

  • Với final, bạn không thể gán lại biến
  • Bạn có thể dùng nó cho cả biến cục bộ và trường
  • final trên các kiểu tham chiếu không có nghĩa là “nội dung bất biến”
  • Hằng số được định nghĩa bằng static final
  • final là công cụ để viết mã an toàn, rõ ràng hơn

7. Giá trị khởi tạo, Giá trị mặc định và Các lỗi thường gặp (Phục hồi từ những sai lầm của người mới bắt đầu)

Cho đến nay, bạn đã học về khai báo, kiểu dữ liệu, phạm vi, var, và final. Tuy nhiên, trong thực tế, có rất nhiều tình huống mà “Tôi hiểu về mặt khái niệm, nhưng vẫn gặp lỗi.”

Trong phần này, chúng ta sẽ sắp xếp những điểm khó khăn thường gặp của người mới bắt đầu cùng với các nguyên nhân.

7.1 Biến cục bộ không nhận giá trị mặc định

Đầu tiên, một quy tắc rất quan trọng:

Biến cục bộ không nhận giá trị mặc định tự động

Đoạn mã sau sẽ gây lỗi biên dịch:

public void sample() {
    int x;
    System.out.println(x); // Error
}

Lý do rất đơn giản: Java không cho phép trạng thái “chúng ta không biết nội dung của x”.

Cách tiếp cận đúng

public void sample() {
    int x = 0;
    System.out.println(x);
}

Đối với các biến cục bộ, hãy tuân thủ nghiêm ngặt quy tắc: luôn tự mình khởi tạo chúng.

.### 7.2 Các Trường Tự Động Nhận Giá Trị Mặc Định

Ngược lại, các trường (biến instance và biến static)
được tự động gán các giá trị khởi tạo.

public class Sample {
    int count;
    boolean active;
    String name;
}

Các giá trị mặc định như sau:

  • int → 0
  • double → 0.0
  • boolean → false
  • Kiểu tham chiếu → null

Do đó đoạn mã sau sẽ không gây lỗi:

public class Sample {
    int count;

    public void print() {
        System.out.println(count); // 0
    }
}

Tuy nhiên, các thiết kế dựa vào giá trị mặc định không được khuyến khích.
Chúng làm cho ý định khó nhận biết và dễ trở thành môi trường sinh ra lỗi.

7.3 Lỗi Gây Ra Bởi null (NullPointerException)

Giá trị mặc định cho các kiểu tham chiếu là null.

String text;

Nếu bạn gọi một phương thức trong trạng thái này, sẽ nhận được lỗi thời gian chạy:

System.out.println(text.length()); // NullPointerException

Đây là một trong những ngoại lệ phổ biến nhất mà người mới bắt đầu gặp phải.

Các Biện Pháp Cơ Bản

  • Khởi tạo khi có thể
  • Xem xét khả năng null
  • Thiết kế để tránh sử dụng null một cách không cố ý
    String text = "";
    

Ngay cả khi khởi tạo bằng một chuỗi rỗng cũng có thể ngăn ngừa nhiều tai nạn.

7.4 Sử Dụng Biến Ngoài Phạm Vi Của Nó

Đây là một lỗi phổ biến khác:

if (true) {
    int value = 10;
}

System.out.println(value); // Error

Đây là vấn đề tham chiếu ra ngoài phạm vi.

  • value chỉ hợp lệ bên trong khối if
  • Ngoài khối, nó không tồn tại

Hãy xác nhận lại quy tắc này.

7.5 Nhầm Lẫn Khi Các Biến Có Tên Trùng Nhau (Shadowing Được Xem Lại)

public class Sample {
    int value = 10;

    public void test() {
        int value = 5;
        System.out.println(value);
    }
}

Đoạn mã này in ra 5.

Người mới thường nhầm lẫn rằng:

  • Giá trị của trường sẽ được sử dụng

Nhưng thực tế,
phạm vi bên trong được ưu tiên.

Vì shadowing gây ra nhầm lẫn:

  • Sử dụng tên biến khác nhau dựa trên vai trò

Hãy ghi nhớ điều này.

7.6 Lỗi Biên Dịch Do Không Khớp Kiểu Dữ Liệu

Việc gán sau không được phép:

int number = 3.14; // Error

Nguyên nhân là:

  • doubleint không được tự động chuyển đổi

Đó là quy tắc.

Cách Tiếp Cận Đúng (Khi Bạn Cố Ý Làm Điều Đó)

int number = (int) 3.14; // 3

Tuy nhiên, hãy cẩn thận vì phần thập phân sẽ bị cắt bỏ.

7.7 Tóm Tắt Phần

  • Biến cục bộ phải được khởi tạo
  • Các trường có giá trị mặc định, nhưng không nên phụ thuộc quá mức vào chúng
  • null thường gây ra lỗi thời gian chạy
  • Bạn không thể sử dụng biến ngoài phạm vi của nó
  • Các lỗi không khớp kiểu được ngăn chặn bằng lỗi biên dịch

8. Tóm Tắt: Những Điểm Chính Cần Nắm Vững Đầu Tiên Về Biến Java

Trong bài viết này, chúng tôi đã sắp xếp chủ đề “Biến Java” từng bước,
tập trung vào những điểm mà người mới thường gây nhầm lẫn.

Cuối cùng, hãy tóm tắt các điểm quan trọng
theo dạng hữu ích cho cả việc học và lập trình thực tế.

8.1 Nghĩ Về Biến Java Như Một Bộ “Kiểu, Tên và Giá Trị”

Biến Java luôn đi kèm với ba yếu tố này:

  • Kiểu : loại giá trị mà nó chứa
  • Tên : đại diện cho giá trị đó
  • Giá trị : dữ liệu thực tế được lưu trữ
    int count = 10;
    

Dòng duy nhất này chứa toàn bộ bản chất của biến Java.

8.2 Phân Biệt Rõ Ràng Giữa Khai Báo, Gán Giá Trị và Khởi Tạo

Một bẫy phổ biến của người mới là nhầm lẫn ba khái niệm này:

  • Khai báo: chuẩn bị sử dụng một biến
  • Gán giá trị: đưa một giá trị vào biến
  • Khởi tạo: khai báo và gán giá trị đồng thời

Đặc biệt với các biến cục bộ,
hãy nhớ quy tắc: không thể sử dụng chúng nếu chưa được khởi tạo.

8.3 Nhận Thức Sự Khác Biệt Giữa Kiểu Nguyên Tử và Kiểu Tham Chiếu

Các kiểu Java được chia rộng thành hai loại:

  • Các kiểu nguyên thủy: xử lý giá trị chính nó
  • Các kiểu tham chiếu: xử lý vị trí (tham chiếu) của giá trị

Giữ sự khác biệt này trong đầu giúp bạn hiểu tự nhiên:

  • Sao chép giá trị
  • Cách mảng và đối tượng hoạt động
  • final thực sự có nghĩa là gì

8.4 Hiểu về Phạm vi Giảm Lỗi Một Cách Đáng Kể

Biến chỉ có thể được sử dụng trong nơi chúng được khai báo.

  • Biến cục bộ → chỉ hợp lệ bên trong khối
  • Trường (fields) → có sẵn trong suốt lớp
  • Biến static → được chia sẻ qua lớp

Nếu bạn từng nghĩ “Tôi không biết tại sao không thể sử dụng nó,”
nghi ngờ phạm vi trước tiên.

8.5 Sử dụng varfinal Dựa trên Mục đích

  • var → Chỉ sử dụng khi kiểu rõ ràng và cải thiện khả năng đọc
  • final → Sử dụng để thể hiện rõ ý định “giá trị này không nên thay đổi”

Cả hai đều tiện lợi, nhưng hãy cẩn thận không biến “sử dụng chúng” thành mục tiêu chính.

8.6 Hướng dẫn cho Người mới bắt đầu Viết An toàn

Trong giai đoạn học tập, các chính sách sau giảm lỗi:

  • Khởi tạo biến khi khai báo chúng
  • Viết kiểu rõ ràng nhất có thể
  • Sử dụng tên có ý nghĩa
  • Nếu không chắc chắn, thử thêm final

Chỉ riêng điều này thôi cũng đưa bạn gần hơn với mã Java dễ đọc và an toàn.

8.7 Các Chủ đề Tiếp theo để Học cho Hiểu Biết Sâu hơn

Sau khi hiểu về biến, khuyến nghị chuyển sang:

  • Câu lệnh điều kiện ( if / switch )
  • Vòng lặp ( for / while )
  • Mảng và bộ sưu tập (List / Map)
  • Phương thức và tham số
  • Cơ bản hướng đối tượng

Biến là nền tảng của tất cả mã Java.

Bằng cách hiểu chúng kỹ lưỡng, việc học sau này của bạn trở nên mượt mà hơn đáng kể.