Java 访问修饰符详解

419 阅读7分钟

类访问修饰

在 类(class) 上可以使用的访问修饰符有 publicprotected默认(什么都不写)private

  • 其中 privateprotected 不能在普通类中写,只能在内部类中写(内部类中 4 种都可以写)
  • 普通的类访问修饰符只有 public默认(什么都不写) 两种

注意:

  • 如果一个类的访问修饰符是 public ,那么 该类可以被任何其他类访问和继承。不受访问限制。其他类可以直接使用这个类创建对象,也可以继承这个类,并可以访问其中的公有方法和属性。
  • 如果一个类的访问修饰符是 默认(啥都不写) ,该类的访问范围限定在定义该类的同一个包内。只有位于同一包下的其他类,才可以直接访问和使用这个类,包括访问其中公有成员和继承这个类。

为什么普通类的类访问修饰符不能是 private ?

答:如果一个类为 `private``, 则在其他文件或类中就无法访问和使用这个类,包括类中的成员变量和成员方法,也无法继承它,这明显不合理,都不能用它写个什么劲呐,哈哈。

为什么普通类的类访问修饰符不能是 protected ?

参考:stackoverflow.com/questions/3…

总结来说是因为没用,因为我们想像如果类修饰符可以为 protected,那么无非是说除了本包的类可以访问这个类以外,在其他包的这个类的子类也可以访问它。 我们仔细看这句话:“其他包的这个类的子类可以访问它”, 我得是你的子类才能访问你,但是我想成为你的子类得先能访问你呀,这不死锁了嘛,所以不合理。既然不合理就不用了嘛,所以对于普通类功能上就只有包内访问和包外访问两种,那么 publicdefault 就可以搞定了,所以就只剩下这两种了。当然这也是因为 java 没有“子包”或“包继承”这样的概念,否则protected可能就有用了。

为什么对于内部类可以用全部 4 种类访问修饰符 (private、default、protected、public)?

说白了,还是看有用没用,没用就不需要那么多了,比如前面提到的普通类的类访问修饰符只有 2 个。我们来看看有什么用。

  • 对于 private,如果用它修饰内部类,那么表示仅在外部类内部使用,对外部类以外的类隐藏实现细节,还是比较有用的。总结来说,使用 private 修饰内部类通常用于以下目的:
    • 封装实现细节:将内部类的实现细节隐藏在外部类内部,防止其他类直接访问和依赖内部类的实现细节
    • 实现辅助类:将内部类作为外部类的辅助类,只在外部类内部使用。
  • 对于public,如果用它修饰内部类,意味着该内部类对外部类以外的其他类是可见的,并且可以被其他类直接访问。
    • 具体来说,使用 public 修饰内部类会产生以下影响:
      • 其他类可以直接访问该内部类:其他类可以通过外部类和内部类的名称直接访问该内部类,包括创建内部类的实例、调用内部类的方法和访问内部类的成员变量
      • 内部类的可见性扩展到外部类以外:内部类的可见性不再限于外部类内部,而是对外部类以外的其他类开放。
    • 使用 public 修饰内部类通常用于以下目的:
      • 使内部类可以被其他类直接使用:当内部类具有独立的功能或需要与外部类以外的代码进行交互时,可以使用 public 修饰内部类,以便其他类可以直接访问和使用该内部类。
  • 对于 default ,如果用它修饰内部类(即没有显式地使用任何访问修饰符),那么该内部类将具有默认访问级别。这意味着内部类对于外部类的其他成员和同一包中的其他类是可见的,但对于外部类所在包之外的类是不可见的。使用默认访问修饰符的内部类通常用于以下目的:
    • 封装实现细节:将内部类的实现细节隐藏在外部类内部和同一包中的其他类之间,防止外部包中的类直接访问和依赖内部类的实现细节。
    • 限制访问范围:将内部类限制在外部类和同一包中,以控制内部类的可见性和使用范围。
  • 对于 protected,如果用它修饰内部类, 那么被 protected 修饰的内部类对于外部类和外部类的子类是可见的,子类可以直接访问该内部类,并创建其实例、调用其方法以及访问其成员变量。另外同一包中的其他类也可以访问该内部类。

你看 内部类可以用 protected ,是因为人家有外部类,可以明确父子类继承关系,但普通类就不行(原因上文说过)。另外的 3 种访问修饰符也是各有各的用途,所以内部类可以使用 全部 4 种访问修饰符。

成员变量和成员方法访问修饰

成员变量和成员方法的访问修饰符和内部类一样都是可以使用全部的 4 个

它的可访问性也如本文开头的那个图描述的,从低到高依次是 privatedefaultprotectedpublic

其实没啥好说的,跟类的大致意思一样,但有 2 个点需要注意一下:

第一个是 private,如果类的构造方法用 private 修饰,那么这个类无法继承,也无法直接创建类对象,需要使用间接的方法,如单例模式

另一个是 protected ,在方法调用上 protected 就更能体现出它的 “保护” 意味了。比如

  • 不同包下,在子类中通过父类引用不可以访问其 protected 方法
public class Parent {

    protected String protect = "protect field";

    protected void getMessage(){
        System.out.println("i am parent");
    }
}

public class Son1 extends Parent{
    public static void main(String[] args) {
        Parent parent1 = new Parent();
        // parent1.getMessage();   错误

        Parent parent2 = new Son1();
        // parent2.getMessage();  错误
    }
}

  • 不同包下,在子类中通过该子类引用可以访问其 protected 方法,也可以在子类方法中直接调用, 还可以通过 super 关键字调用父类中的该方法
  • 不同包下,在子类中不能通过另一个子类引用访问共同基类的 protected 方法

“保护” 的作用在方法调用时体现的比较明显,另外还可以在一定程度上保证继承体系的稳定,不被随意破坏。合理地利用 protected 可以让代码具有良好的封装和可扩展性。下次你在某框架中再看到 protected 时应该知道它为什么用这个来修饰了。比如 Spring 框架中的AbstractApplicationContext 类,有个 prepareRefresh() 方法:

  • prepareRefresh()方法使用protected修饰符来限制其访问范围。这是因为prepareRefresh()方法是一个在应用程序上下文刷新之前执行的关键步骤,它包含了一些框架内部的初始化逻辑和准备工作。
  • 使用protected修饰符可以将prepareRefresh()方法限制在框架内部和继承体系中可见,防止外部代码直接调用该方法。这样的设计可以确保框架在进行刷新之前能够按照预期的顺序执行必要的初始化操作,同时避免外部代码干扰或绕过这些操作。
  • 此外,将prepareRefresh()方法标记为protected还为子类提供了一种扩展和定制的机制。子类可以通过继承AbstractApplicationContext并重写prepareRefresh()方法,以添加或修改特定的初始化逻辑,以满足其特定需求。

参考