«1. Обзор

В языке программирования Java поля, конструкторы, методы и классы могут быть помечены модификаторами доступа. В этом руководстве мы рассмотрим защищенный доступ.

2. Ключевое слово protected

Хотя к элементам, объявленным как private, может получить доступ только класс, в котором они объявлены, ключевое слово protected разрешает доступ из подклассов и членов того же пакета.

Используя защищенное ключевое слово, мы принимаем решения о том, какие методы и поля должны считаться внутренними компонентами пакета или иерархии классов, а какие доступны внешнему коду.

3. Объявление защищенных полей, методов и конструкторов

Во-первых, давайте создадим класс с именем FirstClass, содержащий защищенное поле, метод и конструктор:

public class FirstClass {

    protected String name;

    protected FirstClass(String name) {
        this.name = name;
    }

    protected String getName() {
        return name;
    }
}

В этом примере, используя ключевое слово protected, мы предоставили доступ к этим полям классам в том же пакете, что и FirstClass, и подклассам FirstClass.

4. Доступ к защищенным полям, методам и конструкторам

4.1 Из того же пакета

Теперь давайте посмотрим, как мы можем получить доступ к защищенным полям, создав новый GenericClass, объявленный в том же пакете, что и FirstClass:

public class GenericClass {

    public static void main(String[] args) {
        FirstClass first = new FirstClass("random name");
        System.out.println("FirstClass name is " + first.getName());
        first.name = "new name";
    }
}

Поскольку этот вызывающий класс находится в том же пакете, что и FirstClass, ему разрешено видеть и взаимодействовать со всеми защищенными полями, методами и конструкторами.

4.2. Из другого пакета

Теперь попробуем взаимодействовать с этими полями из класса, объявленного в пакете, отличном от FirstClass:

public class SecondGenericClass {

    public static void main(String[] args) {
        FirstClass first = new FirstClass("random name");
        System.out.println("FirstClass name is "+ first.getName());
        first.name = "new name";
    }
}

Как мы видим, мы получаем ошибки компиляции:

The constructor FirstClass(String) is not visible
The method getName() from the type FirstClass is not visible
The field FirstClass.name is not visible

Вот именно то, что мы ожидали, используя защищенное ключевое слово. Это связано с тем, что SecondGenericClass не находится в том же пакете, что и FirstClass, и не является его подклассом.

4.3 Из подкласса

Давайте теперь посмотрим, что происходит, когда мы объявляем класс, расширяющий FirstClass, но объявленный в другом пакете:

public class SecondClass extends FirstClass {
    
    public SecondClass(String name) {
        super(name);
        System.out.println("SecondClass name is " + this.getName());
        this.name = "new name";
    } 
}

Как и ожидалось, мы можем получить доступ ко всем защищенным полям, методам, и конструкторы. Это связано с тем, что SecondClass является подклассом FirstClass.

5. protected Внутренний класс

В предыдущих примерах мы видели защищенные поля, методы и конструкторы в действии. Есть еще один частный случай — защищенный внутренний класс.

Давайте создадим этот пустой внутренний класс внутри нашего FirstClass:

package com.baeldung.core.modifiers;

public class FirstClass {

    // ...

    protected static class InnerClass {

    }
}

Как мы видим, это статический внутренний класс, и поэтому его можно сконструировать вне экземпляра FirstClass. Однако, поскольку он защищен, мы можем создать его экземпляр только из кода в том же пакете, что и FirstClass.

5.1 Из того же пакета

Чтобы проверить это, давайте отредактируем наш GenericClass:

public class GenericClass {

    public static void main(String[] args) {
        // ...
        FirstClass.InnerClass innerClass = new FirstClass.InnerClass();
    }
}

Как мы видим, мы можем без проблем создать экземпляр InnerClass, поскольку GenericClass находится в том же пакете, что и FirstClass.

5.2. Из другого пакета

Давайте попробуем создать экземпляр InnerClass из нашего SecondGenericClass, который, как мы помним, находится вне пакета FirstClass:

public class SecondGenericClass {

    public static void main(String[] args) {
        // ...

        FirstClass.InnerClass innerClass = new FirstClass.InnerClass();
    }
}

Как и ожидалось, мы получим ошибку компиляции:

The type FirstClass.InnerClass is not visible

5.3 . Из подкласса

Давайте попробуем сделать то же самое из нашего второго класса:

public class SecondClass extends FirstClass {
    
    public SecondClass(String name) {
        // ...
 
        FirstClass.InnerClass innerClass = new FirstClass.InnerClass();
    }     
}

Мы ожидали, что наш внутренний класс будет легко создан. Однако и здесь мы получаем ошибку компиляции:

The constructor FirstClass.InnerClass() is not visible

Давайте посмотрим на наше объявление InnerClass:

protected static class InnerClass {
}

Основная причина, по которой мы получаем эту ошибку, заключается в том, что конструктор по умолчанию для защищенного класса неявно защищены. Кроме того, SecondClass является подклассом FirstClass, но не является подклассом InnerClass. Наконец, мы также объявили SecondClass вне пакета FirstClass.

По всем этим причинам SecondClass не может получить доступ к защищенному конструктору InnerClass.

Если бы мы хотели решить эту проблему и позволить нашему SecondClass создавать экземпляр объекта InnerClass, мы могли бы явно объявить общедоступный конструктор:

protected static class InnerClass {
    public InnerClass() {
    }
}

Делая это, мы больше не получаем ошибку компиляции, и теперь мы можем создать экземпляр InnerClass из SecondClass.

6. Заключение

В этом кратком руководстве мы обсудили защищенный модификатор доступа в Java. С его помощью мы можем обеспечить предоставление только необходимых данных и методов подклассам и классам в одном пакете.

«Как всегда, пример кода доступен на GitHub.