2015-04-06 4 views
13

После изучения Java на некоторое время его использование ключевого слова меня так сильно смутило.«this» ключевое слово: рабочий механизм в Java

Вот как я запутался. Я написал следующий код:

class BasicInheritanceTest3Base{ 
    private int x = 0; 
    public int y; 

    public void a() { 
     x++; 
     this.x++; 
     System.out.println("BasicInheritanceTest3Base.a()"); 
     b(); 
     this.b(); 
     System.out.println(x); 
     System.out.println(y); 
    } 

    public void b(){ 
     System.out.println("BasicInheritanceTest3Base.b()"); 
    } 
} 

public class BasicInheritanceTest3 extends BasicInheritanceTest3Base { 
    private int x = 3; 
    public int y = 2; 

    public void b() { 
     System.out.println("BasicInheritanceTest3.b()"); 
    } 

    public static void main(String[] args){ 
     BasicInheritanceTest3 bit2 = new BasicInheritanceTest3(); 
     bit2.a(); 
    } 
} 

я получил следующий вывод:

BasicInheritanceTest3Base.a() 
BasicInheritanceTest3.b() 
BasicInheritanceTest3.b() 
2 
0 

Теперь первый вопрос здесь: Почему x и this.x указывают на x базового класса, а не класс по уходу за детьми? И если this.x указывает на x базового класса, почему this.b() называет b() дочернего класса? Является ли поведение этого разного для полей и методов?

Однако основная проблема связана с механизмом ключевого слова . Я имею ввиду, что вы знаете, this указывает (относится) к текущему объекту. Если вы думаете, это не волшебное поведение. Место должно быть this. Например, литерал .class для класса невидим, но присутствует в испускаемом байт-коде. Точно так же эта ссылка должна присутствовать в байтекоде.

Хорошо, считая, что это верно, this должен быть public final (пустой финал), который создается при каждом создании объекта и создании его полей. Это означает, что это переменная экземпляра, а не статическая переменная.

Теперь, если этот экземпляр ссылается на ссылку текущего объекта (который является только конкретным объектом), как это происходит выше, использование this отличается для полей и методов? Итак, в целом, что за механизм this? Имеет ли механизм также ключевое слово super?

EDIT: Каждый читает вопрос, а затем комментарии, я хочу спросить о, Где this поле объявляется компилятором и каковы его классификаторы. Каким образом возникающее поведение происходит за кулисами?

+17

Помните это простое правило: «поля не являются полиморфными» (полиморфизм в Java работает только для методов, которые не являются статическими, частными или конечными). Поскольку поля не являются полиморфными, их привязка происходит во время компиляции, а во время компиляции компилятор ничего не знает о производных классах, поэтому он связывает 'x' с ближайшим доступным' x', который он может найти. – Pshemo

+0

@Pshemo: Я все для этого. Это внутренности, о которых мне интересно. –

+1

это ключевое слово может использоваться для (его нельзя использовать со статическими методами): Чтобы получить ссылку на объект, через который этот метод вызывается внутри него (метод экземпляра). Чтобы избежать поля, затененного параметром метода или конструктора. Вызов конструктора того же класса. В случае переопределения метода это используется для вызова метода текущего класса. Ссылка на внутренний класс. например ClassName.this – MeshBoy

ответ

3

Другие ответы и комментарии объяснили, как поля не являются полиморфными и как выражения доступа поля разрешаются на основе времени компиляции типа ссылки экземпляра. Ниже я объясню, как байтовый код обрабатывает ссылку this.

В главе о Прием Аргументы, что Java Virtual Machine Specification состояния

Если N аргументы передаются метод экземпляра, они получены путем конвенции, в локальных переменных, пронумерованных от 1 до п из frame , созданный для нового вызова метода. Аргументы получены в порядке их передачи. Например:

int addTwo(int i, int j) { 
    return i + j; 
} 

компилирует:

Method int addTwo(int,int) 
0 iload_1  // Push value of local variable 1 (i) 
1 iload_2  // Push value of local variable 2 (j) 
2 iadd   // Add; leave int result on operand stack 
3 ireturn  // Return int result 

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

