Материал предоставлен https://it.rfei.ru

Правила видимости

Java предоставляет явные модификаторы доступа для управления доступностью членов класса из внешних клиентов, но в двух случаях доступ управляется специальными правилами области видимости.

  • Область видимости класса для членов: как получить доступ к объявленным членам внутри класса.
  • Область видимости блока для локальных переменных: как получить доступ к объявленным локальным переменным внутри блока.

Область видимости класса для членов

Область видимости класса (class scope) касается доступности членов (включая унаследованные) из кода внутри класса. В табл. 4.1 дается обзор того, как из статического и нестатического кода в классе можно получить доступ к членам класса, включая унаследованные. В табл. 4.1 предполагаются следующие объявления.

class SuperName {
    int instanceVarInSuper;
    static int staticVarInSuper;

    void instanceMethodInSuper()      { /* ... */ }
    static void staticMethodInSuper() { /* ... */ }
    // ...
}

class ClassName extends SuperName {
    int instanceVar;
    static int staticVar;

    void instanceMethod()      { /* ... */ }
    static void staticMethod() { /* ... */ }
    // ...
}

Есть главный принцип о том, что из статического кода нельзя получить доступ к нестатическим членам по их простым именам. Статический код не выполняется в контексте объекта, поэтому ссылки this и super недоступны. Объект, в свою очередь, хранит информацию о своем классе, поэтому к статическим членам из нестатического контекста всегда можно получить доступ.

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

Некоторые факторы, которые могут повлиять на видимость члена:

  • затенение имени поля либо локальными переменными, либо объявлениями в подклассе;
  • инициализация, предшествующая объявлению поля;
  • переопределение метода экземпляра из суперкласса;
  • сокрытие статических членов, объявленных в суперклассе.

Таблица 4.1. Доступ к членам внутри класса

Объявления Нестатический код может ссылаться на Статический код может ссылаться на
Переменные экземпляра instanceVar
this.instanceVar Not possible
instanceVarInSuper
this.instanceVarInSuper
super.instanceVarInSuper
Методы экземпляра instanceMethod()
this.instanceMethod()
instanceMethodInSuper() Not possible
this.instanceMethodInSuper()
super.instanceMethodInSuper()
Статические переменныеstaticVar staticVar
this.staticVar
ClassName.staticVar ClassName.staticVar
staticVarInSuper staticVarInSuper
this.staticVarInSuper
super.staticVarInSuper
ClassName.staticVarInSuper ClassName.staticVarInSuper
SuperName.staticVarInSuper SuperName.staticVarInSuper
Статические методы staticMethod() staticMethod()
this.staticMethod()
ClassName.staticMethod() ClassName.staticMethod()
staticMethodInSuper() staticMethodInSuper()
this.staticMethodInSuper()
super.staticMethodInSuper()
ClassName.staticMethodInSuper()ClassName.staticMethodInSuper()
SuperName.staticMethodInSuper()SuperName.staticMethodInSuper()

Внутри класса C ссылочные переменные типа C могут использоваться для доступа ко всем членам в классе C, несмотря на их модификаторы доступа. В примере 4.6 метод duplicateLight в строке (1) в классе Light содержит параметр oldLight и локальную переменную newLight, которые ссыпаются на объект Light. Даже если поля класса имеют модификатор доступа private, они доступны по двум ссылкам (oldLight и newLight) в методе duplicateLight(), что показано в строках (2), (3), и (4).

Пример 4.6. Класс Light

class Light {
    // Instance variables
    private int     noOfWatts;       // Мощность
    private boolean indicator;       // Включено или выключено
    private String  location;        // Размещение

    // Instance methods
    public void switchOn()  { indicator = true; }
    public void switchOff() { indicator = false; }
    public boolean isOn()   { return indicator; }

    public static Light duplicateLight(Light oldLight) {       // (1)
        Light newLight = new Light();
        newLight.noOfWatts = oldLight.noOfWatts;               // (2)
        newLight.indicator = oldLight.indicator;               // (3)
        newLight.location  = oldLight.location;                // (4)
        return newLight;
   }
}

Область видимости блока для локальных переменных

Объявления и операторы могут быть объединены в блок с помощью фигурных скобок {}. Блоки могут быть вложенными, и к объявлениям локальных переменных в таких блоках применяются определенные правила. Локальное объявление может появиться в любом месте в блоке. Универсальное правило гласит, что переменная, объявленная в блоке, находится в зоне видимости блока, в котором объявлена, она недоступна извне блока. Нет возможности переобъявить переменную, если локальная переменная с таким именем уже объявлена в текущем контексте.

Локальные переменные метода — это формальные параметры метода и переменные, которые объявлены в теле метода. Локальные переменные в методе отличаются при каждом вызове и имеют свою собственную область хранения.

Тело метода является блоком. Параметры не могут быть переобъявлены в теле метода, как показано в строке (1) в блоке 1 (см. пример 4.6(б)).

Локальная переменная — переменная, объявленная в объемлющем блоке и поэтому видимая во вложенных блоках, — не может быть переопределена во вложенном блоке. Это иллюстрируется в строках (3), (5) и (6).

Локальная переменная может быть переопределена в другом блоке, если блоки разделены, т.е. они не перекрываются. Такой случай показан на примере переменной i в строке (2) в блоке 3 и в строке (4) в блоке 4, так как эти блоки разделены.

Область видимости переменной начинается с места, где она определена, и заканчивается, как только этот блок завершается. Область видимости переменной цикла index — это блок 2. Хотя блок 2 вложен в блок 1, объявление переменной index в (7) в блоке 1 допустимо. Ее видимость простирается с момента ее объявления и до конца этого блока, и она не перекрывается с переменной цикла index в блоке 2.

Пример 4.6(б). Область видимости блока

public static void main(String args[]) {  // Блок 1

    // String args = "";

    char digit = 'z';
    for (int index = 0; index < 10; ++index) { // Блок 2
        switch (digit) {                        // Блок 3
            case 'a':
                int i;                               // (2)
            default:

                // int i;                               // (3) уже объявлена в этом блоке

        } //switch

        if (true) {                            // Блок 4

            int i;                             // (4) Ok
            // int digit;                      // (5) уже объявлена в объемлющем блоке 1
            // int index;                      // (6) уже объявлена в объемлющем блоке 2
        } // if
    } // for

    int index;                                //(7) Ok

} // main
КонструкторыПакеты