在面向对象编程(OOP)中,封装(Encapsulation) 是一个核心概念,它通过隐藏对象的内部实现细节并仅暴露必要的接口来提高代码的安全性和可维护性。Java 提供了 访问修饰符(Access Modifiers) 来实现封装,控制类、变量、方法和构造函数的访问权限。
本文将全面解析 Java 的四种访问修饰符(
public
、protected
、default
和private
),并结合实际代码示例、最佳实践和常见面试题,帮助读者深入理解其使用场景和设计哲学。

1. 什么是访问修饰符?
访问修饰符用于定义 类、变量、方法和构造函数的可见性,即哪些代码可以访问它们。Java 提供了四种访问修饰符,按照访问权限从宽到严依次为:
-
public
(公开) -
protected
(受保护) -
default
(默认,包私有) -
private
(私有)
合理使用访问修饰符可以:
-
提高代码安全性:防止外部代码随意修改内部数据。
-
增强封装性:隐藏实现细节,仅暴露必要的接口。
-
降低耦合度:减少类之间的直接依赖,提高可维护性。
2. Java 的四种访问修饰符
(1) public
:完全开放访问
-
作用范围:任何类都可以访问。
-
适用场景:
-
类的主要 API 方法(如
main()
方法)。 -
需要全局访问的常量(如
public static final
)。
-
-
示例:
public class Animal { public String name; // 任何类都能访问 public void eat() { System.out.println("Animal is eating."); } } // 其他类可以自由访问 public class Test { public static void main(String[] args) { Animal dog = new Animal(); dog.name = "Buddy"; // 直接访问 public 变量 dog.eat(); // 调用 public 方法 } }
(2) protected
:子类与同包访问
-
作用范围:
-
同包内的所有类。
-
不同包的子类(通过继承访问)。
-
-
适用场景:
-
需要被子类重写的方法或变量。
-
框架设计时,允许子类扩展但不对外公开。
-
-
示例:
package com.example.animals; public class Animal { protected String species; // 子类和同包类可访问 protected void makeSound() { System.out.println("Animal sound"); } } // 同包类可直接访问 class Zoo { void displayAnimal(Animal a) { System.out.println(a.species); // 允许访问 } } // 不同包的子类 package com.example.pets; import com.example.animals.Animal; public class Dog extends Animal { void bark() { species = "Canine"; // 允许访问 protected 变量 makeSound(); // 允许调用 protected 方法 } }
(3) default
(包私有):同包访问
-
作用范围:仅同包内的类可以访问。
-
适用场景:
-
内部工具类或辅助方法,不希望被外部包使用。
-
模块化设计时,限制某些类仅限当前模块使用。
-
-
示例:
package com.example.utils; class Logger { // 默认 default 修饰符 void log(String message) { System.out.println("LOG: " + message); } } // 同包类可访问 class App { void run() { Logger logger = new Logger(); logger.log("App started"); // 允许访问 } } // 不同包类无法访问 package com.example.test; import com.example.utils.Logger; public class Test { public static void main(String[] args) { Logger logger = new Logger(); // 编译错误!Logger 不可见 } }
(4) private
:仅类内部访问
-
作用范围:仅当前类内部可访问。
-
适用场景:
-
封装类的内部状态(如成员变量)。
-
防止外部直接修改数据,必须通过
getter/setter
方法访问。
-
-
示例:
public class BankAccount { private double balance; // 私有变量,外部无法直接访问 public void deposit(double amount) { if (amount > 0) { balance += amount; } } public double getBalance() { return balance; // 通过 public 方法访问 } } public class Test { public static void main(String[] args) { BankAccount account = new BankAccount(); account.deposit(1000); System.out.println(account.getBalance()); // 正确 // account.balance = 5000; // 编译错误!private 变量不可访问 } }
3. 访问修饰符的作用范围对比
修饰符 | 同类 | 同包 | 子类(同包) | 子类(不同包) | 其他包 |
---|---|---|---|---|---|
public |
✓ | ✓ | ✓ | ✓ | ✓ |
protected |
✓ | ✓ | ✓ | ✓ | ✗ |
default |
✓ | ✓ | ✓ | ✗ | ✗ |
private |
✓ | ✗ | ✗ | ✗ | ✗ |
4. 类级别的访问控制
-
public
类:可以被任何包访问。package com.example; public class PublicClass { } // 任何地方都能访问
-
default
类:仅同包可访问。package com.example; class DefaultClass { } // 仅 com.example 包内可访问
-
注意:
-
一个
.java
文件只能有一个public
类,且文件名必须与public
类名相同。 -
内部类可以使用所有访问修饰符(
public
、protected
、default
、private
)。
-
5. 访问修饰符的最佳实践
-
优先使用
private
:封装成员变量,避免直接暴露。 -
谨慎使用
public
:仅对真正需要全局访问的 API 开放。 -
合理使用
protected
:适用于框架设计,允许子类扩展。 -
default
用于模块化:限制某些类仅限当前包使用。 -
使用
getter/setter
方法:控制对私有变量的访问。
6. 常见面试题
Q1: protected
和 default
的区别?
-
protected
:同包 + 子类(不同包)可访问。 -
default
:仅同包可访问。
Q2: 为什么推荐使用 private
+ getter/setter
?
-
防止外部直接修改数据,可以在
setter
中加入验证逻辑。 -
提高灵活性,未来可以修改内部实现而不影响调用方。
Q3: 一个类可以被 private
或 protected
修饰吗?
-
外部类 :只能是
public
或default
。 -
内部类:可以使用所有访问修饰符。
总结
Java 的访问修饰符是 封装 的关键机制,合理使用它们可以:
✅ 提高代码安全性
✅ 增强可维护性
✅ 降低耦合度
掌握 public
、protected
、default
和 private
的区别,能帮助开发者写出更健壮、更易扩展的代码。