Методы класса (статические) не имеют экземпляра, поэтому для них это использование локальной переменной 0 не требуется. Метод класса начинает использовать локальные переменные индекс 0. Если метод addTwo был метод класса, его аргументы будут переданы аналогичным образом к первой версии:

static int addTwoStatic(int i, int j) { 
    return i + j; 
} 

компилирует:

Method int addTwoStatic(int,int) 
0 iload_0 
1 iload_1 
2 iadd 
3 ireturn 

Единственное отличие состоит в том, что аргументы метода появляются начиная локальных переменных 0, а не 1.

в другом слове s, вы можете либо просмотреть как не объявляемый нигде, либо как объявленный как первый параметр каждого метода экземпляра. Локальная запись таблицы переменных создается для каждого метода экземпляра и заполняется при каждом вызове.

В главе о Invoking methods заявляет

Нормальный вызов метода для метода экземпляра отправляет от типа времени выполнения объекта. (Они являются виртуальными, в терминах на С ++). Такой вызов реализован с использованием команды invokevirtual, которую принимает в качестве своего аргумента индекс для записи пула постоянных пулов , давая внутреннюю форму двоичного имени типа класса объекта , имя вызываемого метода и дескриптор этого метода (п. 4.3.3). Для вызова addTwo метод, определенный ранее как метод экземпляра , мы могли бы написать:

int add12and13() { 
    return addTwo(12, 13); 
} 

Это компилирует:

Method int add12and13() 
0 aload_0    // Push local variable 0 (this) 
1 bipush 12   // Push int constant 12 
3 bipush 13   // Push int constant 13 
5 invokevirtual #4 // Method Example.addtwo(II)I 
8 ireturn    // Return int on top of operand stack; 
         // it is the int result of addTwo() 

Вызов устанавливается на первом нажатии ссылки на ток экземпляр, this, на стек операнда. Признаки метода invoke, значения int значений 12 и 13, затем толкаются. Когда кадр для создается метод addTwo, аргументы, переданные методу , становятся начальными значениями локальных переменных нового фрейма. То есть ссылка на this и два аргумента, вытолкнутых на операнд стек вызывающим, станут начальными значениями местных переменных 0, 1 и 2 вызываемого метода.

+1

Наконец-то, какой ответ я искал, исследуя детали низкого уровня. Спасибо –

1

Почему x и this.x указывают на x базового класса, а не на класс Child?

Поскольку поля в Java не являются полиморфными. Связывание полей разрешается во время компиляции. Если вы хотите использовать инкремент в качестве полиморфизма, вы можете сделать это с помощью метода. Для правильной работы вам необходимо определить его в родительском и дочернем.

public void increment(){ 
    x++; //this.x++; would do the same; 
} 

И если this.x указывает на х базового класса, поэтому this.b() вызывает Ь() класса ребенка?

Поскольку методы, с другой стороны, являются полиморфными, что означает, что их привязка разрешена во время выполнения, и именно поэтому метод this.b() вызывает метод класса child, в вашем случае это экземпляр BasicInheritanceTest3 и соответствующий метод называется.

Является ли поведение этого разного для полей и методов?

Как видите.

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

EDIT Ответ: Это ссылка, которая означает, что это только адрес объекта вместе со всеми его данными в памяти JVM, как JVM обрабатывает это ключевое слово, не очень известное или важное, оно, вероятно, объявлено при создании экземпляра. Но все, что вам нужно знать, в конце концов - это ссылка на экземпляр объекта сам.

+0

Прочтите EDIT и «более позднюю часть» вопроса. То, что вы говорите, я уже знаю. –

+0

@pulp_fiction Добавлен ответ редактирования. – notanormie

0

[EDIT answer] Я провел немного исследований и получил следующую информацию, чтобы ответить на ваш вопрос дальше. Мы действительно можем проверить, что this является частью байт-кода, используя обратный инженерный инструмент для преобразования байт-кода в исходный код java.

Почему мы находим в байте-коде?

