访问修饰符

什么是访问修饰符

简单说就是给你的代码加门禁系统——谁能进来看、谁能用,全靠这几个关键字控制

Java有四种访问级别,从最开放到最封闭:public > protected > 默认(不写) > private

之前在 [hello world](/2026/04/04/hello world/) 里我们写 public classpublic static void main,现在来搞清楚这个 public 到底是啥意思

public —— 完全公开

就像公园,谁都能进,不需要任何门票

public 修饰的类或成员,任何地方都能访问

1
2
3
4
5
6
@Test
public void testPublicAccess() {
// public 成员:任何包、任何类都能直接访问
String s = "hello";
System.out.println(s.length()); // length() 是 public 方法,随便调
}

protected —— 家族可见

就像家族群,本包的人和子类(继承关系的”后代”)能看到,外人看不到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 包 com.example.animal
public class Animal {
protected String name = "动物"; // 受保护的字段

protected void eat() {
System.out.println(name + "在吃东西");
}
}

// 包 com.example.dog(不同包!)
public class Dog extends Animal {
@Test
public void testProtected() {
// ✅ 子类可以访问父类的 protected 成员
System.out.println(this.name); // "动物"
this.eat(); // 正常调用

// ❌ 但不能通过父类对象访问
// Animal a = new Animal();
// a.name; // 编译报错!不同包中只能通过继承访问
}
}

默认(包级私有)—— 邻居可见

什么都不写就是默认级别,就像小区大门,同一个包(小区)里的人能互相串门,外面的人进不来

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
// 包 com.example.util
class Helper { // 没写 public,就是默认访问级别
int count = 0; // 字段也没写修饰符,默认级别

void doSomething() { // 方法也是默认级别
System.out.println("只有同包的类能调我");
}
}

// 同一个包 com.example.util
public class Service {
@Test
public void testDefault() {
Helper h = new Helper(); // ✅ 同包,没问题
h.doSomething(); // ✅ 同包,能调用
}
}

// 不同包 com.example.other
public class Other {
@Test
public void testCrossPackage() {
// Helper h = new Helper(); // ❌ 编译报错!不同包访问不了默认级别的类
}
}

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; // 余额是隐私,必须 private

// 只能通过 public 方法来操作 private 字段
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 方法间接访问

// account.balance = 999999; // ❌ 编译报错!private 字段外部不能直接访问
// 这就是封装的意义:防止别人乱改你的数据
}
}

四种修饰符对比表(必背)

修饰符 同一个类 同一个包 子类(不同包) 任意位置
public
protected
默认(不写)
private

记忆技巧:从上到下,权限一个一个关门。public全开,private只留自己

类级别 vs 成员级别的修饰符限制

顶层类(直接写在 .java 文件里的类)只能用两种修饰符:

public —— 对外公开

默认(不写)—— 包级私有

❌ 不能用 privateprotected 修饰顶层类!想想也对,一个private的类谁都用不了,写它干嘛

成员(字段、方法、内部类)四种都能用

1
2
3
4
5
6
7
8
9
10
11
12
13
// ❌ 编译报错:顶层类不能用 private
// private class Secret { }

// ❌ 编译报错:顶层类不能用 protected
// protected class FamilyOnly { }

// ✅ 正确
public class MyClass {
private int secret; // ✅ 成员可以 private
protected int familyData; // ✅ 成员可以 protected
int packageData; // ✅ 成员可以默认
public int openData; // ✅ 成员可以 public
}

⭐ 最小权限原则 —— 安全第一

核心思想:能用 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,毫无防守
public class BadUser {
public String name;
public int age;
public String password; // 密码都 public?疯了吧!
}

// ✅ 正面教材:最小权限
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;
}

// 注意:密码只有 setter 没有 getter!不让外部读取
public void setPassword(String password) {
this.password = encryptPassword(password);
}

// 加密方法是内部实现细节,private
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
// ===== 文件:com/example/base/Person.java =====
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);
}
}

// ===== 文件:com/example/base/Colleague.java =====
package com.example.base; // 同一个包

public class Colleague {
@Test
public void testSamePackage() {
Person p = new Person();
System.out.println(p.name); // ✅ public
System.out.println(p.age); // ✅ protected(同包)
System.out.println(p.city); // ✅ 默认(同包)
// System.out.println(p.idCard); // ❌ private,不行!
}
}

// ===== 文件:com/example/other/Student.java =====
package com.example.other; // 不同包,但是子类
import com.example.base.Person;

public class Student extends Person {
@Test
public void testSubclass() {
System.out.println(this.name); // ✅ public
System.out.println(this.age); // ✅ protected(子类)
// System.out.println(this.city); // ❌ 默认,不同包看不到
// System.out.println(this.idCard); // ❌ private,不行
}
}

// ===== 文件:com/example/other/Stranger.java =====
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); // ✅ public,唯一能访问的
// System.out.println(p.age); // ❌ protected,不是子类
// System.out.println(p.city); // ❌ 默认,不同包
// System.out.println(p.idCard);// ❌ private
}
}

常见坑

坑1:protected 不是”随便子类都行”

不同包的子类只能通过 this 访问继承来的 protected 成员,不能通过父类对象访问

1
2
3
4
5
6
7
8
9
// 不同包的子类
public class Dog extends Animal {
void test() {
this.name = "旺财"; // ✅ 通过 this 访问

Animal a = new Animal();
// a.name = "小猫"; // ❌ 通过父类对象访问,编译报错!
}
}

坑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; // Person 在另一个包

public class Quiz {
public void test() {
Person p = new Person();
System.out.println(p.name); // A
System.out.println(p.age); // B
System.out.println(p.city); // C
}
}

答案:B和C。age 是 protected 只有子类能跨包访问,city 是默认级别不能跨包

题2:为什么字段推荐用 private 而不是 public?

答案:封装——通过 getter/setter 可以加校验逻辑(比如年龄不能是负数),直接 public 别人随便改没法控制

题3private class MyClass {} 为什么编译报错?

答案:顶层类只能用 public 或默认修饰符,private 的顶层类没人能用到,没有意义。但内部类可以是 private(参见 内部类与匿名类


上一章 目录 下一章
封装继承多态 java基础 接口与抽象类