在Java中,我们写的代码是由一个个类和类的成员(方法、变量等)组成的。为了让代码更安全、更易于维护,Java提供了访问修饰符来控制这些成员的可见范围——也就是哪些地方可以访问它们,哪些地方不能。这篇文章我们就来聊聊最常用的三个访问修饰符:public、private、protected,看看它们是如何控制可见性的。
为什么需要访问修饰符?¶
想象一下,如果所有类的成员都能被任何地方直接访问,那么代码可能会被随意修改或误操作。比如,一个银行账户的余额如果可以被任何人直接修改,那会有多危险?访问修饰符就像“门禁”,帮我们控制谁能进、谁不能进。
1. public:公开的,所有地方都能访问¶
public 是最“开放”的修饰符,表示成员对所有类都是可见的,无论在同一个包还是不同包中。
例子:
假设我们有一个类 Person,里面有一个 public 方法,其他类无论在哪里都能调用它。
// 类 Person 在包 com.example 下
package com.example;
public class Person {
// public 方法,所有地方都能调用
public void sayHello() {
System.out.println("Hello!");
}
}
// 另一个类 OtherClass 在不同包 com.test 下
package com.test;
import com.example.Person; // 导入 Person 类
public class OtherClass {
public static void main(String[] args) {
Person person = new Person();
person.sayHello(); // 成功调用!public 方法跨包可访问
}
}
结论:public 成员可以被任何类直接访问,包括不同包的类。
2. private:私有的,仅限当前类内部访问¶
private 是最“严格”的修饰符,表示成员只能在定义它的类内部访问,其他类(包括同一包的其他类)都无法直接访问。
例子:
假设 Person 类有一个 private 变量,其他类(即使在同一个包)也无法直接读取或修改它。
// 类 Person 在包 com.example 下
package com.example;
class Person {
private String name; // 私有变量,只能 Person 自己访问
private void printName() { // 私有方法,只能 Person 自己调用
System.out.println("Name: " + name);
}
// 提供 public 方法让外部间接访问私有变量
public void setName(String n) {
name = n;
}
public void getName() {
printName(); // 内部调用私有方法
}
}
// 同一包中的类 OtherClass,尝试访问 Person 的 private 成员
package com.example;
public class OtherClass {
public static void main(String[] args) {
Person person = new Person();
person.setName("Alice"); // 正确:通过 public 方法设置
person.getName(); // 正确:通过 public 方法获取
// 错误!private 成员不能直接访问
// System.out.println(person.name); // 编译报错:无法访问私有成员
// person.printName(); // 编译报错:无法访问私有方法
}
}
结论:private 成员只能在类内部访问,外部类必须通过类提供的 public 方法(如 getter/setter)间接访问。
3. protected:受保护的,同包可访问,子类可访问¶
protected 修饰符的可见性介于 public 和 private 之间,需要区分两种情况:
- 同包内:和 public 类似,同一个包中的类可以直接访问 protected 成员。
- 不同包的子类:如果一个类继承了定义 protected 成员的父类,那么子类可以通过继承的方式访问这些成员(即使子类和父类不在同一个包)。
例子1:同包内访问
// 类 A 和类 B 在同一个包 com.example 下
package com.example;
class A {
protected int age = 20; // 受保护的变量,同包可访问
}
class B {
public static void main(String[] args) {
A a = new A();
System.out.println(a.age); // 正确:同包可直接访问
}
}
例子2:不同包的子类访问
// 父类 Person 在包 com.example 下
package com.example;
public class Person {
protected String name; // 受保护的变量
protected void showName() {
System.out.println("Name: " + name);
}
}
// 子类 Student 在不同包 com.test 下,继承自 Person
package com.test;
import com.example.Person; // 导入父类
public class Student extends Person {
public void displayInfo() {
name = "Bob"; // 子类可直接访问父类的 protected 变量
showName(); // 子类可直接调用父类的 protected 方法
}
}
// 测试类,尝试访问非子类的 protected 成员
package com.test;
import com.example.Person;
public class Test {
public static void main(String[] args) {
Person person = new Person();
// 错误!Test 不是 Person 的子类,不同包无法访问 protected 成员
// System.out.println(person.name); // 编译报错
}
}
结论:protected 成员在同包内和不同包的子类中可以访问,但非子类的不同包类无法访问。
4. 默认修饰符(无修饰符):同包可访问¶
如果一个成员没有任何修饰符(即“默认”修饰符),它的可见范围是仅在同一个包内可访问,不同包的类无法访问。
例子:
// 类 A 和类 B 在同一个包 com.example 下
package com.example;
class A {
int count = 10; // 默认修饰符,同包可访问
}
class B {
public static void main(String[] args) {
A a = new A();
System.out.println(a.count); // 正确:同包可直接访问
}
}
// 不同包的类尝试访问默认成员
package com.test;
import com.example.A;
public class Test {
public static void main(String[] args) {
A a = new A();
// 错误!不同包无法访问默认成员
// System.out.println(a.count); // 编译报错
}
}
结论:默认修饰符成员仅在同包内可见,不同包不可访问。
总结:访问修饰符对比表¶
| 修饰符 | 同包内类 | 不同包的非子类 | 不同包的子类 | 其他包类 |
|---|---|---|---|---|
| public | ✅ 是 | ✅ 是 | ✅ 是 | ✅ 是 |
| private | ❌ 否 | ❌ 否 | ❌ 否 | ❌ 否 |
| protected | ✅ 是 | ❌ 否 | ✅ 是 | ❌ 否 |
| 默认 | ✅ 是 | ❌ 否 | ❌ 否 | ❌ 否 |
实际开发中的建议¶
- 成员变量优先用 private:通过
public方法(如getter/setter)控制访问,既能保证数据安全,又能在方法中添加逻辑(比如验证数据)。 - 控制类的可见性:如果类只在当前包内使用,用默认修饰符;如果需要被其他包的类使用,用
public。 protected用于继承场景:比如父类想让子类访问某些成员,但又不想让其他类访问,就用protected。
掌握访问修饰符,是写出安全、清晰代码的第一步!