Java 访问修饰符

更新于 2025-12-25

Jakob Jenkov 2018-09-16

Java 访问修饰符(Access Modifier)用于指定哪些类可以访问某个给定的类、字段、构造方法或普通方法。你可以分别为类、构造方法、字段和方法单独指定访问修饰符。

在日常交流中,Java 访问修饰符有时也被称为 Java 访问说明符(access specifiers),但正确的术语是 Java 访问修饰符

类、字段、构造方法和方法可以使用以下四种 Java 访问修饰符之一:

  • private
  • default(包级私有)
  • protected
  • public

下表总结了每种 Java 访问修饰符可应用于哪些 Java 构造元素:

private default protected public
类(Class)
嵌套类(Nested Class)
构造方法(Constructor)
方法(Method)
字段(Field)

为类、构造方法、字段或方法分配访问修饰符,有时也被称为“将该元素标记为”某种访问级别。例如,将一个方法标记为 public,就是指为其分配了 public 访问修饰符。


private 访问修饰符

如果一个方法或变量被标记为 private(即为其分配了 private 访问修饰符),那么只有同一个类内部的代码才能访问该变量或调用该方法。子类中的代码无法访问,外部类中的代码也无法访问

类不能被标记为 private。因为如果一个类是 private 的,那么其他任何类都无法访问它,也就无法真正使用这个类。因此,Java 不允许对顶层类使用 private 修饰符。

下面是一个将字段标记为 private 的示例:

public class Clock {
    private long time = 0;
}

在这个例子中,Clock 类中的成员变量 time 被标记为 private,这意味着在 Clock 类之外的代码无法直接访问 time


通过访问器方法访问 private 字段

字段经常被声明为 private,以控制外部对其的访问。在某些情况下,这些字段确实是完全私有的,仅在类内部使用;而在其他情况下,可以通过访问器方法(如 getter 和 setter)来间接访问这些字段。

下面是一个访问器方法的示例:

public class Clock {
    private long time = 0;

    public long getTime() {
        return this.time;
    }

    public void setTime(long theTime) {
        this.time = theTime;
    }
}

在上面的例子中,getTime()setTime() 方法可以访问 time 成员变量。这两个方法被声明为 public,意味着应用程序中的任何地方都可以调用它们。


private 构造方法

如果一个类中的构造方法被标记为 private,则表示该构造方法不能从类外部调用。不过,它仍然可以从同一个类中的其他构造方法静态方法中调用。

下面是一个示例:

public class Clock {
    private long time = 0;

    private Clock(long time) {
        this.time = time;
    }

    public Clock(long time, long timeOffset) {
        this(time); // 调用 private 构造方法
        this.time += timeOffset;
    }

    public static Clock newClock() {
        return new Clock(System.currentTimeMillis());
    }
}

此版本的 Clock 类包含一个 private 构造方法和一个 public 构造方法。private 构造方法既可以从 public 构造方法中调用(通过 this(time)),也可以从静态方法 newClock() 中调用。

注意:上述示例仅用于说明 private 构造方法的调用方式,并不代表一种“优秀设计”。


default(包级私有)访问修饰符

默认访问修饰符是指不显式写出任何访问修饰符。此时,该类/字段/构造方法/方法可以被同一类内部以及同一包中的其他类访问。因此,默认访问修饰符有时也被称为 包级私有(package-private)访问修饰符。

如果你还不了解 Java 包(package)的概念,可以参考 Java Packages 教程

需要注意的是:即使子类继承了父类,只要子类不在同一个包中,就无法访问父类中使用默认访问修饰符的字段或方法

下面是一个默认访问修饰符的示例:

// Clock.java
public class Clock {
    long time = 0; // 默认(包级私有)访问
}

// ClockReader.java(必须与 Clock 在同一包中)
public class ClockReader {
    Clock clock = new Clock();

    public long readClock() {
        return clock.time; // 可以访问,因为同包
    }
}

Clock 类中的 time 字段没有访问修饰符,因此它是默认(包级私有)的。只要 ClockReaderClock 在同一个 Java 包中,ClockReader 就可以读取 clock.time


protected 访问修饰符

protected 访问修饰符提供的访问权限与默认访问修饰符相同(即同包内可访问),此外还允许子类访问父类中被标记为 protected 的字段和方法,即使子类位于不同的包中。

示例:

// Clock.java
public class Clock {
    protected long time = 0; // 时间(毫秒)
}

// SmartClock.java(可在不同包中)
public class SmartClock extends Clock {
    public long getTimeInSeconds() {
        return this.time / 1000; // 子类可以访问 protected 字段
    }
}

即使 SmartClockClock 不在同一个包中,SmartClock 仍能访问父类的 time 字段,因为它是 protected 的。


public 访问修饰符

public 访问修饰符表示所有代码都可以访问该类、字段、构造方法或方法,无论访问代码位于哪个类或哪个包中。

示例:

// Clock.java
public class Clock {
    public long time = 0;
}

// ClockReader.java(可在任意包中)
public class ClockReader {
    Clock clock = new Clock();

    public long readClock() {
        return clock.time; // 任何地方都能访问
    }
}

由于 time 字段是 public 的,因此无论 ClockReader 在哪个包中,都可以访问它。


类的访问修饰符

需要特别注意:类本身的访问修饰符优先级高于其内部成员(字段、方法、构造方法)的访问修饰符

例如,如果一个类被标记为默认(包级私有)访问修饰符,那么包外的任何类都无法访问该类,包括其 public 字段、public static 方法等——因为连类本身都不可见。

另外,顶层类(top-level class)只能使用两种访问修饰符:

  • 默认(包级私有)
  • public

不能对顶层类使用 privateprotected

注:嵌套类(nested class)可以使用所有四种访问修饰符。


接口的访问修饰符

Java 接口旨在为实现类提供公开可用的字段和方法规范。因此:

  • 接口中不能使用 privateprotected 访问修饰符。
  • 接口中的字段和方法默认就是 public,即使你不写 public
  • 因此,接口中也不能使用默认(包级私有)访问修饰符。

换句话说,接口中的所有成员都是隐式 public 的。


访问修饰符与继承

当你创建一个子类并重写(override)父类的方法时,子类中重写的方法不能比父类中的原方法具有更严格的访问权限

规则如下:

  • 如果父类方法是 public,子类重写方法必须也是 public
  • 如果父类方法是 protected,子类重写方法可以是 protectedpublic
  • 如果父类方法是默认(包级私有),子类重写方法可以是默认、protectedpublic

✅ 允许:扩大访问权限(例如从默认变为 public
❌ 不允许:缩小访问权限(例如从 public 变为 protected