四大訪問級別
| 修飾符 | 同類 | 同包 | 子類(異包) | 非子類(異包) | 典型用途 |
public | ✓ | ✓ | ✓ | ✓ | API 公開契約、介面方法 |
protected | ✓ | ✓ | ✓ | ✗ | 子類覆寫/模板鉤子、包內+繼承體系共享 |
default(包私有) | ✓ | ✓ | ✗ | ✗ | 模塊內部實現類、包級工具、SPI 實現類 |
private | ✓ | ✗ | ✗ | ✗ | 內部實現細節、字段封裝、輔助方法 |
適用目標範圍
| 目標 | 可用修飾符 | 說明 |
| 頂層類/介面 | public / default | 同一源文件僅一 public 類,文件名匹配 |
| 成員字段 | 四種皆可 | 規範:private + getter/setter |
| 構造器 | 四種皆可 | private 防實例化/單例;protected 限制繼承 |
| 成員方法 | 四種皆可 | 按契約暴露 |
| 局部變量 | 不可用 | 作用域由代碼塊決定 |
封裝最佳實踐:字段私有 + 存取器
public class Student {
private int age; // 字段私有
private String id;
public int getAge() { return age; }
public void setAge(int age) {
if (age < 0 || age > 150)
throw new IllegalArgumentException("Invalid age");
this.age = age;
}
public String getId() { return id; }
public void setId(String id) {
if (id == null || !id.matches("\\d{10}"))
throw new IllegalArgumentException("Invalid ID");
this.id = id;
}
}
- 控制權:校驗邏輯集中在 setter,防止非法狀態。
- 可維護性:內部存儲變更(如
int→Long、拆分字段)不影響調用者。
- 只讀/寫控制:僅提供 getter → 不可變;僅 setter → 寫入-only(罕見)。
- IDE 生成:Eclipse Alt+Shift+S→R, IntelliJ Alt+Insert→Getter/Setter。
進階封裝模式
1. 防實例化 + 防繼承(工具類標準)
public final class StringUtils {
private StringUtils() {} // private 構造器:不可 new、不可 extends
public static boolean isBlank(String s) { ... }
}
2. 包私有實現類(SPI/模塊邊界)
// com.myapp.dao.UserDao.java (public 介面)
public interface UserDao { User findById(Long id); }
// com.myapp.dao.impl.UserDaoImpl.java (default 類)
class UserDaoImpl implements UserDao { // 無 public,包外不可見
public User findById(Long id) { ... }
}
// com.myapp.dao.DaoFactory.java (public 工廠)
public class DaoFactory {
public static UserDao createUserDao() { return new UserDaoImpl(); }
}
調用方僅依賴介面+工廠,實現類可隨時替換,編譯期強制隔離。
3. protected 模板方法鉤子
public abstract class AbstractParser {
// 子類可覆寫,包外不可直接調用
protected void preProcess() { }
protected void postProcess() { }
public final String parse(String input) {
preProcess();
String result = doParse(input);
postProcess();
return result;
}
protected abstract String doParse(String input);
}
模塊化系統中的訪問控制(JDK 9+ JPMS)
module-info.java 導出包:
module com.myapp.core {
exports com.myapp.api; // 公開 API 包
exports com.myapp.spi; // 公開 SPI 介面
// com.myapp.internal 不導出 → 內部實現強封裝
// 即使類是 public,模塊外也不可訪問(反射需 opens)
}
- JPMS 在類訪問控制之上增加模塊級邊界。
public 類若所在包未 exports,模塊外不可用。
opens 允許反射深度訪問(框架如 Spring/Hibernate 需要)。
常見錯誤與重構建議
| 錯誤模式 | 問題 | 重構 |
字段 public | 破壞封裝,無法校驗、追蹤、遷移 | private + getter/setter |
方法過度 public | 洩露內部邏輯,擴大耦合面 | 縮小至 protected/default/private |
構造器 public 工具類 | 允許無意義實例化 | private 構造器 + final 類 |
測試代碼訪問 private | 破壞封裝邊界 | 同包測試類用 default;或反射(setAccessible(true)) |
訪問控制設計原則
- 最小權限原則:默認
private,按需放寬。
- 面向介面編程:對外暴露
public interface,實現類 default 或 package-private。
- 繼承設計:預留給子類的用
protected,內部實現用 private。
- 模塊邊界:JPMS
exports 精確控制包級可見性。