文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

Java 四个访问修饰符(Access Modifiers):private、default(包级私有)、protected 和 public

核心区别与异同(总览)

修饰符当前类同包类不同包子类不同包非子类主要用途
private隐藏实现细节,仅供类内部使用。
默认包内可见,实现包级别的封装。
protected允许类自身、同包类、以及所有子类(无论是否同包)访问。
public公开接口,任何地方都可访问。

关键异同点:

  1. 可见性范围: 从上表清晰可见,public 范围最广,private 范围最窄。protected 介于 默认public 之间,特别之处在于它向所有子类开放了访问权限(即使子类在不同包)。
  2. 封装性:
    • private 提供了最强的封装,完全隐藏内部实现。
    • 默认 提供了包级别的封装。
    • protected 在封装的同时,为继承体系提供了扩展点。
    • public 几乎不提供封装,暴露接口。
  3. 设计原则: 遵循“最小权限原则”。优先使用最严格的访问级别(通常是 private),只在确实需要更大范围访问时才放宽限制(如 protected 用于继承,public 用于 API)。

详细解释与源码示例

1. private

  • 可见性: 仅在声明它的类内部可见。同一个类的不同实例之间可以互相访问对方的 private 成员。
  • 原理: 编译器在编译时会检查访问权限。如果外部代码(其他类)尝试访问一个 private 成员,编译器会直接报错。
  • 主要用途:
    • 隐藏类的内部状态(字段)。
    • 隐藏仅供类内部使用的辅助方法。
    • 强制通过公共方法(如 getter/setter)访问或修改数据,实现数据封装和验证。
  • 源码示例:
public class BankAccount {
    // private 字段:外部无法直接访问余额
    private double balance;

    // public 方法:外部可以通过此方法存款(封装了验证逻辑)
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    // public 方法:外部可以通过此方法查询余额
    public double getBalance() {
        return balance;
    }

    // private 方法:仅供类内部使用,例如计算利息
    private double calculateInterest() {
        return balance * 0.05; // 假设 5% 利息
    }

    // 类内部的一个方法可以调用 private 方法
    public void applyInterest() {
        balance += calculateInterest();
    }
}

// 另一个类中
public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(1000);
        System.out.println(account.getBalance()); // 正确:通过 public 方法访问

        // account.balance = 2000; // 编译错误!balance 是 private
        // double interest = account.calculateInterest(); // 编译错误!calculateInterest 是 private
    }
}

2. 默认 (Default / Package-Private)

  • 可见性:同一个包(package)内的所有类中可见。没有显式指定任何访问修饰符时,就是默认访问级别。
  • 原理: 编译器检查访问是否发生在同一个包内。
  • 主要用途:
    • 实现包级别的封装。包内的类可以相互协作,但对包外的类隐藏实现细节。
    • 当某个成员主要供同一个包内的其他类使用,且不需要被子类(尤其是不同包的子类)直接访问时使用。
  • 源码示例:
// 文件: com/example/util/Logger.java
package com.example.util;

class Logger { // 注意:没有 public, private, protected -> 默认访问级别
    void log(String message) {
        System.out.println("[LOG] " + message);
    }
}

// 文件: com/example/util/Service.java (同一个包 com.example.util)
package com.example.util;

public class Service {
    public void doSomething() {
        Logger logger = new Logger(); // 可以访问,同包
        logger.log("Service is doing something");
    }
}

// 文件: com/example/app/Main.java (不同包 com.example.app)
package com.example.app;

import com.example.util.Service;

public class Main {
    public static void main(String[] args) {
        Service service = new Service();
        service.doSomething(); // 可以访问 Service 的 public 方法

        // Logger logger = new Logger(); // 编译错误!Logger 是默认访问,不同包不可见
        // logger.log("Hello"); // 即使能创建实例(实际上不能),log 方法也不可见
    }
}

3. protected

  • 可见性:
    • 声明它的类内部可见。
    • 同一个包内的所有类中可见(与默认访问相同)。
    • 不同包中,只有声明类的子类中可以访问(通过继承)。
    • 重要: 在子类中访问父类的 protected 成员时,该访问必须通过子类自身或子类的对象来进行(或者通过子类内部访问),不能直接通过父类的对象(如果父类对象在另一个包中)。
  • 原理: 编译器检查访问是否发生在同一个包内,或者访问者是否是声明类的子类(无论是否同包)。
  • 主要用途:
    • 允许子类访问父类的特定成员(字段或方法),以便子类可以扩展或修改父类的行为。这是实现继承和多态的关键机制之一。
    • 在同一个包内,它的效果等同于 默认 访问。
  • 源码示例:
