第24章

Java访问修饰符

掌握public、private、protected和默认访问权限的使用方法与访问控制规则

学习目标

访问修饰符概述

访问修饰符是Java面向对象编程中的重要概念,用于控制类、方法和变量的访问权限。Java提供了四种访问修饰符,它们决定了代码的可见性和封装程度,是实现数据隐藏和接口设计的关键工具。

访问控制的重要性:
  • 保护类的内部实现细节
  • 提供清晰的公共接口
  • 防止不当的外部访问
  • 支持代码的模块化设计

public(公共的)

语法示例:
public class MyClass {
    public int publicField;
    public void publicMethod() {
        // 公共方法
    }
}
  • 任何地方都可以访问
  • 跨包访问无限制
  • 适用于公共API
  • 提供对外接口

private(私有的)

语法示例:
public class MyClass {
    private int privateField;
    private void privateMethod() {
        // 私有方法
    }
}
  • 只能在同一个类中访问
  • 实现数据封装
  • 隐藏实现细节
  • 提高代码安全性

protected(受保护的)

语法示例:
public class Parent {
    protected int protectedField;
    protected void protectedMethod() {
        // 受保护方法
    }
}
  • 同包内可以访问
  • 子类可以访问
  • 支持继承机制
  • 平衡封装和继承

默认(包访问权限)

语法示例:
class MyClass {
    int defaultField;
    void defaultMethod() {
        // 默认访问权限方法
    }
}
  • 同包内可以访问
  • 不跨包访问
  • 包级别的封装
  • 模块内部使用

访问权限对比表

下表详细说明了四种访问修饰符在不同访问场景下的权限控制:

访问修饰符 同一个类 同一个包 不同包的子类 不同包的非子类
public
protected
默认
private

详细用法示例

public修饰符详解

public修饰符提供最大的访问权限,适用于需要对外公开的类、方法和字段。

public修饰符示例:
// 公共类,可以被任何地方访问
public class PublicExample {
    // 公共字段,通常不推荐
    public String publicField = "公共字段";
    
    // 公共常量,推荐使用
    public static final String CONSTANT = "常量";
    
    // 公共构造方法
    public PublicExample() {
        System.out.println("创建PublicExample对象");
    }
    
    // 公共方法,对外提供服务
    public void publicMethod() {
        System.out.println("这是公共方法");
    }
    
    // 公共静态方法,工具方法
    public static void utilityMethod() {
        System.out.println("这是公共静态方法");
    }
}

private修饰符详解

private修饰符提供最严格的访问控制,是实现封装的重要手段。

private修饰符示例:
public class PrivateExample {
    // 私有字段,外部无法直接访问
    private String name;
    private int age;
    private double salary;
    
    // 私有常量
    private static final int MAX_AGE = 120;
    
    // 公共构造方法
    public PrivateExample(String name, int age) {
        this.name = name;
        this.age = validateAge(age); // 调用私有方法
    }
    
    // 私有方法,内部逻辑处理
    private int validateAge(int age) {
        if (age < 0 || age > MAX_AGE) {
            throw new IllegalArgumentException("年龄无效");
        }
        return age;
    }
    
    // 私有方法,计算退休年龄
    private int calculateRetirementAge() {
        return 65 - age;
    }
    
    // 公共getter方法,提供受控访问
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    // 公共setter方法,提供受控修改
    public void setAge(int age) {
        this.age = validateAge(age);
    }
    
    // 公共方法,使用私有方法
    public void displayRetirementInfo() {
        int yearsToRetirement = calculateRetirementAge();
        if (yearsToRetirement > 0) {
            System.out.println(name + "还有" + yearsToRetirement + "年退休");
        } else {
            System.out.println(name + "已经退休");
        }
    }
}

protected修饰符详解

protected修饰符在继承关系中发挥重要作用,允许子类访问父类的受保护成员。

protected修饰符示例:
// 父类
public class Animal {
    // 受保护字段,子类可以访问
    protected String species;
    protected int age;
    
    // 受保护构造方法
    protected Animal(String species, int age) {
        this.species = species;
        this.age = age;
    }
    
    // 受保护方法,子类可以重写
    protected void makeSound() {
        System.out.println("动物发出声音");
    }
    
    // 受保护方法,提供给子类使用
    protected void displayInfo() {
        System.out.println("物种: " + species + ", 年龄: " + age);
    }
}

// 子类
public class Dog extends Animal {
    private String breed;
    
    public Dog(String breed, int age) {
        super("犬类", age); // 调用父类受保护构造方法
        this.breed = breed;
    }
    
    // 重写父类受保护方法
    @Override
    protected void makeSound() {
        System.out.println("汪汪汪!");
    }
    
    // 子类方法,访问父类受保护成员
    public void showDetails() {
        displayInfo(); // 调用父类受保护方法
        System.out.println("品种: " + breed);
        System.out.println("物种: " + species); // 访问父类受保护字段
    }
}

默认访问权限详解

默认访问权限(包访问权限)适用于包内部的类和成员,提供包级别的封装。

默认访问权限示例:
// 包内类,只能在同一包中访问
class PackageClass {
    // 包访问权限字段
    String packageField = "包字段";
    int packageInt = 100;
    
    // 包访问权限构造方法
    PackageClass() {
        System.out.println("创建包类对象");
    }
    
    // 包访问权限方法
    void packageMethod() {
        System.out.println("这是包方法");
    }
    
    // 包访问权限静态方法
    static void staticPackageMethod() {
        System.out.println("这是包静态方法");
    }
}

// 同包中的公共类
public class PackageExample {
    public void demonstratePackageAccess() {
        // 可以访问同包中的包类
        PackageClass obj = new PackageClass();
        
        // 可以访问包字段和方法
        System.out.println(obj.packageField);
        obj.packageMethod();
        
        // 可以调用包静态方法
        PackageClass.staticPackageMethod();
    }
}

在线代码体验

点击下面的按钮,在在线IDE中查看和运行完整的访问修饰符示例代码:

💻 查看完整代码 - 在线IDE体验

访问修饰符最佳实践

设计原则和最佳实践

推荐做法

  • 最小权限原则:使用最严格的访问修饰符
  • 字段私有化:将字段设为private,通过getter/setter访问
  • 接口公开:公共方法提供清晰的接口
  • 工具方法:使用public static修饰工具方法
  • 继承支持:使用protected支持子类扩展
  • 包内协作:使用默认访问权限进行包内协作

避免做法

  • 过度公开:不要将所有成员都设为public
  • 公共字段:避免使用public字段(除常量外)
  • 无意义protected:不要随意使用protected
  • 访问权限混乱:保持一致的访问控制策略
  • 忽略封装:不要忽视数据隐藏的重要性
  • 权限过宽:避免给予不必要的访问权限

常见问题和注意事项

重要注意事项:

  • 构造方法访问权限:构造方法的访问权限决定了类的实例化方式
  • 静态成员访问:静态成员的访问权限影响类级别的访问
  • 内部类访问:内部类可以访问外部类的所有成员,包括private
  • 接口成员:接口中的方法默认是public,字段默认是public static final

设计建议:

  • API设计:公共API应该稳定,避免频繁修改public接口
  • 版本兼容:修改public成员可能破坏向后兼容性
  • 测试友好:考虑测试需求,适当提供包访问权限
  • 文档说明:为public成员提供详细的文档说明

章节总结