Java四大访问修饰符作用域不同:private限本类,default限同包,protected限同包+跨包子类,public全局可见;封装核心在于合理暴露接口并校验行为。

Java中四个访问修饰符的实际作用范围
Java的private、default(包级)、protected、public不是按“严格程度”线性递增,而是各自解决不同场景的可见性问题。比如protected既允许子类访问,又允许同包访问——这和public有本质区别,不能简单理解为“比default多一点”。
-
private:仅限本类内部;连子类、同包类都不可见 -
default(不写任何修饰符):仅限同包内;跨包子类也访问不到 -
protected:同包 + 不同包的直接子类;但不同包子类的子类(孙类)仍不可见 -
public:任何地方都可访问;唯一完全开放的修饰符
封装不只是加private,关键在暴露什么接口
把字段设为private只是第一步。真正体现封装意图的是你提供的getter/setter是否合理。比如一个age字段,如果只提供getAge()而不提供setAge(int),或者在setAge()里校验范围(如if (age 150) throw new IllegalArgumentException();),这才是封装的价值所在。
- 不要为每个
private字段机械生成所有getter/setter - 对只读字段,只保留
getter;对只写字段(极少见),只保留setter - 在
setter中做参数校验,比在业务层重复判断更可靠 - 返回集合时,避免直接返回内部
ArrayList引用;应返回Collections.unmodifiableList(list)或新副本
protected常被误用:继承 vs. 包内协作
很多人把protected当成“给子类用的public”,结果导致非继承关系的同包类也能随意修改关键状态。例如工具类JsonUtils里一个protected static final ObjectMapper mapper,本意是供子类复用,却被同包下任意类调用并mapper.configure(...)污染全局配置。
- 若仅希望子类扩展行为,优先考虑
protected方法而非字段 - 若字段需被子类访问,且该字段本身不应被外部修改,声明为
protected final - 若实际需求是“包内共享”,明确使用
default更清晰;protected暗示了继承契约 - 跨包继承时,
protected成员在子类中可访问,但在子类对象上调用(如childObj.protectedMethod())依然编译失败——必须通过this或super调用
final + private组合是强封装的常见模式
当一个字段既不允许外部访问,也不允许子类修改,private final是最小安全面。它配合构造器注入,能构建出真正不可变的对象。注意:仅final不加private毫无意义——public final字段虽不可改,但可被任意代码读取甚至反射绕过。
立即学习“Java免费学习笔记(深入)”;
public class Config {
private final String apiKey;
private final int timeoutMs;
public Config(String apiKey, int timeoutMs) {
this.apiKey = Objects.requireNonNull(apiKey);
this.timeoutMs = Math.max(100, timeoutMs); // 封装校验逻辑
}
public String getApiKey() {
return this.apiKey; // 只读暴露,不泄露引用
}
}
这里apiKey是private final,构造器完成初始化,getApiKey()只返回值——没有意外的引用泄漏,也没有运行时修改可能。这种写法在Spring配置类、领域模型中非常关键。
最容易被忽略的一点:封装不是靠修饰符堆砌,而是靠设计时明确“谁需要访问”“访问后能做什么”。哪怕全用public,只要接口语义清晰、行为受控,仍是良好封装;反之,满屏private却到处暴露可变集合引用,封装就形同虚设。










