Java访问修饰符详解:全面掌握public、private、protected和默认修饰符
在Java中,访问修饰符(Access Modifiers)是控制类、方法、变量和构造函数的可见性和访问权限的关键工具。合理使用访问修饰符是实现封装、提高代码安全性和可维护性的基础。本文将全面解析四种访问修饰符的使用规则和最佳实践。
访问修饰符概述
Java提供了四种访问级别,从严格到开放依次为:
| 修饰符 | 本类内部 | 同一包内 | 不同包子类 | 不同包非子类 |
|---|---|---|---|---|
| private | ✓ | ✗ | ✗ | ✗ |
| 默认(无修饰符) | ✓ | ✓ | ✗ | ✗ |
| protected | ✓ | ✓ | ✓ | ✗ |
| public | ✓ | ✓ | ✓ | ✓ |
修饰类的规则
顶级类(非内部类)
顶级类只能使用 public 或 默认 修饰符:
java
// 正确示例
public class PublicClass { } // 任何地方都可以访问
class DefaultClass { } // 同一包内可以访问
// 错误示例
// private class PrivateClass { } // 编译错误
// protected class ProtectedClass { } // 编译错误
内部类(嵌套类)
内部类可以使用所有四种访问修饰符:
java
public class OuterClass {
public class PublicInner { }
protected class ProtectedInner { }
private class PrivateInner { } // 只能在OuterClass内部访问
class DefaultInner { }
}
修饰成员变量⭐
同一包中访问级别对比
java
public class VariableExample {
public int publicVar; // 任何地方可访问
protected int protectedVar; // 同一包内和子类可访问
int defaultVar; // 同一包内可访问
private int privateVar; // 仅本类可访问
public void testAccess() {
this.publicVar = 1; // ✓ 可访问
this.protectedVar = 2; // ✓ 可访问
this.defaultVar = 3; // ✓ 可访问
this.privateVar = 4; // ✓ 可访问
}
}
不同包中的访问
java
// 在不同包的子类中
public class SubClass extends VariableExample {
public void accessInSubclass() {
publicVar = 1; // ✓ 可访问
protectedVar = 2; // ✓ 可访问(通过继承)
// defaultVar = 3; // ✗ 编译错误(不同包)
// privateVar = 4; // ✗ 编译错误
}
}
// 在不同包的非子类中
public class OtherClass {
public void accessInOther() {
VariableExample example = new VariableExample();
example.publicVar = 1; // ✓ 可访问
// example.protectedVar = 2; // ✗ 编译错误
// example.defaultVar = 3; // ✗ 编译错误
// example.privateVar = 4; // ✗ 编译错误
}
}
修饰方法
普通方法
java
public class MethodExample {
public void publicMethod() { }
protected void protectedMethod() { }
void defaultMethod() { }
private void privateMethod() { }
// 调用本类方法
public void callAllMethods() {
publicMethod(); // ✓
protectedMethod(); // ✓
defaultMethod(); // ✓
privateMethod(); // ✓
}
}
方法重写的访问规则
重写方法的访问级别不能比原方法更严格:
java
public class Parent {
public void publicMethod() { }
protected void protectedMethod() { }
void defaultMethod() { }
}
class Child extends Parent {
@Override
public void publicMethod() { } // ✓ 可以保持public
@Override
protected void protectedMethod() { } // ✓ 可以保持protected
@Override
public void protectedMethod() { } // ✓ 可以提升为public
// @Override
// private void protectedMethod() { } // ✗ 编译错误(不能降级为private)
@Override
void defaultMethod() { } // ✓ 在同一包中可以
}
修饰构造函数
java
package university;
// 学生类 - 展示各种访问级别的构造函数
public class Student {
private String id;
private String name;
private String major;
// 1. public构造函数 - 任何人都能创建普通学生
public Student(String name) {
this.id = generateId();
this.name = name;
this.major = "未定";
System.out.println("新生入学:" + name);
}
// 2. protected构造函数 - 专门给子类(研究生)使用
protected Student(String name, String major) {
this.id = generateId();
this.name = name;
this.major = major;
System.out.println("研究生入学:" + name + ",专业:" + major);
}
// 3. 默认构造函数 - 只能在本包内使用(用于快速创建测试对象)
Student(String name, String id, String major) {
this.id = id;
this.name = name;
this.major = major;
System.out.println("(内部使用)学生:" + name);
}
// 4. private构造函数 - 只在类内部使用(用于特殊初始化)
private Student() {
this.id = "000000";
this.name = "系统保留";
this.major = "管理员";
System.out.println("系统内部创建管理员学生");
}
// 静态工厂方法 - 外部通过这个获取特殊实例
public static Student createAdminStudent() {
return new Student(); // 调用private构造函数
}
private String generateId() {
return "S" + System.currentTimeMillis();
}
public void showInfo() {
System.out.println("学号:" + id + ",姓名:" + name + ",专业:" + major);
}
}
java
package university;
// 研究生类 - 继承Student
public class GraduateStudent extends Student {
public GraduateStudent(String name, String major) {
super(name, major); // 调用父类的protected构造函数 ✅
System.out.println("研究生详细信息已创建");
}
}
java
package university;
// 测试类 - 同一个包内
public class TestSamePackage {
public static void main(String[] args) {
// 测试public构造函数
Student s1 = new Student("张三"); // ✅ 可以
s1.showInfo();
// 测试protected构造函数(注意:在同一个包内也可以直接调用protected)
Student s2 = new Student("李四", "计算机"); // ✅ 可以在同一包内调用protected
s2.showInfo();
// 测试默认构造函数
Student s3 = new Student("王五", "S001", "数学"); // ✅ 可以,同一包内
s3.showInfo();
// 测试private构造函数(不能直接调用)
// Student s4 = new Student(); // ❌ 编译错误
// 通过静态方法获取private构造的对象
Student s4 = Student.createAdminStudent(); // ✅ 可以
s4.showInfo();
// 测试子类
GraduateStudent gs = new GraduateStudent("赵六", "物理"); // ✅ 可以
gs.showInfo();
}
}
java
package company; // 不同包
import university.Student;
public class TestDifferentPackage {
public static void main(String[] args) {
// public构造函数 - 任何地方都能用
Student s1 = new Student("小明"); // ✅ 可以
s1.showInfo();
// protected构造函数 - 在不同包的非子类中不能调用
// Student s2 = new Student("小红", "英语"); // ❌ 编译错误
// 默认构造函数 - 在不同包中不能调用
// Student s3 = new Student("小刚", "S002", "化学"); // ❌ 编译错误
// private构造函数 - 只能通过静态方法
Student s4 = Student.createAdminStudent(); // ✅ 可以(静态方法是public)
s4.showInfo();
}
}
| 构造函数修饰符 | 使用场景 | 典型例子 |
|---|---|---|
| public | 普通的、任何人都能创建的类 | DTO、实体类、工具类 |
| protected | 抽象类、基类,主要给子类用 | 框架中的基类 |
| 默认 | 包内协作,不对外公开的类 | 内部实现类、辅助类 |
| private | 严格控制创建方式 | 单例、工厂模式、工具类 |
修饰接口成员
接口成员有特殊的访问规则:
java
public interface MyInterface {
// 接口中的变量默认是 public static final
int PUBLIC_CONSTANT = 100; // 实际上是 public static final
// 接口中的方法默认是 public abstract
void publicMethod(); // 实际上是 public abstract
// Java 9+ 可以有private方法
private void privateMethod() {
System.out.println("Private method in interface");
}
// default方法默认是public
default void defaultMethod() {
privateMethod(); // 可以调用private方法
}
}
最佳实践总结
选择原则
- 最小权限原则:尽可能使用最严格的访问级别
- 封装性:将实现细节隐藏起来,只暴露必要的API
常用场景
| 场景 | 推荐修饰符 | 原因 |
|---|---|---|
| API公开方法 | public | 需要对外提供服务 |
| 工具类常量 | public | 需要全局访问 |
| 内部实现细节 | private | 隐藏实现,便于修改 |
| 子类需要的方法 | protected | 平衡封装和继承 |
| 同一包协作的类 | 默认 | 包级封装 |
java
public class BankAccount {
// 私有成员变量 - 封装数据
private String accountNumber;
private double balance;
// 公共构造函数
public BankAccount(String accountNumber) {
this.accountNumber = accountNumber;
this.balance = 0;
}
// 公共API
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
logTransaction("Deposit", amount); // 调用私有方法
}
}
public double getBalance() {
return balance;
}
// 保护方法 - 子类可以扩展
protected void logTransaction(String type, double amount) {
System.out.println(type + ": $" + amount);
}
// 私有方法 - 内部实现
private boolean validateAmount(double amount) {
return amount > 0 && amount < 10000;
}
}