在Java中,我們寫的代碼是由一個個類和類的成員(方法、變量等)組成的。爲了讓代碼更安全、更易於維護,Java提供了訪問修飾符來控制這些成員的可見範圍——也就是哪些地方可以訪問它們,哪些地方不能。這篇文章我們就來聊聊最常用的三個訪問修飾符:public、private、protected,看看它們是如何控制可見性的。
爲什麼需要訪問修飾符?¶
想象一下,如果所有類的成員都能被任何地方直接訪問,那麼代碼可能會被隨意修改或誤操作。比如,一個銀行賬戶的餘額如果可以被任何人直接修改,那會有多危險?訪問修飾符就像“門禁”,幫我們控制誰能進、誰不能進。
1. public:公開的,所有地方都能訪問¶
public 是最“開放”的修飾符,表示成員對所有類都是可見的,無論在同一個包還是不同包中。
例子:
假設我們有一個類 Person,裏面有一個 public 方法,其他類無論在哪裏都能調用它。
// 類 Person 在包 com.example 下
package com.example;
public class Person {
// public 方法,所有地方都能調用
public void sayHello() {
System.out.println("Hello!");
}
}
// 另一個類 OtherClass 在不同包 com.test 下
package com.test;
import com.example.Person; // 導入 Person 類
public class OtherClass {
public static void main(String[] args) {
Person person = new Person();
person.sayHello(); // 成功調用!public 方法跨包可訪問
}
}
結論:public 成員可以被任何類直接訪問,包括不同包的類。
2. private:私有的,僅限當前類內部訪問¶
private 是最“嚴格”的修飾符,表示成員只能在定義它的類內部訪問,其他類(包括同一包的其他類)都無法直接訪問。
例子:
假設 Person 類有一個 private 變量,其他類(即使在同一個包)也無法直接讀取或修改它。
// 類 Person 在包 com.example 下
package com.example;
class Person {
private String name; // 私有變量,只能 Person 自己訪問
private void printName() { // 私有方法,只能 Person 自己調用
System.out.println("Name: " + name);
}
// 提供 public 方法讓外部間接訪問私有變量
public void setName(String n) {
name = n;
}
public void getName() {
printName(); // 內部調用私有方法
}
}
// 同一包中的類 OtherClass,嘗試訪問 Person 的 private 成員
package com.example;
public class OtherClass {
public static void main(String[] args) {
Person person = new Person();
person.setName("Alice"); // 正確:通過 public 方法設置
person.getName(); // 正確:通過 public 方法獲取
// 錯誤!private 成員不能直接訪問
// System.out.println(person.name); // 編譯報錯:無法訪問私有成員
// person.printName(); // 編譯報錯:無法訪問私有方法
}
}
結論:private 成員只能在類內部訪問,外部類必須通過類提供的 public 方法(如 getter/setter)間接訪問。
3. protected:受保護的,同包可訪問,子類可訪問¶
protected 修飾符的可見性介於 public 和 private 之間,需要區分兩種情況:
- 同包內:和 public 類似,同一個包中的類可以直接訪問 protected 成員。
- 不同包的子類:如果一個類繼承了定義 protected 成員的父類,那麼子類可以通過繼承的方式訪問這些成員(即使子類和父類不在同一個包)。
例子1:同包內訪問
// 類 A 和類 B 在同一個包 com.example 下
package com.example;
class A {
protected int age = 20; // 受保護的變量,同包可訪問
}
class B {
public static void main(String[] args) {
A a = new A();
System.out.println(a.age); // 正確:同包可直接訪問
}
}
例子2:不同包的子類訪問
// 父類 Person 在包 com.example 下
package com.example;
public class Person {
protected String name; // 受保護的變量
protected void showName() {
System.out.println("Name: " + name);
}
}
// 子類 Student 在不同包 com.test 下,繼承自 Person
package com.test;
import com.example.Person; // 導入父類
public class Student extends Person {
public void displayInfo() {
name = "Bob"; // 子類可直接訪問父類的 protected 變量
showName(); // 子類可直接調用父類的 protected 方法
}
}
// 測試類,嘗試訪問非子類的 protected 成員
package com.test;
import com.example.Person;
public class Test {
public static void main(String[] args) {
Person person = new Person();
// 錯誤!Test 不是 Person 的子類,不同包無法訪問 protected 成員
// System.out.println(person.name); // 編譯報錯
}
}
結論:protected 成員在同包內和不同包的子類中可以訪問,但非子類的不同包類無法訪問。
4. 默認修飾符(無修飾符):同包可訪問¶
如果一個成員沒有任何修飾符(即“默認”修飾符),它的可見範圍是僅在同一個包內可訪問,不同包的類無法訪問。
例子:
// 類 A 和類 B 在同一個包 com.example 下
package com.example;
class A {
int count = 10; // 默認修飾符,同包可訪問
}
class B {
public static void main(String[] args) {
A a = new A();
System.out.println(a.count); // 正確:同包可直接訪問
}
}
// 不同包的類嘗試訪問默認成員
package com.test;
import com.example.A;
public class Test {
public static void main(String[] args) {
A a = new A();
// 錯誤!不同包無法訪問默認成員
// System.out.println(a.count); // 編譯報錯
}
}
結論:默認修飾符成員僅在同包內可見,不同包不可訪問。
總結:訪問修飾符對比表¶
| 修飾符 | 同包內類 | 不同包的非子類 | 不同包的子類 | 其他包類 |
|---|---|---|---|---|
| public | ✅ 是 | ✅ 是 | ✅ 是 | ✅ 是 |
| private | ❌ 否 | ❌ 否 | ❌ 否 | ❌ 否 |
| protected | ✅ 是 | ❌ 否 | ✅ 是 | ❌ 否 |
| 默認 | ✅ 是 | ❌ 否 | ❌ 否 | ❌ 否 |
實際開發中的建議¶
- 成員變量優先用 private:通過
public方法(如getter/setter)控制訪問,既能保證數據安全,又能在方法中添加邏輯(比如驗證數據)。 - 控制類的可見性:如果類只在當前包內使用,用默認修飾符;如果需要被其他包的類使用,用
public。 protected用於繼承場景:比如父類想讓子類訪問某些成員,但又不想讓其他類訪問,就用protected。
掌握訪問修飾符,是寫出安全、清晰代碼的第一步!