public,private与protected
什么时候可以点出来类中的属性呢?
public与private
在 Java 中,public
和 private
是访问修饰符(Access Modifiers),它们用于控制类、方法、变量等成员的可见性和访问权限。它们的主要区别如下:
✅ public
(公有的)
- 访问范围:任何地方都可以访问。
- 适用对象:类、方法、变量、构造方法等。
- 特点:
- 被
public
修饰的成员可以被其他类、包、子类等访问。 - 如果一个类是
public
的,那么类名必须与文件名一致。
- 被
示例:
public class Person {public String name;public void sayHello() {System.out.println("Hello, my name is " + name);}
}
❌ private
(私有的)
- 访问范围:只能在定义它的类内部访问。
- 适用对象:变量、方法、构造方法(不能修饰外部类)。
- 特点:
- 被
private
修饰的成员不能被其他类访问,即使是子类也不行。 - 常用于封装(Encapsulation),通过公共方法(如 getter/setter)间接访问私有变量。
- 被
示例:
public class Person {private int age;public int getAge() {return age;}public void setAge(int age) {if (age > 0) {this.age = age;}}
}
总结对比表:
特性 | public | private |
---|---|---|
访问范围 | 任何地方 | 仅类内部 |
封装性 | 差(暴露太多) | 好(隐藏实现细节) |
安全性 | 低 | 高 |
使用场景 | 对外接口、工具类方法 | 内部实现、敏感数据 |
✅ 实战建议:
- 变量通常设为
private
,通过public
的getter/setter
方法访问。 - 方法如果是工具方法或对外提供功能,可以是
public
;如果是内部逻辑,建议private
。 - 构造方法可以是
private
来实现单例模式等设计模式。
protected
protected
是 Java 的第三大访问修饰符,它位于 public
和 private
之间,专门服务于继承体系和包级访问。很多开发者只把它当成“包内可见 + 子类可见”,其实细节远不止这一句。下面把它的真实规则、使用场景、容易踩坑的点一次性讲透。
1. 访问权限的真面目(JLS 原文直译)
可见位置 | 是否可访问 |
---|---|
同一个类内部 | ✅ |
同一个包中的任何类(无论是否子类) | ✅ |
不同包中的子类 | ✅ 但只能通过“继承关系”访问(见第 3 点) |
不同包中的非子类 | ❌ |
一句话记忆:
“包级友好 + 跨包子类继承链”——缺了其中任何一个条件,跨包就失效。
2. 代码演示:三条规则分别长什么样
目录结构
src
└── p1├── Base.java└── SamePkgKid.java
└── p2├── SubKid.java└── Stranger.java
2.1 规则 A:同包随便用
package p1;
public class Base {protected int val = 10;
}package p1;
public class SamePkgKid {public void foo() {Base b = new Base();System.out.println(b.val); // ✅ 同包可见}
}
2.2 规则 B:跨包必须是子类
package p2;
import p1.Base;
public class SubKid extends Base {public void bar() {System.out.println(val); // ✅ 通过继承拿到 protected 成员}
}
2.3 规则 C:跨包且非子类 → 直接编译失败
package p2;
import p1.Base;
public class Stranger {public void baz() {Base b = new Base();System.out.println(b.val); // ❌ 编译错误:val 不可见}
}
3. 最容易被忽视的“继承链”陷阱
跨包时,即使你是子类,也只能通过“继承视角”使用 protected
成员,而不能像 public
那样“点”出来。
package p2;
import p1.Base;
public class SubKid extends Base {public void test() {System.out.println(val); // ✅ 继承来的,相当于 this.valBase other = new Base();System.out.println(other.val); // ❌ 编译错误:不是继承链}
}
4. 与默认(package-private)的区别
场景 | 默认修饰符 | protected |
---|---|---|
同包 | ✅ | ✅ |
跨包子类 | ❌ | ✅(仅限继承链) |
跨包非子类 | ❌ | ❌ |
所以:
- 只想同包用 → 不写修饰符(默认)即可,语义更清晰。
- 希望被下游框架/库继承 → 用
protected
,明确开放扩展点。
5. 典型使用场景
-
模板方法模式
父类把骨架方法置public
,把可重写的小步骤置protected
,既不允许外部直接调用,又留给子类覆盖权。public abstract class Template {public final void process() { // 骨架,不允许子类改step1();step2();}protected abstract void step1(); // 留给子类protected abstract void step2(); }
-
工具类的辅助方法
只想让同包或子类用到,不让外部随便调。public class JSONWriter {protected void writeRaw(String s) { … } }
-
构造器限制
构造器设为protected
,禁止普通new
,但允许同包或子类实例化(常见在工厂或单例的继承体系)。public class Parent {protected Parent() {} // 同包或子类才能 new }
6. 小结速背卡
- 同包 = 自家客厅,谁都能进。
- 跨包 = 外来访客,必须同时满足:
① 带“子类通行证”
② 只能走“继承通道”,不能走“对象引用通道”。
记住这幅图,再也不怕面试追问“protected
到底能被谁看见”了。