📊 权限修饰符总览
| 修饰符 | 同类 | 同包 | 子类(不同包) | 不同包非子类 | 级别 |
|---|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ | 最严格 |
| 默认 (无修饰符) | ✅ | ✅ | ❌ | ❌ | 包级别 |
| protected | ✅ | ✅ | ✅ | ❌ | 子类级别 |
| public | ✅ | ✅ | ✅ | ✅ | 最宽松 |
一般来说小编大多只使用private和public
🎯 四种权限修饰符详解
1. private(私有访问)
特点
- 只能在本类内部访问
- 最严格的访问控制
- 常用于封装,隐藏实现细节
java
typescript
package com.example;
public class PrivateDemo {
// 私有属性 - 只能在本类中访问
private String secret = "这是机密信息";
private int privateNumber = 100;
// 私有方法 - 只能在本类中调用
private void showSecret() {
System.out.println("机密信息: " + secret);
}
// 公共方法提供对私有属性的受控访问
public String getSecret() {
// 可以添加验证逻辑
if (checkPermission()) {
return secret;
}
return "无权访问";
}
public void setSecret(String newSecret) {
if (validateSecret(newSecret)) {
this.secret = newSecret;
}
}
private boolean checkPermission() {
// 权限检查逻辑
return true;
}
private boolean validateSecret(String secret) {
// 验证逻辑
return secret != null && !secret.trim().isEmpty();
}
// 在本类中可以自由访问私有成员
public void testAccess() {
System.out.println("在本类中访问:");
System.out.println("直接访问私有属性: " + secret);
System.out.println("调用私有方法: ");
showSecret();
// 修改私有属性
privateNumber = 200;
System.out.println("修改后: " + privateNumber);
}
public static void main(String[] args) {
PrivateDemo demo = new PrivateDemo();
demo.testAccess();
// 通过公共方法访问私有属性
System.out.println("\n通过公共方法访问:");
System.out.println("getSecret(): " + demo.getSecret());
demo.setSecret("新的机密");
System.out.println("修改后: " + demo.getSecret());
// ❌ 错误:不能直接访问私有成员
// System.out.println(demo.secret); // 编译错误
// demo.showSecret(); // 编译错误
// demo.privateNumber = 300; // 编译错误
}
}
// 同包中的另一个类
class SamePackageClass {
void test() {
PrivateDemo demo = new PrivateDemo();
// ❌ 错误:同包其他类也不能访问private成员
// System.out.println(demo.secret); // 编译错误
// demo.showSecret(); // 编译错误
// ✅ 正确:只能通过公共方法访问
System.out.println(demo.getSecret());
}
}
private 在构造器中的应用
java
csharp
public class Singleton {
// 私有静态变量
private static Singleton instance;
// 私有构造器 - 防止外部创建实例
private Singleton() {
System.out.println("Singleton实例被创建");
}
// 公共静态方法提供访问
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void showMessage() {
System.out.println("Hello Singleton!");
}
public static void main(String[] args) {
// ❌ 错误:不能直接new
// Singleton s = new Singleton();
// ✅ 正确:通过静态方法获取
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println("s1 == s2: " + (s1 == s2)); // true
s1.showMessage();
}
}
2. 默认/包私有(Default/Package-private)
特点
- 没有明确指定修饰符
- 在同一个包内可以访问
- 也称为"包访问权限"或"包私有"
java
csharp
package com.example;
// 默认类:同包可见
class DefaultClass {
// 默认属性:同包可见
String defaultField = "默认属性";
// 默认方法:同包可见
void defaultMethod() {
System.out.println("默认方法");
}
// 公共方法:所有地方可见
public void publicMethod() {
System.out.println("公共方法");
}
}
public class DefaultModifierDemo {
// 默认属性
String packagePrivateField = "包私有属性";
// 默认方法
void packagePrivateMethod() {
System.out.println("包私有方法");
System.out.println("可以访问: " + packagePrivateField);
}
public static void main(String[] args) {
DefaultModifierDemo demo = new DefaultModifierDemo();
// 在本类中可以访问默认成员
demo.packagePrivateMethod();
// 在同包中访问另一个类的默认成员
DefaultClass obj = new DefaultClass();
System.out.println("\n访问同包的DefaultClass:");
System.out.println(obj.defaultField); // ✅ 可以访问
obj.defaultMethod(); // ✅ 可以调用
obj.publicMethod(); // ✅ 可以调用
}
}
// 同包中的另一个类
package com.example;
class AnotherClassInSamePackage {
void test() {
DefaultModifierDemo demo = new DefaultModifierDemo();
// ✅ 正确:同包可以访问默认成员
System.out.println(demo.packagePrivateField);
demo.packagePrivateMethod();
DefaultClass obj = new DefaultClass();
System.out.println(obj.defaultField);
obj.defaultMethod();
}
}
不同包的情况
java
java
// 不同包中的类
package other;
import com.example.DefaultModifierDemo;
import com.example.DefaultClass; // ❌ 编译错误:DefaultClass不是public
public class DifferentPackageClass {
void test() {
DefaultModifierDemo demo = new DefaultModifierDemo();
// ❌ 错误:不同包不能访问默认成员
// System.out.println(demo.packagePrivateField); // 编译错误
// demo.packagePrivateMethod(); // 编译错误
// ✅ 正确:可以访问公共成员
// demo.publicMethod(); // 如果有的话
// ❌ 错误:不能访问默认类
// DefaultClass obj = new DefaultClass(); // 编译错误
}
}
3. protected(受保护访问)
特点
- 在同一个包内可以访问
- 在不同包的子类中可以访问
- 常用于允许子类访问父类的实现细节
java
csharp
package com.base;
public class Parent {
// protected 属性
protected String protectedField = "父类的protected属性";
// protected 方法
protected void protectedMethod() {
System.out.println("父类的protected方法");
}
// 默认属性(对比用)
String defaultField = "父类的默认属性";
// 公共方法
public void show() {
System.out.println("在本类中访问:");
System.out.println("protectedField: " + protectedField);
System.out.println("defaultField: " + defaultField);
protectedMethod();
}
}
// 同包的子类
package com.base;
class SamePackageChild extends Parent {
public void test() {
System.out.println("同包子类访问:");
// ✅ 可以访问 protected 成员
System.out.println("protectedField: " + protectedField);
protectedMethod();
// ✅ 可以访问默认成员(同包)
System.out.println("defaultField: " + defaultField);
// 通过 super 访问
System.out.println("通过super访问: " + super.protectedField);
super.protectedMethod();
}
}
// 同包的非子类
package com.base;
class SamePackageNonChild {
public void test() {
Parent parent = new Parent();
System.out.println("同包非子类访问:");
// ✅ 可以访问 protected 成员(同包)
System.out.println("protectedField: " + parent.protectedField);
parent.protectedMethod();
// ✅ 可以访问默认成员(同包)
System.out.println("defaultField: " + parent.defaultField);
}
}
不同包子类的情况
java
java
// 不同包的子类
package com.derived;
import com.base.Parent;
public class DifferentPackageChild extends Parent {
public void test() {
System.out.println("不同包子类访问:");
// ✅ 可以访问 protected 成员(子类权限)
System.out.println("protectedField: " + protectedField);
protectedMethod();
// 通过 super 访问
System.out.println("通过super访问: " + super.protectedField);
super.protectedMethod();
// ❌ 错误:不能访问默认成员(不同包)
// System.out.println(defaultField); // 编译错误
// ❌ 错误:不能通过父类实例访问 protected 成员
Parent parent = new Parent();
// System.out.println(parent.protectedField); // 编译错误
// parent.protectedMethod(); // 编译错误
// ✅ 正确:可以通过自身实例访问
DifferentPackageChild child = new DifferentPackageChild();
System.out.println("通过子类实例: " + child.protectedField);
child.protectedMethod();
}
}
// 不同包的非子类
package com.derived;
import com.base.Parent;
class DifferentPackageNonChild {
public void test() {
Parent parent = new Parent();
System.out.println("不同包非子类访问:");
// ❌ 错误:不能访问 protected 成员
// System.out.println(parent.protectedField); // 编译错误
// parent.protectedMethod(); // 编译错误
// ❌ 错误:不能访问默认成员
// System.out.println(parent.defaultField); // 编译错误
// ✅ 正确:只能访问 public 成员
parent.show(); // 如果有的话
}
}
4. public(公共访问)
特点
- 在任何地方都可以访问
- 最宽松的访问控制
- 用于定义对外提供的接口
java
csharp
package com.example;
// public 类:所有地方可见
public class PublicDemo {
// public 属性:所有地方可见
public String publicField = "公共属性";
// public 方法:所有地方可见
public void publicMethod() {
System.out.println("公共方法");
System.out.println("可以访问: " + publicField);
}
// 其他权限的方法
protected void protectedMethod() {
System.out.println("受保护方法");
}
void defaultMethod() {
System.out.println("默认方法");
}
private void privateMethod() {
System.out.println("私有方法");
}
public static void main(String[] args) {
PublicDemo demo = new PublicDemo();
// 在本类中可以访问所有权限的成员
demo.publicMethod();
demo.protectedMethod();
demo.defaultMethod();
demo.privateMethod();
}
}
// 同包的另一个类
package com.example;
class SamePackageClass {
void test() {
PublicDemo demo = new PublicDemo();
System.out.println("同包访问:");
// ✅ 可以访问 public 成员
System.out.println(demo.publicField);
demo.publicMethod();
// ✅ 可以访问 protected 成员(同包)
demo.protectedMethod();
// ✅ 可以访问默认成员(同包)
demo.defaultMethod();
// ❌ 不能访问 private 成员
// demo.privateMethod(); // 编译错误
}
}
不同包的情况
java
csharp
// 不同包中的类
package other;
import com.example.PublicDemo;
public class DifferentPackageClass {
void test() {
PublicDemo demo = new PublicDemo();
System.out.println("不同包访问:");
// ✅ 可以访问 public 成员
System.out.println(demo.publicField);
demo.publicMethod();
// ❌ 不能访问 protected 成员(不同包非子类)
// demo.protectedMethod(); // 编译错误
// ❌ 不能访问默认成员(不同包)
// demo.defaultMethod(); // 编译错误
// ❌ 不能访问 private 成员
// demo.privateMethod(); // 编译错误
}
}
🏗️ 权限修饰符的应用场景
1. 封装:数据隐藏
java
typescript
// 银行账户类 - 使用封装保护数据
public class BankAccount {
// 私有属性:外部不能直接访问
private String accountNumber;
private String accountHolder;
private double balance;
private String password;
// 公共构造器
public BankAccount(String accountNumber, String accountHolder,
String password, double initialBalance) {
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.password = password;
this.balance = initialBalance;
}
// 公共方法:提供受控的访问
// Getter 方法(只读)
public String getAccountNumber() {
return accountNumber;
}
public String getAccountHolder() {
return accountHolder;
}
public double getBalance() {
return balance;
}
// 没有 password 的 Getter(安全考虑)
// Setter 方法(有验证)
public void setAccountHolder(String accountHolder) {
if (accountHolder != null && !accountHolder.trim().isEmpty()) {
this.accountHolder = accountHolder;
}
}
// 业务方法
public boolean deposit(double amount) {
if (amount > 0) {
balance += amount;
return true;
}
return false;
}
public boolean withdraw(double amount, String password) {
if (authenticate(password) && amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
// 私有方法:内部使用
private boolean authenticate(String inputPassword) {
return this.password.equals(inputPassword);
}
private void logTransaction(String type, double amount) {
System.out.println("交易记录: " + type + " $" + amount);
}
// 受保护方法:给子类扩展用
protected void applyFee(double fee) {
if (fee > 0 && balance >= fee) {
balance -= fee;
}
}
public static void main(String[] args) {
BankAccount account = new BankAccount("123456", "张三", "123456", 1000);
// ✅ 正确:通过公共方法访问
System.out.println("账户: " + account.getAccountNumber());
System.out.println("余额: $" + account.getBalance());
account.deposit(500);
System.out.println("存款后余额: $" + account.getBalance());
boolean success = account.withdraw(200, "123456");
System.out.println("取款" + (success ? "成功" : "失败"));
System.out.println("取款后余额: $" + account.getBalance());
// ❌ 错误:不能直接访问私有属性
// System.out.println(account.balance); // 编译错误
// account.balance = 10000; // 编译错误
// System.out.println(account.password); // 编译错误
}
}
2. 继承框架设计
java
csharp
package framework;
// 框架基类
public abstract class BaseService {
// 公共方法:对外接口
public final void execute() {
initialize();
process();
cleanup();
}
// 受保护方法:子类必须实现或可以重写
protected abstract void initialize();
protected abstract void process();
// 受保护方法:子类可以重写
protected void cleanup() {
System.out.println("执行清理工作...");
}
// 私有方法:内部实现细节
private void log(String message) {
System.out.println("[LOG] " + message);
}
// 默认方法:同包的工具方法
void internalHelper() {
log("内部辅助方法");
}
}
// 同包的工具类
package framework;
class ServiceValidator {
// 默认方法:同包内使用
static boolean validate(BaseService service) {
// 可以访问 service.internalHelper()(同包)
return true;
}
}
java
typescript
// 用户实现类(不同包)
package userapp;
import framework.BaseService;
public class UserService extends BaseService {
// 实现抽象方法
@Override
protected void initialize() {
System.out.println("用户服务初始化");
}
@Override
protected void process() {
System.out.println("处理用户请求");
}
// 可以重写 cleanup 方法
@Override
protected void cleanup() {
super.cleanup(); // 调用父类方法
System.out.println("额外的清理工作");
}
// 不能访问父类的私有方法
// private void tryAccess() {
// log("测试"); // 编译错误:不能访问父类的private方法
// }
// 不能访问父类的默认方法(不同包)
// void tryInternal() {
// internalHelper(); // 编译错误:不同包不能访问默认方法
// }
public static void main(String[] args) {
UserService service = new UserService();
service.execute();
}
}
3. 常量定义
java
arduino
// 常量类设计
public final class Constants {
// 私有构造器:防止实例化
private Constants() {
throw new AssertionError("不能实例化常量类");
}
// 公共常量:所有地方可用
public static final String APP_NAME = "MyApplication";
public static final double PI = 3.141592653589793;
public static final int MAX_RETRY_COUNT = 3;
// 受保护常量:子类可用
protected static final String DEFAULT_ENCODING = "UTF-8";
// 默认常量:同包可用
static final String CONFIG_FILE = "app.properties";
// 私有常量:仅本类可用
private static final String SECRET_KEY = "secret123";
// 公共方法使用常量
public static String getSecretHash() {
return hash(SECRET_KEY); // 可以访问私有常量
}
private static String hash(String input) {
// 哈希计算
return input + "_hashed";
}
// 同包辅助方法
static String getConfigPath() {
return CONFIG_FILE; // 可以访问默认常量
}
}
// 使用常量
public class ConstantUsage {
public static void main(String[] args) {
System.out.println("应用名称: " + Constants.APP_NAME);
System.out.println("PI值: " + Constants.PI);
System.out.println("最大重试次数: " + Constants.MAX_RETRY_COUNT);
// 访问公共方法
System.out.println("密钥哈希: " + Constants.getSecretHash());
// ❌ 错误:不能访问受保护常量(不是子类)
// System.out.println(Constants.DEFAULT_ENCODING);
// ❌ 错误:不能访问默认常量(不同包)
// System.out.println(Constants.CONFIG_FILE);
// ❌ 错误:不能访问私有常量
// System.out.println(Constants.SECRET_KEY);
}
}
⚠️ 权限修饰符的注意事项
1. 类级别的访问控制
java
csharp
// 公共类:所有地方可见(文件名必须与类名相同)
public class PublicClass {
public void show() {
System.out.println("公共类");
}
}
// 默认类:同包可见
class DefaultClass {
void show() {
System.out.println("默认类");
}
}
// 错误:一个文件只能有一个公共类,且必须与文件名相同
/*
public class AnotherPublicClass { // 编译错误
}
*/
// 内部类可以是任何访问级别
class Outer {
public class PublicInner {}
protected class ProtectedInner {}
class DefaultInner {}
private class PrivateInner {}
}
// 测试
public class ClassLevelAccess {
public static void main(String[] args) {
PublicClass pc = new PublicClass();
pc.show();
// 同包可以访问默认类
DefaultClass dc = new DefaultClass();
dc.show();
System.out.println("\n类访问规则:");
System.out.println("1. 一个.java文件只能有一个public类");
System.out.println("2. public类名必须与文件名相同");
System.out.println("3. 可以有多个默认类");
System.out.println("4. 内部类可以是任何访问级别");
}
}
2. 接口成员的访问权限
java
csharp
// 接口中的成员默认都是 public
public interface MyInterface {
// 常量默认是 public static final
String CONSTANT = "接口常量"; // 等价于 public static final
// 方法默认是 public abstract
void doSomething(); // 等价于 public abstract void doSomething()
// Java 8+:默认方法
default void defaultMethod() {
System.out.println("默认方法");
}
// Java 8+:静态方法
static void staticMethod() {
System.out.println("静态方法");
}
// ❌ 错误:不能使用private(Java 9+可以)
// private void privateMethod(); // Java 8编译错误
// ❌ 错误:不能使用protected
// protected void protectedMethod(); // 编译错误
}
// 实现接口
class MyImplementation implements MyInterface {
// 必须使用public重写(不能更严格)
@Override
public void doSomething() {
System.out.println("实现接口方法");
}
// 可以重写默认方法(可选)
@Override
public void defaultMethod() {
MyInterface.super.defaultMethod(); // 调用接口的默认方法
System.out.println("重写的默认方法");
}
}
public class InterfaceAccess {
public static void main(String[] args) {
MyInterface.staticMethod(); // 调用接口静态方法
MyImplementation impl = new MyImplementation();
impl.doSomething();
impl.defaultMethod();
System.out.println("常量: " + MyInterface.CONSTANT);
}
}
3. 构造器的访问权限
java
csharp
public class ConstructorAccess {
// 公共构造器:所有地方可以创建对象
public ConstructorAccess() {
System.out.println("公共构造器");
}
// 受保护构造器:同包或子类可以创建对象
protected ConstructorAccess(int a) {
System.out.println("受保护构造器: " + a);
}
// 默认构造器:同包可以创建对象
ConstructorAccess(String s) {
System.out.println("默认构造器: " + s);
}
// 私有构造器:只能在本类中创建对象
private ConstructorAccess(boolean b) {
System.out.println("私有构造器: " + b);
}
// 静态工厂方法访问私有构造器
public static ConstructorAccess createPrivateInstance() {
return new ConstructorAccess(true);
}
public static void main(String[] args) {
// 本类中可以访问所有构造器
new ConstructorAccess(); // public
new ConstructorAccess(10); // protected
new ConstructorAccess("test"); // default
new ConstructorAccess(true); // private
// 工厂方法
ConstructorAccess.createPrivateInstance();
}
}
// 同包的其他类
class SamePackageClass {
void test() {
new ConstructorAccess(); // ✅ public
new ConstructorAccess(20); // ✅ protected(同包)
new ConstructorAccess("hello"); // ✅ default(同包)
// new ConstructorAccess(true); // ❌ private
}
}
// 不同包子类
package other;
import ConstructorAccess;
class DifferentPackageChild extends ConstructorAccess {
public DifferentPackageChild() {
super(30); // ✅ 可以调用protected构造器(子类)
// super("test"); // ❌ 不能调用default构造器(不同包)
}
void test() {
// new ConstructorAccess(40); // ❌ 不能直接调用(需要是子类构造器中)
}
}
// 不同包非子类
package other;
import ConstructorAccess;
class DifferentPackageNonChild {
void test() {
new ConstructorAccess(); // ✅ public
// new ConstructorAccess(50); // ❌ protected
// new ConstructorAccess("test"); // ❌ default
// new ConstructorAccess(true); // ❌ private
}
}
💡 权限修饰符最佳实践
1. 最小权限原则
java
typescript
// 遵循最小权限原则:只暴露必要的内容
public class Employee {
// 私有属性:隐藏实现细节
private String id;
private String name;
private double salary;
private String ssn; // 社保号(敏感信息)
// 公共构造器:允许创建对象
public Employee(String id, String name) {
this.id = id;
this.name = name;
this.salary = 0.0;
}
// 公共getter:只读访问
public String getId() {
return id;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
// 受保护setter:允许子类或同包修改
protected void setSalary(double salary) {
if (salary >= 0) {
this.salary = salary;
}
}
// 默认方法:同包工具方法
void internalUpdate(String newName) {
if (newName != null && newName.length() >= 2) {
this.name = newName;
}
}
// 私有方法:内部逻辑
private void calculateBonus() {
// 奖金计算逻辑
}
// 没有提供ssn的getter/setter(安全考虑)
}
// 管理者类(需要修改工资)
class Manager extends Employee {
public Manager(String id, String name) {
super(id, name);
}
public void adjustSalary(Employee emp, double newSalary) {
// 可以调用受保护方法
emp.setSalary(newSalary);
}
}
2. 模块化设计
java
java
// 包结构示例:
// com.company
// ├── api (公共API)
// ├── impl (实现,包私有)
// ├── internal (内部实现,包私有)
// └── util (工具类)
// api包:公共接口
package com.company.api;
public interface UserService {
User getUser(String id);
void saveUser(User user);
}
// impl包:实现(包私有)
package com.company.impl;
import com.company.api.UserService;
import com.company.internal.UserValidator;
class UserServiceImpl implements UserService {
private UserValidator validator = new UserValidator();
@Override
public User getUser(String id) {
// 实现...
return null;
}
@Override
public void saveUser(User user) {
if (validator.validate(user)) {
// 保存逻辑
}
}
}
// internal包:内部工具(包私有)
package com.company.internal;
class UserValidator {
boolean validate(User user) {
// 验证逻辑
return true;
}
}
// 公共工厂
package com.company;
import com.company.api.UserService;
import com.company.impl.UserServiceImpl;
public class ServiceFactory {
// 公共方法返回接口类型,隐藏实现
public static UserService createUserService() {
return new UserServiceImpl();
}
}
📊 权限修饰符选择指南
选择流程图
text
arduino
开始
↓
需要从外部访问吗?
├─ 否 → 使用 private
↓
需要被子类访问吗?
├─ 否 → 需要同包访问吗?
│ ├─ 是 → 使用 默认
│ ↓
│ 否 → 使用 public
↓
是 → 需要不同包子类访问吗?
├─ 是 → 使用 protected
↓
否 → 使用 默认
决策表格
| 场景 | 推荐修饰符 | 示例 |
|---|---|---|
| 数据字段 | private | private String name; |
| 常量 | public static final | public static final PI = 3.14; |
| 工具方法 | private | private void validate() |
| 模板方法 | protected | protected void initialize() |
| 接口方法 | public | public void save() |
| 构造器 | public / private | public User() / 单例模式 |
| 内部类 | private | private class Node |
| 测试钩子 | protected | protected void setUp() |
🎓 总结要点
- private:严格封装,仅本类可见
- 默认:包级别封装,同包可见
- protected:继承友好,同包+子类可见
- public:完全开放,所有地方可见
核心原则:
- 最小权限原则:只开放必要的访问权限
- 封装变化:使用private隐藏实现细节
- 面向接口:通过public方法提供稳定接口
- 继承设计:protected用于设计可扩展的框架
- 包组织:合理使用包和默认权限管理模块
记住:良好的访问控制是高质量代码的基础!合理的权限设置可以提高代码的安全性、可维护性和可扩展性。