什么是访问修饰符
简单说就是给你的代码加门禁系统——谁能进来看、谁能用,全靠这几个关键字控制
Java有四种访问级别,从最开放到最封闭:public > protected > 默认(不写) > private
之前在 [hello world](/2026/04/04/hello world/) 里我们写 public class 和 public static void main,现在来搞清楚这个 public 到底是啥意思
public —— 完全公开
就像公园,谁都能进,不需要任何门票
用 public 修饰的类或成员,任何地方都能访问
1 2 3 4 5 6
| @Test public void testPublicAccess() {
String s = "hello"; System.out.println(s.length()); }
|
protected —— 家族可见
就像家族群,本包的人和子类(继承关系的”后代”)能看到,外人看不到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Animal { protected String name = "动物";
protected void eat() { System.out.println(name + "在吃东西"); } }
public class Dog extends Animal { @Test public void testProtected() { System.out.println(this.name); this.eat();
} }
|
默认(包级私有)—— 邻居可见
什么都不写就是默认级别,就像小区大门,同一个包(小区)里的人能互相串门,外面的人进不来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Helper { int count = 0;
void doSomething() { System.out.println("只有同包的类能调我"); } }
public class Service { @Test public void testDefault() { Helper h = new Helper(); h.doSomething(); } }
public class Other { @Test public void testCrossPackage() { } }
|
private —— 自己可见
就像你房间的日记本,只有你自己能看,别人都不行(包括子类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class BankAccount { private double balance = 0;
public void deposit(double amount) { if (amount > 0) { this.balance += amount; } }
public double getBalance() { return this.balance; } }
public class Test { @Test public void testPrivate() { BankAccount account = new BankAccount(); account.deposit(1000); System.out.println(account.getBalance());
} }
|
四种修饰符对比表(必背)
| 修饰符 |
同一个类 |
同一个包 |
子类(不同包) |
任意位置 |
public |
✅ |
✅ |
✅ |
✅ |
protected |
✅ |
✅ |
✅ |
❌ |
| 默认(不写) |
✅ |
✅ |
❌ |
❌ |
private |
✅ |
❌ |
❌ |
❌ |
记忆技巧:从上到下,权限一个一个关门。public全开,private只留自己
类级别 vs 成员级别的修饰符限制
顶层类(直接写在 .java 文件里的类)只能用两种修饰符:
public —— 对外公开
默认(不写)—— 包级私有
❌ 不能用 private 或 protected 修饰顶层类!想想也对,一个private的类谁都用不了,写它干嘛
成员(字段、方法、内部类)四种都能用
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public class MyClass { private int secret; protected int familyData; int packageData; public int openData; }
|
⭐ 最小权限原则 —— 安全第一
核心思想:能用 private 就别用 public
这是写好代码最重要的原则之一,理由很简单:
暴露得越少,别人能搞破坏的地方就越少
以后改代码时,private的东西随便改,不影响其他人;public的东西一改,所有调用者都可能出问题
实际操作
字段一律 private,通过 getter/setter 方法访问(参见 JavaBean规范)
方法:只有外部需要调用的才 public,内部工具方法用 private
类:只有需要跨包使用的才 public
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class BadUser { public String name; public int age; public String password; }
public class GoodUser { private String name; private int age; private String password;
public String getName() { return name; } public void setName(String name) { this.name = name; }
public int getAge() { return age; } public void setAge(int age) { if (age < 0 || age > 150) { throw new IllegalArgumentException("年龄不合法"); } this.age = age; }
public void setPassword(String password) { this.password = encryptPassword(password); }
private String encryptPassword(String raw) { return "encrypted_" + raw; } }
|
不同包访问修饰符的完整示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.example.base;
public class Person { public String name = "张三"; protected int age = 25; String city = "北京"; private String idCard = "110xxx";
public void sayHi() { System.out.println(name + ", " + age + ", " + city + ", " + idCard); } }
package com.example.base;
public class Colleague { @Test public void testSamePackage() { Person p = new Person(); System.out.println(p.name); System.out.println(p.age); System.out.println(p.city); } }
package com.example.other; import com.example.base.Person;
public class Student extends Person { @Test public void testSubclass() { System.out.println(this.name); System.out.println(this.age); } }
package com.example.other; import com.example.base.Person;
public class Stranger { @Test public void testStranger() { Person p = new Person(); System.out.println(p.name); } }
|
常见坑
坑1:protected 不是”随便子类都行”
不同包的子类只能通过 this 访问继承来的 protected 成员,不能通过父类对象访问
1 2 3 4 5 6 7 8 9
| public class Dog extends Animal { void test() { this.name = "旺财";
Animal a = new Animal(); } }
|
坑2:一个 .java 文件只能有一个 public 类
而且 public 类的类名必须和文件名一致
坑3:接口中的成员默认修饰符
接口中的方法默认是 public abstract(详见 接口与抽象类)
接口中的字段默认是 public static final
不用手动写,但要知道本质是这样
练习题
题1:下面哪行代码会编译报错?
1 2 3 4 5 6 7 8 9 10 11
| package com.test; import com.example.base.Person;
public class Quiz { public void test() { Person p = new Person(); System.out.println(p.name); System.out.println(p.age); System.out.println(p.city); } }
|
答案:B和C。age 是 protected 只有子类能跨包访问,city 是默认级别不能跨包
题2:为什么字段推荐用 private 而不是 public?
答案:封装——通过 getter/setter 可以加校验逻辑(比如年龄不能是负数),直接 public 别人随便改没法控制
题3:private class MyClass {} 为什么编译报错?
答案:顶层类只能用 public 或默认修饰符,private 的顶层类没人能用到,没有意义。但内部类可以是 private(参见 内部类与匿名类)