Модификаторы доступа в Java - Вопросы

Всего: 5 вопросов

1. 

Сколько в Java модификаторов доступа и сколько уровней доступа?

Модификаторов доступа триpublic, protected и private, а уровней доступа четыре. Четвёртый уровень, package-private (уровень доступа по умолчанию), не имеет собственного ключевого слова: он получается, когда модификатор не указан вообще.

Откуда виден член класса на каждом уровне:

  • public — из любого кода, который видит сам класс;
  • protected — из всего своего пакета и дополнительно из классов-наследников в других пакетах (через ссылку типа наследника);
  • package-private — только из кода в том же пакете;
  • private — только внутри тела своего класса верхнего уровня, включая его вложенные классы.

Уровни строго упорядочены по строгости: private → package-private → protectedpublic. Начиная с Java 9 поверх них работает модульная система JPMS: public-класс не виден из другого модуля, если его пакет не экспортирован директивой exports.

2. 

Что открывает модификатор protected и почему вызов other.protectedAccessMethod() внутри Child не компилируется?

package oop.p2;

import oop.p1.Parent;

public class Child extends Parent {
    public void compare(Parent other, Child sibling) {
        this.protectedAccessMethod();
        super.protectedAccessMethod();
        sibling.protectedAccessMethod();
        other.protectedAccessMethod();
    }
}

protected делает две вещи сразу: открывает член всем классам своего пакета (как package-private) и дополнительно наследникам в других пакетах. То есть он строго шире package-private, а распространённая формулировка «protected — это для наследников» неполна: класс AccessClass, лежащий рядом с Parent в пакете oop.p1, вызывает protectedAccessMethod() вообще без наследования.

Последняя строка не компилируется из-за отдельного правила спецификации языка (JLS 6.6.2.1): из наследника в другом пакете обращаться к protected-члену можно только через выражение, тип которого — сам этот наследник или его подтип. У this, super и sibling тип Child, поэтому эти три вызова корректны; other объявлен как Parent, и компилятор его отклоняет.

Смысл ограничения: наследник получает право работать со своей унаследованной частью объекта, а не со всеми объектами родительского типа в системе.

3. 

Какие уровни доступа применимы к классу верхнего уровня в Java?

Только два из четырёх: public и package-private. Модификаторы private и protected к классу верхнего уровня применить нельзя — компилятор выдаст ошибку. А вложенному классу доступны все четыре уровня, потому что он является членом внешнего класса.

Если сам класс не виден, недоступно ничего внутри него, включая public-члены. Попытка обратиться к package-private классу HotBeverage из другого пакета падает уже на строке import:

package oop.p1;

class HotBeverage {
}
package oop.p2;

// import oop.p1.HotBeverage; // Ошибка компиляции: класс не public

public class Tea {
    // extends HotBeverage - невозможно из другого пакета
}

И правило про файлы: public-класс должен быть единственным public-типом верхнего уровня в файле, а имя файла обязано совпадать с именем класса. Рядом с ним можно объявить сколько угодно package-private классов — но это независимые классы верхнего уровня, и private-члены друг друга они не видят:

// файл Beverage.java
public class Beverage {
}

class HotBeverage {
}

class ColdBeverage {
}
4. 

Можно ли при переопределении метода сузить уровень доступа?

Нет. При переопределении уровень доступа можно сохранить или расширить, но не сузить: package-private поднимается до protected или public, protected — до public. Попытка сузить даёт ошибку компиляции «attempting to assign weaker access privileges».

package oop.p1;

public class Child extends Parent {
    @Override
    public void hook() { // OK: доступ расширен с protected до public
    }

    // @Override
    // void hook() {} // Ошибка: попытка ослабить доступ
}

Причина — принцип подстановки Лисков: код, работающий с переменной типа Parent, обязан иметь возможность вызвать hook(), какой бы конкретный наследник туда ни подставили.

С private-методом история другая: он не наследуется и потому не переопределяется вообще. Метод с той же сигнатурой в наследнике — это просто новый, независимый метод, и аннотация @Override на нём вызовет ошибку компиляции.

5. 

Какие модификаторы доступа имеют члены интерфейса в Java?

В интерфейсах действуют свои правила:

  • абстрактные методы неявно public abstract, а поля — неявно public static final;
  • писать public у метода интерфейса разрешено, но избыточно и обычно считается лишним шумом. Отсутствие public не делает метод package-private — он всё равно public;
  • с Java 8 в интерфейсе можно объявлять default- и static-методы с телом (ключевое слово default здесь про реализацию по умолчанию, а не про уровень доступа);
  • с Java 9 в интерфейсе разрешены private-методы — для вынесения общего кода из default-методов.

Итог: единственный уровень доступа, который реально выбирается внутри интерфейса, — private для вспомогательного метода; всё, что входит в контракт, — public.

Страница 1 из 1