Подтвердить что ты не робот

Как переменная 'this' в Java фактически установлена ​​на текущий объект?

Рассмотрим:

class TestParent{
  public int i = 100;
  public void printName(){
    System.err.println(this); //{[email protected]} according to the Debugger.
    System.err.println(this.i); //this.i is 100.
  }
}

class TestChild extends TestParent{
  public int i = 200;
}

public class ThisTest {
  public static void main(String[] args) {
    new TestChild().printName();
  }
}

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

Позвольте мне объяснить, как я понимаю результат вышеупомянутого изображения.

  • Поскольку объект new TestChild(), вызывающий метод printName(), переменная this в строке 6 устанавливается в объект TestChild - {TestChild @428} в соответствии с отладчиком.

  • Однако, поскольку Java не имеет виртуального поля - я не совсем уверен, что это значит, но я концептуально понимаю это как противоположность Java-методам, которые поддерживают Полиморфизм - this.i до 100 из TestParent во время компиляции.

  • Итак, независимо от того, что this, this.i в методе TestParent всегда будет переменной i в классе TestParent.

Я не уверен, что мое понимание правильное, поэтому, пожалуйста, исправьте меня, если я ошибаюсь.

А также, мой главный вопрос:

Как переменная this установлена ​​на текущий объект, вызывающий метод? Как это реализовано?

4b9b3361

Ответ 1

В сущности, нет разницы между

this.foo()

и

anyObject.foo()

поскольку оба реализованы одинаково. Имейте в виду, что "в конце концов" объектная ориентация - это только абстракция, а в "реальности" происходит что-то вроде:

foo(callingObject)

Другими словами: всякий раз, когда вы используете какую-либо ссылку на объект для вызова метода... в конце концов нет объекта на. Потому что в глубине ассемблера и машинного кода чего-то вроде "вызова на что-то" не существует.

Что действительно происходит, это вызов функции; и первым (неявным/невидимым по уровню исходного кода) является этот объект.

Кстати: вы можете записать это на Java, например:

class Bar {
   void foo(Bar this) { ... }

а затем используйте

new Bar().foo();

И для this.fieldA, в конце: у вас есть ссылка на какое-то место в памяти; и таблицу, в которой говорится о том, на каком "смещении" вы найдете поле А.

Изменить - только для записи. Если вас интересует более подробная информация о foo (Bar this), вы можете обратиться к этому question; предоставляя детали в спецификации Java за ней!

Ответ 2

Что здесь происходит, так это два совершенно разных поля, называемых i; для использования их полных имен, один TestParent::i, а один - TestChild::i.

Поскольку метод printName определяется в TestParent, когда он ссылается на i, он может видеть только TestParent::i, который установлен в 100.

Если вы установили i в 200 в TestChild, оба поля, называемые i, видны, но поскольку они имеют одинаковое имя, TestChild::i скрывает TestParent::i, и вы заканчиваете установку TestChild::i и оставляете TestParent::i нетронутой.

Ответ 3

Хорошо, когда создается новый объект, у объекта есть адрес в памяти, поэтому вы можете думать об этом, как будто у объекта был закрытый член this, который задан адресом при создании объекта. Вы также можете думать об этом так: obj.method(param) - это просто синтаксический сахар для method(obj, param); и this - фактически параметр method.

Ответ 4

Чтобы напрямую обращаться к тому, что вы видите на выходе: вызов для печати "this.i" передается как аргумент "print()" значение поля "i" в текущей области действия, которое является областью родительский класс. Напротив, вызов для печати 'this' переводится под капотом на вызов для печати 'this.getClass(). GetName()' [грубо говоря], а вызов getClass() получает фактический объект класса, который предназначен для дочернего класса.

Ответ 5

Добавление дополнительной информации поверх ответа @Tom Anderson, в котором объясняется скрытая концепция.

Я добавил еще один конструктор в Child (TestChild), который печатает значения я в родительском и дочернем.

Если вы хотите получить значение i от child (TestChild), переопределите метод в TestChild.

class TestParent{
  public int i = 100;
  public void printName(){
    System.err.println("TestParent:printName()");
    System.err.println(this); //{[email protected]_NUM} according to the Debugger.
    System.err.println(this.i); //this.i is 100.
  }
}

class TestChild extends TestParent{
  public int i = 200;
  public TestChild(){
    System.out.println("TestChild.i and TestParent.i:"+this.i+":"+super.i);
  }
  public void printName(){
      //super.printName();
      System.err.println("TestChild:printName()");
      System.err.println(this); //{[email protected]_NUM} according to the Debugger.
      System.err.println(this.i); //this.i is 200.
  }
}

public class ThisTest {
  public static void main(String[] args) {
    TestParent parent = new TestChild();
    parent.printName();
  }
}

Случай 1: если я прокомментирую вызов super.printName() от дочернего, дочерняя версия TestChild.printName() печатает значение я в TestChild

Выход:

TestChild.i and TestParent.i:200:100
TestChild:printName()
[email protected]
200

Случай 2: TestChild.printName() вызывает super.printName() как первую строку в методе printName(). В этом случае значение я из родительского и дочернего элементов отображается в соответствующих методах.

Выход:

TestChild.i and TestParent.i:200:100
TestParent:printName()
[email protected]
100
TestChild:printName()
[email protected]
200