--- title: "03-访问权限" created: 2025-11-25 tags: - Java --- # 访问权限 ## 核心概念 访问权限是面向对象编程中实现封装的重要机制。通过控制类、方法和字段的可见性,我们可以隐藏实现细节,提供更安全和更清晰的接口。 --- ## 一、访问权限修饰符 ### 1.1 四种访问权限 ```java public class AccessDemo { // 四种访问权限 public int publicField; // 公共的,任何地方可访问 protected int protectedField; // 同包 + 子类可访问 int defaultField; // 包私有(默认),同包可访问 private int privateField; // 仅类内部可访问 } ``` ### 1.2 可访问性矩阵 | 修饰符 | 类内部 | 同包 | 子类 | 任何地方 | | --- | --- | --- | --- | --- | | `private` | ✅ | ❌ | ❌ | ❌ | | `default`(包私有) | ✅ | ✅ | ❌ | ❌ | | `protected` | ✅ | ✅ | ✅ | ❌ | | `public` | ✅ | ✅ | ✅ | ✅ | ### 1.3 详细示例 ```java package com.example.access; public class Parent { public int publicField = 1; protected int protectedField = 2; int defaultField = 3; private int privateField = 4; public void publicMethod() {} protected void protectedMethod() {} void defaultMethod() {} private void privateMethod() {} } // 同包子类 class Child extends Parent { void test() { publicField = 10; // ✅ 可访问 protectedField = 20; // ✅ 可访问 defaultField = 30; // ✅ 可访问 // privateField = 40; // ❌ 不可访问 publicMethod(); // ✅ 可访问 protectedMethod(); // ✅ 可访问 defaultMethod(); // ✅ 可访问 // privateMethod(); // ❌ 不可访问 } } // 不同包的类 public class OtherClass { void test() { Parent parent = new Parent(); parent.publicField = 10; // ✅ 可访问 // parent.protectedField = 20; // ❌ 不可访问 // parent.defaultField = 30; // ❌ 不可访问 // parent.privateField = 40; // ❌ 不可访问 } } ``` --- ## 二、封装原则(Encapsulation) ### 2.1 封装的目的 ```java // ❌ 错误的设计:暴露内部细节 public class User { public String username; public String password; } // ✅ 正确的设计:封装 public class User { private String username; private String password; // 提供受控的访问方法 public String getUsername() { return username; } public void setUsername(String username) { // 可以添加验证逻辑 if (username != null && !username.isEmpty()) { this.username = username; } } } ``` ### 2.2 封装的好处 | 好处 | 说明 | 示例 | | --- | --- | --- | | **隐藏实现细节** | 内部实现可以随时改变 | 修改私有字段不影响外部代码 | | **数据验证** | 控制数据的合法性 | 检查年龄、密码等 | | **只读/只写控制** | 精细控制属性访问 | 只提供getter不提供setter | | **计算属性** | 动态计算属性值 | 年龄根据出生日期计算 | ### 2.3 高级封装示例 ```java public class BankAccount { private double balance; private List transactions = new ArrayList<>(); // 私有内部类 private class Transaction { private double amount; private Date timestamp; // 仅在BankAccount内部使用 } // 受控的存款方法 public void deposit(double amount) { if (amount > 0) { balance += amount; recordTransaction(amount); } } // 受控的取款方法 public void withdraw(double amount) { if (amount > 0 && amount <= balance) { balance -= amount; recordTransaction(-amount); } } // 只读属性 public double getBalance() { return balance; } // 私有方法,仅在类内部使用 private void recordTransaction(double amount) { transactions.add(new Transaction(amount, new Date())); } } ``` --- ## 三、继承中的访问权限 ### 3.1 继承与访问权限 ```java public class Parent { private int privateField; protected int protectedField; private void privateMethod() {} protected void protectedMethod() {} } public class Child extends Parent { void test() { // protectedField = 10; // ✅ 可以访问 // protectedMethod(); // ✅ 可以调用 // privateField = 20; // ❌ 不能访问 // privateMethod(); // ❌ 不能调用 } } ``` ### 3.2 重写方法的访问权限 ```java public class Parent { protected void method() {} } public class Child extends Parent { // ✅ 可以扩大访问权限 @Override public void method() {} // ❌ 不能缩小访问权限 // @Override // private void method() {} // 编译错误 } ``` **重写方法访问权限规则:** - 可以扩大访问权限 - 不能缩小访问权限 - 遵循"不能降低可见性"原则 --- ## 四、常见陷阱与最佳实践 ### 4.1 常见错误 ```java // ❌ 错误:过度暴露 public class User { public String password; // 安全风险 } // ❌ 错误:不恰当的getter/setter public class Person { private int age; // 没有任何验证的setter public void setAge(int age) { this.age = age; // 可以设置负数年龄 } } ``` ### 4.2 最佳实践 ```java // ✅ 正确的封装 public class User { // 1. 私有字段 private String username; private String password; // 2. 安全的getter/setter public String getUsername() { return username; } public void setUsername(String username) { // 添加验证逻辑 if (username != null && !username.isEmpty()) { this.username = username; } } // 3. 只读属性 public boolean isAdmin() { return "admin".equals(username); } // 4. 防御性拷贝 public List getRoles() { return new ArrayList<>(roles); // 返回副本 } } ``` --- ## 五、高频面试题 ### Q1: 四种访问权限修饰符的区别? **答:** | 修饰符 | 类内部 | 同包 | 子类 | 任何地方 | | --- | --- | --- | --- | --- | | private | ✅ | ❌ | ❌ | ❌ | | default | ✅ | ✅ | ❌ | ❌ | | protected | ✅ | ✅ | ✅ | ❌ | | public | ✅ | ✅ | ✅ | ✅ | ### Q2: 为什么要使用private? **答:** - 隐藏实现细节 - 防止外部直接修改内部状态 - 提供更可控的访问方式 - 实现封装的基本原则 ### Q3: protected和default有什么区别? **答:** - `default`:仅同包可访问 - `protected`:同包 + 子类可访问 - `protected`更灵活,支持继承场景 ### Q4: 可以在子类中访问父类的private成员吗? **答:** - 不能直接访问 - 只能通过父类提供的public/protected方法间接访问 ### Q5: 为什么要遵循最小权限原则? **答:** - 降低耦合 - 提高安全性 - 隐藏实现细节 - 便于后期重构 --- ## 六、最佳实践 ### ✅ 推荐做法 ```java // 1. 尽量使用private public class User { private String username; // 提供受控的访问方法 public String getUsername() { return username; } } // 2. 使用final防止过度继承 public final class ImmutableClass { private final int value; } // 3. 接口方法默认public public interface MyInterface { void method(); // 默认public } ``` ### ❌ 避免做法 ```java // 1. 避免public字段 public class BadDesign { public int value; // 不好 } // 2. 不要过度开放方法 public class Service { public void internalMethod() { // 不应该是public // 内部实现细节 } } ``` --- ## 相关链接 - [[01-继承与多态|01-继承与多态]] - [[02-抽象类与接口|02-抽象类与接口]] - [[07-面向对象设计的SOLID原则|面向对象设计的SOLID原则]] --- ## 记忆口诀 ``` 私有最严格,默认同包行 保护子类用,公共天下宽 封装隐细节,getter setter来护航 最小权限牢记,安全是根本 ``` ## 相关笔记 [[06-访问控制修饰符|访问控制修饰符]] 是本篇的精简对照版(站在"Java 优势/OOP 特性"视角),内容互有详略:本篇按"概念→四类范围→常见坑"系统展开,那边更像面试速记。两边可对照读,示例代码互补。 --- ⬅️ [[02-抽象类与接口|抽象类与接口]] 🏠 [[00-Java|00-Java]] ➡️ [[04-重写与重载|重写与重载]]