Кто-нибудь может объяснить этот код, связанный с теневым копированием в Java?

Просматривая документы Oracle, читая о вложенных классах, я нашел этот фрагмент кода, вывод которого я не мог понять. Может кто-нибудь, пожалуйста, объясните это?

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

Ниже приведен вывод этого примера:

x = 23
this.x = 1
ShadowTest.this.x = 0 //why is 0 printed here? why not 1 because "this" is the object of FirstLevel class.

Оригинальный код можно найти здесь

2 ответа

Решение

Локальная переменная x тени this.x а также ShadowTest.this.x,

Переменная экземпляра внутреннего класса (this.x) скрывает переменную экземпляра окружающего класса (к которой можно получить доступ ShadowTest.this.x).

System.out.println("x = " + x); // prints the local variable passed to the method
System.out.println("this.x = " + this.x); // prints the instance variable of the inner class
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x); // prints the instance variable of the enclosing class instance

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

Я изменил название ShadowClassвнешний класс и изменил переменные с типа intпечатать String.

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

      public class NestedClasses {
    
    public String x = "outer class variable";
    
    class FirstLevel {

        public String x = "first level class variable";

        void methodInFirstLevel(String x) {
            System.out.println("methodInFirstLevel:");
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("NestedClasses.this.x = " + NestedClasses.this.x);
        }
        
        class SecondLevel {
            
            public String x = "second level class variable";

            void methodInSecondLevel(String x) {
                System.out.println("methodInSecondLevel:");
                System.out.println("x = " + x);
                System.out.println("this.x = " + this.x);
                System.out.println("NestedClasses.this.x = " + NestedClasses.this.x);
                System.out.println("FirstLevel.this.x = " + FirstLevel.this.x);
                System.out.println("NestedClasses.FirstLevel.this.x = " + NestedClasses.FirstLevel.this.x);
            }
        }
    }

    public static void main(String[] args) {

        NestedClasses st = new NestedClasses();
        NestedClasses.FirstLevel fl = st.new FirstLevel();
        NestedClasses.FirstLevel.SecondLevel sl = fl.new SecondLevel();

        fl.methodInFirstLevel("first level method arg");
        System.out.println();
        sl.methodInSecondLevel("second level method arg");
    }
}

Выход:

methodInFirstLevel:
= метод первого уровня arg
= переменная класса первого уровня
= переменная внешнего класса

methodInSecondLevel:
= аргумент метода второго уровня
this.x= переменная класса второго уровня
= переменная внешнего класса
FirstLevel.this.x= переменная класса
первого уровня = переменная класса первого уровня

Каждый из трех созданных объектов ( st, fl, sl) содержат указатель на другую область памяти в куче, где можно найти значение. Я полагаю, что компилятор ищет «назад» к самому внешнему слою, чтобы найти первый соответствующий класс, когда он видит имя класса с префиксом во вложенном классе. Первый соответствующий класс, который он находит, является классом, на который он будет ссылаться при определении экземпляра объекта, который необходимо найти для поиска. Это будет означать NestedClasses.FirstLevel.this.xна самом деле делает два шага назад и один шаг вперед, чтобы определить, на какое пространство памяти смотреть в куче, т.е. на какое пространство памяти ссылается.

Летний:
NestedClasses.this.x

  • Имя класса с префиксом NestedClassesсообщает JVM, какой класс используется, этот класс должен быть доступен из текущей области.
  • Ключевое слово сообщает JVM, что нужно смотреть на пространство памяти созданного объекта класса, на который ссылаются, а не на пространство памяти самого объекта класса (которое используется для статических членов класса). Более конкретно, будет ссылаться на экземпляр внешнего класса, который использовался (и требовался) для создания экземпляра нестатического внутреннего класса.
  • член объекта xдобавлено к thisсообщает JVM, что искать в области памяти , на которую она смотрит.
Другие вопросы по тегам