Java 访问修饰符:public、protected、private讲解

文章目录

    • 前言
    • 一、四种访问级别总览
      • [1.1 一张表总结](#1.1 一张表总结)
      • [1.2 可见性矩阵](#1.2 可见性矩阵)
    • [二、`private` :封装的基石](#二、private :封装的基石)
      • [2.1 核心作用](#2.1 核心作用)
      • [2.2 `private` 方法的用途](#2.2 private 方法的用途)
      • [2.3 `private` 用于构造方法](#2.3 private 用于构造方法)
      • [2.4 测试代码](#2.4 测试代码)
    • [三、`public` :对外暴露的接口](#三、public :对外暴露的接口)
      • [3.1 核心作用](#3.1 核心作用)
      • [3.2 `public` 类与非 `public` 类](#3.2 public 类与非 public 类)
      • [3.3 测试代码](#3.3 测试代码)
    • [四、`protected` :继承体系专用](#四、protected :继承体系专用)
      • [4.1 核心作用](#4.1 核心作用)
      • [4.2 `protected` 的一个重要细节](#4.2 protected 的一个重要细节)
      • [4.3 测试代码](#4.3 测试代码)
    • 五、默认访问级别(包私有)
      • [5.1 核心作用](#5.1 核心作用)
    • 总结

前言

Java 提供的四种访问修饰符(private默认(包私有)protectedpublic)。


一、四种访问级别总览

1.1 一张表总结

Java 共有四种访问级别,其中三种有关键字,一种是默认(不写任何修饰符):

访问级别 关键字 可访问范围
私有 private 仅本类内部
默认(包私有) (不写) 本类 + 同包的类
受保护 protected 本类 + 同包的类 + 子类(跨包也可)
公开 public 任何地方

1.2 可见性矩阵

访问位置 private 默认 protected public
本类
同包其他类
不同包子类
不同包无关类

二、private :封装的基石

2.1 核心作用

private 是封装的直接体现:把实现细节藏起来,只暴露必要的接口

java 复制代码
public class BankAccount {

    private double balance;  // 余额不能被外部直接修改

    // 通过方法控制修改逻辑,可以加校验
    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("存款金额必须大于0");
        this.balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > balance) throw new IllegalArgumentException("余额不足");
        this.balance -= amount;
    }

    public double getBalance() {
        return balance;
    }
}

如果 balancepublic,任何地方都能 account.balance = -9999,安全性差。

2.2 private 方法的用途

private 方法通常是内部的工具方法,不对外暴露:

java 复制代码
public class PasswordUtils {

    public String encrypt(String rawPassword) {
        String salted = addSalt(rawPassword);   // 调用私有方法
        return hash(salted);
    }

    private String addSalt(String password) {   // 加盐逻辑,外部不需要知道
        return password + "SALT_2025";
    }

    private String hash(String input) {         // 哈希逻辑,外部不需要知道
        return Integer.toHexString(input.hashCode());
    }
}

2.3 private 用于构造方法

private 构造方法用于阻止外部直接实例化,常见于:

  • 单例模式:控制只有一个实例
  • 工具类:不需要实例化,只有静态方法
  • 工厂方法模式:强制通过工厂方法创建对象
java 复制代码
// 工具类:禁止实例化
public class MathUtils {
    private MathUtils() {}  // 私有构造,防止 new MathUtils()

    public static int add(int a, int b) { return a + b; }
}

// 单例模式
public class ConfigManager {
    private static final ConfigManager INSTANCE = new ConfigManager();

    private ConfigManager() {}  // 私有构造

    public static ConfigManager getInstance() { return INSTANCE; }
}

2.4 测试代码

java 复制代码
public class PrivateTest {

    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(1000);
        account.withdraw(200);
        System.out.println("余额:" + account.getBalance()); // 800.0

        // account.balance = -9999; // 编译错误:balance has private access

        try {
            account.withdraw(9999);
        } catch (IllegalArgumentException e) {
            System.out.println("异常捕获:" + e.getMessage()); // 余额不足
        }

        // 工具类无法实例化(编译期报错)
        // MathUtils utils = new MathUtils(); // 编译错误
        System.out.println(MathUtils.add(3, 4)); // 7

        // 单例
        ConfigManager c1 = ConfigManager.getInstance();
        ConfigManager c2 = ConfigManager.getInstance();
        System.out.println(c1 == c2); // true,同一个实例
    }
}

三、public :对外暴露的接口

3.1 核心作用

public 意味着承诺:这个方法或类对所有调用方开放,一旦对外公开,就要保证向后兼容,轻易不能改变签名。

java 复制代码
// 这是一个公开的 API,改动签名会影响所有调用方
public interface PaymentService {
    public PayResult pay(String orderId, double amount);
}

3.2 public 类与非 public

一个 .java 文件中只能有一个 public 类,且类名必须与文件名一致。同文件中的其他类只能是默认(包私有)访问级别:

java 复制代码
// 文件名必须是 Order.java
public class Order {          // public 类,对外暴露
    private OrderItem item;   // 使用同文件的非 public 类
}

class OrderItem {             // 默认访问级别,包私有,外部模块不可见
    String productName;
    int quantity;
}

OrderItem 只是 Order 的实现细节,不需要对外暴露,用默认访问级别把它藏在包内部。

3.3 测试代码

java 复制代码
// 文件:PublicTest.java
public class PublicTest {

    public static void main(String[] args) {
        Order order = new Order();
        // OrderItem item = new OrderItem(); // 同包内可以,跨包则编译错误
    }
}

四、protected :继承体系专用

4.1 核心作用

protected 是三个修饰符中最容易被误解的一个。它的设计目的是:允许子类访问父类的实现细节,同时对外部无关类保持关闭

java 复制代码
// 父类(假设在 com.animal 包)
public abstract class Animal {

    private String name;          // 子类也不能直接访问

    protected int energy;         // 子类可以直接访问,外部无关类不能访问

    public Animal(String name) {
        this.name = name;
        this.energy = 100;
    }

    protected void breathe() {    // 子类可以调用或重写,外部无法直接调用
        energy -= 1;
        System.out.println(name + " 呼吸,消耗能量");
    }

    public abstract void move();  // 公开抽象方法,子类必须实现
}

// 子类(在 com.animal.impl 包,跨包继承)
public class Dog extends Animal {

    public Dog(String name) {
        super(name);
    }

    @Override
    public void move() {
        breathe();          // ✓ 可以调用父类 protected 方法
        energy -= 5;        // ✓ 可以直接访问父类 protected 字段
        System.out.println("狗在跑,剩余能量:" + energy);
    }
}

4.2 protected 的一个重要细节

跨包的子类只能通过自身引用 访问父类的 protected 成员,不能通过父类引用访问:

java 复制代码
// 假设 Dog 和 Animal 不在同一个包
public class Dog extends Animal {

    public void test(Animal otherAnimal) {
        this.energy = 50;           // ✓ 通过自身引用访问,合法
        // otherAnimal.energy = 50; // ✗ 编译错误!不能通过父类引用访问
    }
}

这个细节很多人不知道,面试中也经常考察。原因是:protected 的语义是"子类对自己继承来的部分有访问权",而不是"子类对所有 Animal 对象都有访问权"。

4.3 测试代码

java 复制代码
// com.animal.Animal.java
package com.animal;

public abstract class Animal {
    protected int energy = 100;
    protected void breathe() { energy--; }
    public abstract void move();
}

// com.animal.impl.Dog.java
package com.animal.impl;

import com.animal.Animal;

public class Dog extends Animal {
    @Override
    public void move() {
        breathe();        // ✓ 跨包子类调用父类 protected 方法
        energy -= 5;      // ✓ 跨包子类访问父类 protected 字段
        System.out.println("剩余能量:" + energy);
    }

    public void test(Animal other) {
        this.energy = 80; // ✓ 通过 this
        // other.energy = 80; // ✗ 编译错误
    }
}

// 测试入口
public class ProtectedTest {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.move();  // 剩余能量:94

        // dog.energy = 999;  // ✗ 不同包的无关类,编译错误
        // dog.breathe();     // ✗ 不同包的无关类,编译错误
    }
}

五、默认访问级别(包私有)

5.1 核心作用

不写任何修饰符,就是包私有(package-private)

它的语义是:这个类/方法只属于当前包的内部实现,不对包外暴露

这是模块内聚的重要工具,把一个功能模块的内部实现类全部设为包私有,对外只暴露 public 的接口类。

java 复制代码
// com.payment 包内部

// 对外暴露的接口
public interface PaymentProcessor {
    PayResult process(Order order);
}

// 包内部的实现类,外部模块不可见
class AlipayClient {          // 包私有,支付宝底层客户端
    String call(String url) { return "ok"; }
}

class SignatureUtils {        // 包私有,签名工具
    static String sign(String data) { return data + "_signed"; }
}

// 对外暴露的实现
public class AlipayProcessor implements PaymentProcessor {
    private AlipayClient client = new AlipayClient();  // 内部使用

    @Override
    public PayResult process(Order order) {
        String signed = SignatureUtils.sign(order.toString());
        client.call(signed);
        return new PayResult(true);
    }
}

外部模块只能看到 PaymentProcessorAlipayProcessorAlipayClientSignatureUtils 完全不可见,随时可以重构而不影响外部。


总结

访问修饰符是 Java 封装性的基础工具,用一句话总结每种级别的设计意图:

修饰符 设计意图
private 这是我的实现细节,跟你无关
默认 这是我们包内部的事,外人别插手
protected 这是给子类预留的扩展点,无关类别碰
public 这是我对外的承诺,随时可以调用
相关推荐
刀法如飞1 小时前
JavaScript 数组去重的 20 种实现方式,学会用不同思路解决问题
前端·javascript·算法
Ting-yu1 小时前
SpringCloud快速入门(5)---- 均衡负载
java·spring·spring cloud
学不思则罔1 小时前
ParallelStream并发陷阱解析
java·开发语言·windows
认真的小羽❅1 小时前
【Java并发编程】volatile关键字深度解析:从内存语义到实际应用
java·开发语言
jayson.h2 小时前
可视化界面
开发语言·python
奋斗的小乌龟2 小时前
langchain4j笔记-08
java·spring boot·笔记
kgduu2 小时前
python中的魔法方法
开发语言·python
leonidZhao2 小时前
Java25新特性:加密对象的PEM编码
java