1. Giới thiệu
Trong Java, cũng như trong đời sống, không phải mọi thứ đều nên sẵn có cho mọi người mọi lúc. Hãy hình dung một căn hộ: bạn đâu muốn hàng xóm đi lại trong phòng ngủ của mình, đúng không? Tương tự trong chương trình, đôi khi ta muốn “đóng cửa” đối với một số biến hoặc phương thức để không thể truy cập chúng từ bên ngoài.
Vì vậy trong Java có các bộ sửa đổi truy cập — những từ khóa đặc biệt cho biết ở đâu có thể dùng biến hay phương thức nào đó: ví dụ public hoặc private.
Các bộ sửa đổi truy cập chính
| Bộ sửa đổi | Phạm vi hiển thị |
|---|---|
|
Ở mọi nơi nơi lớp hiển thị (kể cả trong package khác và tệp khác) |
|
Chỉ bên trong cùng lớp đó |
|
Chỉ trong cùng package (tức là bên trong các lớp cùng thư mục) |
Còn có các bộ sửa đổi khác, nhưng ta sẽ nói tới sau khi đến phần kế thừa.
Ví dụ thực tế
public class User
{
public String name; // ai cũng thấy được
private int age; // chỉ thấy bên trong lớp User
public void sayHello()
{
System.out.println("Xin chào, tôi tên là " + name);
}
private void secretMethod()
{
System.out.println("Đây là phương thức bí mật!");
}
}
Giải thích:
- name — khả dụng ở mọi nơi mà lớp User khả dụng.
- age — chỉ thấy bên trong chính lớp User.
- sayHello() — phương thức public, có thể gọi từ bất kỳ lớp nào khác.
- secretMethod() — phương thức private, không thể gọi từ bên ngoài.
Điều này vận hành thế nào trong các bài toán thực tế?
Giả sử ta có một lớp mô tả tài khoản ngân hàng. Rõ ràng ta không muốn ai cũng có thể thay đổi số dư trực tiếp! Vì thế biến số dư sẽ là private, và sẽ có các phương thức chuyên dụng để thao tác với nó.
Vì sao không nên để mọi thứ đều public?
Có thể sẽ nảy sinh cám dỗ: “Hãy làm tất cả public cho đỡ rắc rối nhé!” Nhưng đó là con đường dẫn tới hỗn loạn. Hãy tưởng tượng ai cũng có thể đổi số dư của bạn, tên người dùng của bạn hoặc thậm chí gọi phương thức vốn chỉ để dùng nội bộ. Trong các chương trình lớn, điều này dẫn đến lỗi và những bug “ma quái”.
Quy tắc vàng của lập trình viên Java:
Trước hết hãy đặt mọi thứ là private, rồi chỉ mở ra bên ngoài những gì thật sự cần.
2. Phạm vi của biến
Phạm vi là vùng mã nơi biến “tồn tại” và có thể được sử dụng. Ra khỏi vùng đó, biến sẽ “biến mất” — như thể nó chưa từng tồn tại.
Trong Java có một vài loại biến theo phạm vi:
- Biến cục bộ — được khai báo bên trong phương thức hoặc khối mã { ... }.
- Tham số phương thức — được khai báo trong dấu ngoặc tròn của phương thức.
- Trường (field) của lớp — được khai báo bên trong lớp, nhưng ngoài các phương thức.
Nếu một biến được khai báo bên trong { khối },
nó chỉ nhìn thấy bên trong khối đó
và không thể truy cập từ bên ngoài.
Biến cục bộ
Biến cục bộ chỉ “sống” bên trong phương thức hoặc khối nơi nó được khai báo.
void printSum(int a, int b)
{
int sum = a + b; // biến cục bộ
System.out.println(sum);
}
// sum không còn tồn tại ở đây nữa!
Cố gắng truy cập sum ngoài phương thức sẽ gây lỗi biên dịch: “Không thể tìm thấy biến sum”.
Tham số phương thức
Tham số cũng là biến, nhưng chúng chỉ tồn tại bên trong phương thức.
void greet(String name)
{
System.out.println("Xin chào, " + name);
}
// name không còn tồn tại ở đây nữa!
Trường của lớp (biến thành viên)
Các trường của lớp được khai báo bên trong lớp nhưng ngoài các phương thức. Chúng thấy được trong mọi phương thức của lớp này.
public class Counter
{
private int count = 0; // trường của lớp
public void increment()
{
count++; // có thể dùng trường
}
public int getCount()
{
return count; // cũng có thể dùng trường
}
}
3. Che khuất biến (Shadowing)
Che khuất (shadowing) — là tình huống khi trong một phạm vi, ta khai báo một biến (hoặc tham số) trùng tên với biến ở phạm vi ngoài. Bên trong khối đó, tên “mới” sẽ che khuất tên cũ, và không thể truy cập trực tiếp giá trị ở bên ngoài.
Ví dụ về che khuất:
class ShadowDemo
{
int value = 10; // trường của lớp
void printValue()
{
System.out.println(value); // 10 — in ra trường của lớp
int value = 5; // biến cục bộ che khuất trường của lớp
System.out.println(value); // in ra 5, chứ không phải 10
}
}
Trong ví dụ này, khi viết int value = 5;, ta khai báo một biến cục bộ mới có ưu tiên cao hơn trường của lớp cùng tên. Khi truy cập value bên trong phương thức printValue(), biến cục bộ sẽ được dùng thay vì trường của lớp.
Nếu vẫn cần truy cập trường tĩnh (static) của lớp, hãy dùng tên lớp như tiền tố:
class ShadowDemo
{
static int value = 10; // trường tĩnh của lớp
void printValue()
{
System.out.println(value); // 10 — trường của lớp
int value = 5;
System.out.println(value); // 5 — biến cục bộ
System.out.println(ShadowDemo.value); // 10 — trường tĩnh của lớp, truy cập thông qua 'ShadowDemo'
}
}
Nếu cần truy cập trường không tĩnh của lớp, sử dụng từ khóa this. Nó trỏ tới thực thể (instance) hiện tại của đối tượng.
class ShadowDemo
{
int value = 10;
void printValue()
{
System.out.println(value); // 10 — trường của lớp
int value = 5;
System.out.println(value); // 5 — biến cục bộ
System.out.println(this.value); // 10 — trường của lớp, truy cập thông qua 'this'
}
}
4. Thực hành: bộ sửa đổi truy cập và phạm vi
Hãy tiếp tục phát triển ứng dụng học tập: một hệ thống quản lý sinh viên đơn giản. Ta sẽ đặt các mức truy cập khác nhau cho trường và phương thức.
Ví dụ: lớp Student
public class Student
{
public String name; // tên sinh viên (ai cũng thấy)
private int age; // tuổi (chỉ thấy bên trong lớp)
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
public void sayHello()
{
System.out.println("Xin chào, tôi tên là " + name);
}
private void printSecret()
{
System.out.println("Tuổi của tôi: " + age);
}
public void revealSecret()
{
printSecret(); // có thể gọi phương thức private bên trong lớp
}
}
Sử dụng lớp Student
public class Main
{
public static void main(String[] args)
{
Student s = new Student("Vasya", 20);
s.sayHello(); // OK: phương thức public
s.revealSecret(); // OK: phương thức public, gọi private bên trong
s.age = 30; // Lỗi! Trường age là private
s.printSecret(); // Lỗi! Phương thức printSecret là private
}
}
5. Lỗi thường gặp khi làm việc với bộ sửa đổi truy cập và phạm vi
Lỗi số 1: Để mọi thứ public — hỗn loạn ngay!
Nếu đặt tất cả trường và phương thức là public, bạn có nguy cơ mất kiểm soát về việc ai thay đổi dữ liệu và thay đổi như thế nào. Hãy đặt các trường là private và chỉ mở ra bên ngoài những gì cần thiết.
Lỗi số 2: Cố dùng biến ngoài phạm vi của nó.
Ví dụ khai báo biến bên trong phương thức rồi cố dùng nó bên ngoài — bạn sẽ nhận lỗi biên dịch. Biến cục bộ chỉ tồn tại trong khối { ... } của nó.
Lỗi số 3: Xung đột tên (che khuất biến).
Nếu trong phương thức khai báo biến trùng tên với trường của lớp, rất dễ nhầm đang dùng biến nào. Hãy dùng this để chỉ rõ trường của lớp: this.value.
Lỗi số 4: Cố truy cập phương thức hoặc trường private từ lớp khác.
Nếu phương thức hoặc trường được đánh dấu private, chúng chỉ truy cập được bên trong cùng lớp. Cố truy cập từ bên ngoài sẽ gây lỗi biên dịch.
Lỗi số 5: Quên phạm vi trong vòng lặp hoặc khối.
Biến được khai báo bên trong vòng lặp hoặc bất kỳ khối nào { } chỉ tồn tại bên trong khối đó. Ngoài khối, nó không tồn tại.
GO TO FULL VERSION