// 文件: com/example/shape/Shape.java
package com.example.shape;

public class Shape {
    protected int x, y; // protected 字段,子类(无论是否同包)和同包类可访问

    protected void draw() { // protected 方法
        System.out.println("Drawing shape at (" + x + ", " + y + ")");
    }
}

// 文件: com/example/shape/Circle.java (同一个包 com.example.shape)
package com.example.shape;

public class Circle extends Shape {
    private int radius;

    @Override
    public void draw() {
        super.draw(); // 可以访问父类 (Shape) 的 protected draw() 方法 (同包)
        System.out.println("Drawing circle with radius " + radius + " at (" + x + ", " + y + ")"); // 可以直接访问父类 protected 字段 x, y (同包子类)
    }
}

// 文件: com/example/app/GraphicApp.java (不同包 com.example.app)
package com.example.app;

import com.example.shape.Shape;

public class GraphicApp {
    public void render(Shape shape) {
        // shape.x = 10; // 编译错误!在不同包的非子类中不能直接访问 protected 字段
        // shape.draw(); // 编译错误!在不同包的非子类中不能直接访问 protected 方法
        shape.somePublicMethod(); // 如果 Shape 有 public 方法,可以调用
    }
}

// 文件: com/example/app/MyCircle.java (不同包 com.example.app,是 Shape 的子类)
package com.example.app;

import com.example.shape.Shape;

public class MyCircle extends Shape {
    public void drawMyCircle() {
        x = 5; // 可以访问:不同包子类访问继承来的 protected 字段 (通过自身/this)
        y = 10;
        draw(); // 可以访问:不同包子类访问继承来的 protected 方法 (通过自身/this)

        Shape parentShape = new Shape();
        // parentShape.x = 15; // 编译错误!不能通过父类引用(在另一个包)访问 protected 成员
        // parentShape.draw(); // 编译错误!同上
    }
}

4. public

  • 可见性: 在任何地方(任何包中的任何类)都可以访问。
  • 原理: 编译器不做访问限制检查(除了类本身必须是 public 才能在包外被访问)。
  • 主要用途:
    • 定义类或接口的公共 API(应用程序编程接口)。
    • 暴露那些需要被程序任何部分使用的常量、方法或构造函数。
    • 声明可被任何代码实例化的类。
  • 源码示例:
// 文件: com/example/util/MathUtils.java
package com.example.util;

public class MathUtils { // public 类,任何地方可访问
    public static final double PI = 3.14159; // public 常量

    public static int add(int a, int b) { // public 静态方法
        return a + b;
    }
}

// 文件: com/example/app/Main.java (不同包 com.example.app)
package com.example.app;

import com.example.util.MathUtils;

public class Main {
    public static void main(String[] args) {
        double circleArea = MathUtils.PI * 5 * 5; // 访问 public 常量
        int sum = MathUtils.add(10, 20); // 调用 public 静态方法
        System.out.println("Area: " + circleArea + ", Sum: " + sum);
    }
}

总结与最佳实践

  1. private 你的秘密。只在类内部使用。用于隐藏实现细节,保护数据(通过 getter/setter 控制访问)。
  2. 默认 (无修饰符): 你的家庭(包)秘密。同一个包内的类可以共享。用于包内部的协作实现,对外部包隐藏。
  3. protected 你的家庭(包)秘密 + 告诉你的孩子(子类)。允许同包类和所有子类访问。用于设计可扩展的类库,为子类提供特定的扩展点。
  4. public 向全世界广播。定义你的公共合约(API)。任何代码都可以使用。

最佳实践:

  • 优先使用 private 尽可能将字段声明为 private,通过公共方法提供访问(如果需要)。
  • 谨慎使用 public 只将真正构成公共 API 的部分设为 public。过多的 public 会破坏封装,增加维护难度。
  • 善用 protected 支持继承: 当设计类时,如果明确希望某些成员被子类使用(即使在不同包),使用 protected
  • 默认 用于包内协作: 当一些类只在同一个包内紧密协作时,使用默认访问级别。
  • 类的访问修饰符: 一个 .java 文件中只能有一个 public 类,且文件名必须与该 public 类名一致。其他类只能是 默认 访问级别(同一个包内可见)。顶级类不能是 privateprotected(但嵌套类可以)。

理解并正确应用这些访问修饰符是编写健壮、可维护、封装良好的 Java 代码的基础。

posted @ 2025-09-23 10:22  NeoLshu  阅读(11)  评论(0)    收藏  举报  来源