Потому что с Java является многопроходным компилятором и так байткод можно ожидать, будет работать на любой другой платформе и любой другой машину, вся информация должна быть в байткоде, достаточно информации, чтобы иметь возможность перепроектировать байт-код в исходный код. Furhter, поскольку исходный код должен быть таким же, как исходный источник для байт-кода, все, включая точные имена переменных и полей, должно быть «каким-то образом» хорошо организовано со всей информацией в байт-коде. В то время как C++ или pascal, в отличие от java, которые используют компилятор с одним проходом, в основном не будут содержать точные имена полей, и поскольку такие языки выдают окончательный «исполняемый» файл, который должен быть готов к запуску, может точные имена (инструкция не должна проходить через другой «проход»). Если кто-нибудь обратит инженеров исполняемый файл (C++ или Pascal), имена переменных не будут доступны для чтения. Таким образом, в байт-коде «это» может быть представлено как нечитаемый для человека формат, но то же самое можно было бы вернуть обратно в «это». То же самое не относится к однопроходному компилятору.

Multi Pass Compiler

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

Теперь первый вопрос: почему x и this.x указывают на x из базового класса, а не на класс ребенка?

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

Почему this.b() вызывает b() дочернего класса? Является ли поведение этого по-разному для полей и методов?

С этой строкой: BasicInheritanceTest3 бит2 = новый BasicInheritanceTest3(); Единственным объектом в куче (в терминах базового и дочернего классов) является объект типа BasicInheritanceTest3. Поэтому, независимо от this, вызов будет применяться к методу дочернего класса. bit2 ссылается на свою собственную иерархию (наследования) в кучу.

Теперь, как работает с ним компилятор, то же самое, что и другие ключевые/зарезервированные слова обрабатываются jdk. этот не допускается в контексте методов класса Методы класса не могут напрямую обращаться к переменным экземпляра или методам экземпляра - они должны использовать ссылку на объект. Кроме того, методы класса не могут использовать это ключевое слово, так как для этого нет экземпляра. Действительно, интригующий вопрос, таким образом, дал ОП за голосование по этому вопросу.

Дополнительная информация Информация, которую я читал, была по строкам: Идентификаторы и зарезервированные ключевые слова являются токенами, такими как одиночные символы, как +, и последовательности символов, подобных! =.

Я хотел бы попросить сообщество сохранить эту тему. Я не изучал, как jdk (как компилятор, так и среда выполнения) обрабатывают ключевые слова и зарезервированные слова.

Java Api Docs: this

1

1. Почему х и this.x точка х базового класса, а не класс по уходу за детьми?

мы можем увидеть этот пример:

class TestBase { 
    private int x; 
    public void a() { 
     this.x++; 
    } 
    public int getX() { 
     return x; 
    } 
} 
public class Test extends TestBase{ 
    private int x; 
    public int getX() { 
     return this.x; 
    } 
} 

и сгенерированный байткод:

public class Test extends TestBase{ 
public Test(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method TestBase."<init>":()V 
    4: return 

public int getX(); 
    Code: 
    0: aload_0 
    1: getfield  #2; //Field x:I 
    4: ireturn 

public void a(); 
    Code: 
    0: aload_0 
    1: invokespecial #3; //Method TestBase.a:()V 
    4: return 

} 

В там TestextendsTestBase и метод a компилируется в Test класс, он будет называть это отец 1: invokespecial #3; //Method TestBase.a:()V.

Test «s getX метод будет вызывать 1: getfield #2; //Field x:I от его собственного constant pool table, http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings

TestBase класс байткодом:

class TestBase extends java.lang.Object{ 
TestBase(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public void a(); 
    Code: 
    0: aload_0 
    1: dup 
    2: getfield  #2; //Field x:I 
    5: iconst_1 
    6: iadd 
    7: putfield  #2; //Field x:I 
    10: return 

public int getX(); 
    Code: 
    0: aload_0 
    1: getfield  #2; //Field x:I 
    4: ireturn 

} 

метод a() также получит x от его собственного постоянного пула getfield #2; //Field x:I.

так что есть другое: Java getter и setter - это зло.

1

На самом деле, свойство полиморфизма в языке программирования JAVA применимо только к методам , которые имеют достаточную квалификацию, чтобы быть полиморфными членами. Вы не должны думать о Поля в качестве членов, у которых есть указанное свойство. Таким образом, вы больше не будете путаться в таких проблемах.

Смежные вопросы