【Java SE】Java访问修饰符总结

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方法
    }
}

最佳实践总结

选择原则

  1. 最小权限原则:尽可能使用最严格的访问级别
  2. 封装性:将实现细节隐藏起来,只暴露必要的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;
    }
}
相关推荐
拾贰_C2 小时前
【idea | knife4j | springboot2/3|接上篇|终篇】knife4j版本号与spring boot版本不兼容问题(细节问题)
java·spring boot·intellij-idea
C182981825752 小时前
Rocketmq
java·rocketmq·java-rocketmq
枫叶丹42 小时前
【HarmonyOS 6.0】聚合链接(App Linking)实战:从创建配置到应用跳转
开发语言·华为·harmonyos
甲枫叶2 小时前
【openclaw】我用 OpenClaw 自动化了这些工作
java·python·自动化·ai编程
6+h2 小时前
【Spring】Mapper层常用注解详解
java·后端·spring
予枫的编程笔记2 小时前
【面试专栏|Java并发编程】Thread类核心方法全解:start和run的区别
java·thread·java多线程·java面试·start方法·wait notify·sleep方法
y = xⁿ2 小时前
【黑马点评配置篇】
java·ide·intellij-idea
一路向北North2 小时前
Spring Security OAuth2.0(10):登出
java·后端·spring
代码的奴隶(艾伦·耶格尔)2 小时前
Hbase的使用
java·前端·hbase