《Thinking in Java》读书笔记---访问权限控制

访问控制(或隐藏具体实现)与"最初的实现并不恰当"有关。


一、访问控制基础理论

1.1 访问控制的核心目的

  • 封装性:隐藏对象内部实现细节

  • 安全性:防止外部代码意外破坏对象状态

  • 模块化:明确组件边界和交互接口

  • 可维护性:减少耦合,便于修改内部实现

1.2 Java访问权限层级体系

访问级别 关键字 可见范围
公开访问 public 所有类
包级私有 (默认) 同一包内
受保护 protected 同一包内+子类
私有访问 private 仅当前类

二、类成员访问权限详解

2.1 public访问权限

2.1.1 特性分析
  • 最大可见性:可被任何其他类访问

  • API设计:构成类的外部契约

  • 使用建议

    复制代码
    public class BankAccount {
        public BigDecimal getBalance() { ... }  // 公共API方法
        public void deposit(BigDecimal amount) { ... }
    }
2.1.2 典型应用场景
  • 对外暴露的服务接口

  • 常量定义(通常配合static final)

  • 工具类方法

2.2 protected访问权限

2.2.1 跨包继承可见性
复制代码
// Package A
public class Parent {
    protected int protectedField;
}

// Package B
public class Child extends Parent {
    void method() {
        protectedField = 10;  // 可访问父类protected成员
    }
}

// Package B
public class Unrelated {
    void method() {
        Parent p = new Parent();
        // p.protectedField = 10;  // 编译错误!非子类不能访问
    }
}
2.2.2 设计考量
  • 继承框架:允许子类访问但限制外部使用

  • 封装平衡:比包访问更开放,比public更受限

  • 最佳实践

    复制代码
    public abstract class Shape {
        protected abstract void drawInternal();  // 子类实现细节
        
        public final void draw() {
            preDraw();
            drawInternal();
            postDraw();
        }
    }

2.3 默认(package-private)访问权限

2.3.1 包内协作机制
复制代码
// Package com.example.util
class PackagePrivateHelper {  // 默认访问权限
    void doWork() { ... }
}

public class PublicTool {
    PackagePrivateHelper helper = new PackagePrivateHelper();
    
    public void service() {
        helper.doWork();  // 同一包内可访问
    }
}
2.3.2 设计模式应用
  • 工厂模式:隐藏实现类

  • 包私有实现:仅暴露必要接口

  • 测试友好:同一包内的测试类可访问

2.4 private访问权限

2.4.1 严格封装实现
复制代码
public class StrictEncapsulation {
    private int secretValue;
    private void internalProcess() { ... }
    
    public void publicMethod() {
        internalProcess();  // 仅内部可调用
    }
}
2.4.2 现代Java增强
  • Record类:自动生成private final字段

  • 密封类:配合private实现更严格继承控制


三、类本身访问控制

3.1 public类规范

3.1.1 文件命名要求
  • 每个.java文件只能有一个public类

  • 文件名必须与public类名完全一致

  • 可以有多个非public类(但非推荐做法)

3.1.2 跨项目可见性
复制代码
projectA/src/com/example/PublicClass.java
projectB/src/.../Client.java

// Client.java可以:
import com.example.PublicClass;

3.2 默认访问权限类

3.2.1 包内专用类
复制代码
// Package com.example.internal
class InternalUtils {  // 默认访问权限
    static void helperMethod() { ... }
}

// 同一包内的其他类可访问
public class Service {
    void service() {
        InternalUtils.helperMethod();
    }
}
3.2.2 设计优势
  • 模块化封装:隐藏非必要公开的实现类

  • 减少API污染:避免外部代码依赖内部实现


四、接口与枚举的访问控制

4.1 接口成员访问规则

4.1.1 默认public特性
复制代码
public interface MyInterface {
    int CONSTANT = 10;  // 默认public static final
    void method();      // 默认public abstract
}
4.1.2 Java 9+私有方法
复制代码
public interface ModernInterface {
    private void internalHelper() { ... }  // Java 9+
    
    default void service() {
        internalHelper();
    }
}

4.2 枚举类型访问控制

4.2.1 枚举常量特性
复制代码
public enum Color {
    RED, GREEN, BLUE;  // 默认public static final
    
    private int code;  // 枚举私有字段
    
    public int getCode() { return code; }
}
4.2.2 包私有枚举应用
复制代码
enum InternalState {  // 包内可见
    INIT, WORKING, DONE
}

五、模块系统下的访问控制(Java 9+)

5.1 module-info.java规范

复制代码
module com.example.myapp {
    requires java.base;
    requires transitive com.example.common;
    exports com.example.api;
    opens com.example.internal to com.example.test;
}

5.2 模块访问权限类型

指令 作用 示例
exports 导出包 exports com.example.api
opens 反射开放 opens com.example.internal
requires 模块依赖 requires java.sql
provides...with 服务提供 provides Service with MyServiceImpl

六、设计模式中的访问控制应用

6.1 工厂方法模式

复制代码
public interface Product {
    void use();
}

class ConcreteProduct implements Product {  // 包私有实现
    @Override 
    public void use() { ... }
}

public class ProductFactory {
    public static Product getProduct() {
        return new ConcreteProduct();  // 包内可访问
    }
}

6.2 单例模式实现

复制代码
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {}  // 私有构造器
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

七、访问控制最佳实践

7.1 成员访问设计原则

  1. 最小权限原则:从private开始,按需放宽

  2. 稳定接口:public方法应保持长期兼容

  3. 防御性编程:protected成员也应考虑子类误用

  4. 文档配套:明确说明protected/包私有成员的预期用途

7.2 典型设计模板

复制代码
public class WellDesignedClass {
    // 1. 私有字段
    private int internalState;
    
    // 2. 包私有构造器(工厂方法模式)
    WellDesignedClass(int initialState) {
        this.internalState = initialState;
    }
    
    // 3. 公共API方法
    public void publicMethod() {
        internalHelper();
    }
    
    // 4. 受保护的可扩展点
    protected void overrideMe() { ... }
    
    // 5. 包私有工具方法
    void packagePrivateUtility() { ... }
    
    // 6. 私有实现细节
    private void internalHelper() { ... }
}

八、常见问题与解决方案

8.1 反射绕过访问限制

问题表现
复制代码
Field f = obj.getClass().getDeclaredField("privateField");
f.setAccessible(true);  // 绕过private限制
f.set(obj, value);
防御方案
  • Java 9+模块系统限制非法访问

  • 安全管理器检查

  • 关键字段使用SecurityManager验证

8.2 跨包继承问题

典型场景
复制代码
// Package A
public class Base {
    protected int value;
}

// Package B
public class Derived extends Base {
    void method() {
        Base b = new Base();
        // b.value = 10;  // 编译错误!必须通过Derived实例访问
        this.value = 10;  // 正确
    }
}
解决方案
  • 通过protected方法而非直接字段访问

  • 使用包结构合理规划继承体系


**结语:**随着Java语言的演进,访问控制在模块化、安全性等方面持续增强,成为Java编程中不可或缺的核心概念。