專案結構與需求
- pack1 套件:Father(父類)、Son(子類,非繼承關係)
- pack2 套件:Text_F(測試主類)
- 驗證
private、protected、public、default 四種存取層級在同套件與跨套件的可見性
pack1.Father 類別
package pack1;
public class Father {
private int age; // 僅本類別可見
public String name; // 公開存取
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public void work() {
System.out.println(name + ": I have a job.");
}
public void drive() {
System.out.println(name + ": I have a car, I'm driving.");
}
}
pack1.Son 類別
package pack1;
public class Son {
protected int age; // 同套件 + 子類可見(雖此處非繼承 Father)
private String name; // 僅本類別可見
public void setAge(int age) { this.age = age; }
public int getAge() { return age; }
public void setName(String name) { this.name = name; }
public String getName() { return name; }
private int play() { // 僅本類別可見
System.out.println(name + ": I like to play !");
return age;
}
public int getPlay() { return age; } // 公開存取器
public void study() {
System.out.println(name + ": I like to study !");
}
}
pack2.Text_F 測試類別
package pack2;
import pack1.Father;
import pack1.Son;
public class Text_F {
public static void main(String[] args) {
// 測試 Father
Father f1 = new Father();
f1.setAge(55); // OK: public setter
f1.name = "張三"; // OK: public field
f1.drive(); // OK: public method
f1.work(); // OK: public method
System.out.println(f1.name + "的年齡:" + f1.getAge() + "歲。");
// 測試 Son
Son s1 = new Son();
s1.setAge(17); // OK: public setter
s1.setName("張三三"); // OK: public setter
s1.study(); // OK: public method
System.out.println(s1.getName() + "的年齡:" + s1.getAge() + "歲。");
// 編譯錯誤示範(已註解):
// f1.age = 10; // 錯誤: private
// s1.age = 20; // 錯誤: protected (跨套件非子類不可見)
// s1.name = "test"; // 錯誤: private
// s1.play(); // 錯誤: private
}
}
執行結果

存取權限矩陣總結
| 修飾符 | 同類別 | 同套件 | 不同套件子類 | 不同套件非子類 |
| private | ✓ | ✗ | ✗ | ✗ |
| default (無) | ✓ | ✓ | ✗ | ✗ |
| protected | ✓ | ✓ | ✓ | ✗ |
| public | ✓ | ✓ | ✓ | ✓ |
關鍵觀察點
Father.age 為 private,必須透過 getAge()/setAge() 存取
Son.age 為 protected,在 pack2 中不可直接存取(非子類),需透過公開方法
Son.name 為 private,同樣需透過 getName()/setName()
Son.play() 為 private,外部完全不可見,連 getPlay() 也只是回傳 age 而非呼叫 play()
- 跨套件測試類只能使用
public 成員,完美驗證了封裝邊界
設計建議
- 欄位優先
private,提供 public 存取器(Getter/Setter)
protected 僅用於「預期被子類繼承且需直接存取」的場景
- 避免使用
default(包私有)除非刻意限制在模組內部
- 公開 API 應盡量縮小可見範圍,遵循「最小權限原則」