访问权限
核心概念
访问权限是面向对象编程中实现封装的重要机制。通过控制类、方法和字段的可见性,我们可以隐藏实现细节,提供更安全和更清晰的接口。
一、访问权限修饰符
1.1 四种访问权限
public class AccessDemo {
// 四种访问权限
public int publicField; // 公共的,任何地方可访问
protected int protectedField; // 同包 + 子类可访问
int defaultField; // 包私有(默认),同包可访问
private int privateField; // 仅类内部可访问
}
1.2 可访问性矩阵
| 修饰符 | 类内部 | 同包 | 子类 | 任何地方 |
|---|---|---|---|---|
private |
✅ | ❌ | ❌ | ❌ |
default(包私有) |
✅ | ✅ | ❌ | ❌ |
protected |
✅ | ✅ | ✅ | ❌ |
public |
✅ | ✅ | ✅ | ✅ |
1.3 详细示例
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 封装的目的
// ❌ 错误的设计:暴露内部细节
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 高级封装示例
public class BankAccount {
private double balance;
private List<Transaction> 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 继承与访问权限
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 重写方法的访问权限
public class Parent {
protected void method() {}
}
public class Child extends Parent {
// ✅ 可以扩大访问权限
@Override
public void method() {}
// ❌ 不能缩小访问权限
// @Override
// private void method() {} // 编译错误
}
重写方法访问权限规则:
- 可以扩大访问权限
- 不能缩小访问权限
- 遵循"不能降低可见性"原则
四、常见陷阱与最佳实践
4.1 常见错误
// ❌ 错误:过度暴露
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 最佳实践
// ✅ 正确的封装
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<String> getRoles() {
return new ArrayList<>(roles); // 返回副本
}
}
五、高频面试题
Q1: 四种访问权限修饰符的区别?
答:
| 修饰符 | 类内部 | 同包 | 子类 | 任何地方 |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| default | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
Q2: 为什么要使用private?
答:
- 隐藏实现细节
- 防止外部直接修改内部状态
- 提供更可控的访问方式
- 实现封装的基本原则
Q3: protected和default有什么区别?
答:
default:仅同包可访问protected:同包 + 子类可访问protected更灵活,支持继承场景
Q4: 可以在子类中访问父类的private成员吗?
答:
- 不能直接访问
- 只能通过父类提供的public/protected方法间接访问
Q5: 为什么要遵循最小权限原则?
答:
- 降低耦合
- 提高安全性
- 隐藏实现细节
- 便于后期重构
六、最佳实践
✅ 推荐做法
// 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
}
❌ 避免做法
// 1. 避免public字段
public class BadDesign {
public int value; // 不好
}
// 2. 不要过度开放方法
public class Service {
public void internalMethod() { // 不应该是public
// 内部实现细节
}
}
相关链接
记忆口诀
私有最严格,默认同包行
保护子类用,公共天下宽
封装隐细节,getter setter来护航
最小权限牢记,安全是根本
相关笔记
访问控制修饰符 是本篇的精简对照版(站在"Java 优势/OOP 特性"视角),内容互有详略:本篇按"概念→四类范围→常见坑"系统展开,那边更像面试速记。两边可对照读,示例代码互补。
💬 评论