@Sandeep - по поводу вашего последнего комментария (на момент написания) ...
Если в Java все нестатические методы являются виртуальными по умолчанию, почему в книгах говорится: «Повторюсь, компилятор смотрит только на ссылочный тип, а не на тип экземпляра»? Разве это утверждение не является привязкой времени компиляции?
Думаю, книга немного неполная ...
Под «типом ссылки» в книге говорится о том, как объявляется данная переменная; мы можем назвать это классом переменной. Одна вещь, которая поможет вам перейти от C ++, - это рассматривать всю Java как переменные как указатели на конкретный экземпляр (кроме примитивных типов, таких как 'int'). Достаточно легко сказать, что все в Java - это «передача по значению», но поскольку переменные всегда являются указателями, это значение указателя, которое помещается в стек при каждом вызове метода ... сами экземпляры объекта остаются в том же самом разместить в куче.
Это то, что я писал изначально, прежде чем заметил комментарий ...
Идеи «время компиляции» и «время выполнения» не так полезны (для меня) для прогнозирования поведения.
Я говорю это, потому что более полезный вопрос (для меня) - «Как мне узнать, какой метод будет вызван во время выполнения?»
И под «Откуда мне знать» я имею в виду «Как мне предсказать»?
Методы экземпляра Java управляются тем, чем на самом деле является экземпляр (виртуальные функции в C ++). Экземпляр класса Horse всегда будет экземпляром Horse. Ниже приведены три разные переменные («ссылочные типы», если использовать формулировку из книг), которые относятся к одному и тому же экземпляру Horse.
Horse x = new Horse();
Animal y = x;
Object z = x;
Методы класса Java (в основном любой метод со статическим перед ним) менее интуитивно понятны и в значительной степени ограничены точным классом, на который они ссылаются в исходном коде, что означает «привязка во время компиляции».
Обратите внимание на результаты теста (ниже) при чтении следующего:
Я добавил еще одну переменную в ваш класс TestAnimals и немного поиграл с форматированием ... В main () теперь у нас есть 3 переменные:
Animal a = new Animal();
Animal b = new Horse();
Horse c = new Horse(); // 'c' is a new variable.
Я немного изменил вывод eat ().
Я также добавил метод класса xyz () как в Animal, так и в Horse.
Из распечаток видно, что все это разные экземпляры. На моем компьютере «a» указывает на Animal @ 42847574 (ваш скажет Animal @ some_number, фактическое число будет варьироваться от одного запуска к другому).
'a' points to Animal@42847574
'b' points to Horse@63b34ca.
'c' points to Horse@1906bcf8.
Итак, в начале main () у нас есть один экземпляр Animal и два разных экземпляра Horse.
Большая разница, которую следует наблюдать, заключается в том, как ведет себя .eat () и как ведет себя .xyz (). Методы экземпляра, такие как .eat (), обращают внимание на класс экземпляра. Не имеет значения, в каком классе находится переменная, указывающая на экземпляр.
С другой стороны, методы класса всегда следуют за объявленной переменной. В приведенном ниже примере, хотя Animal 'b' относится к экземпляру Horse, b.xyz () вызывает Animal.xyz (), а не Horse.xyz ().
Сравните это с Horse 'c', что заставляет c.xyz () вызывать метод Horse.xyz ().
Это сводило меня с ума, когда я изучал Java; по моему скромному мнению, это был дешевый способ сохранить поиск метода во время выполнения. (И, честно говоря, в середине 1990-х, когда создавалась Java, возможно, было важно использовать такие сокращения производительности).
В любом случае, это может быть более ясным после того, как я переназначу животное «а» той же лошади, что и «с»:
a = c;
Now a and c point to same instance:
Animal a=Horse@1906bcf8
Horse c=Horse@1906bcf8
Рассмотрим поведение животного «а» и лошади «с» после этого. Методы экземпляра по-прежнему делают то, что на самом деле является экземпляром. Методы класса по-прежнему следуют, однако переменная объявляется.
=== начать пример запуска TestAnimals ===
$ ls
Animal.java Horse.java TestAnimals.java
$ javac *.java
$ java TestAnimals
Animal a=Animal@42847574
Animal b=Horse@63b34ca
Horse c=Horse@1906bcf8
calling a.eat(): Hello from Animal.eat()
calling b.eat(): Hello from Horse.eat()
calling c.eat(): Hello from Horse.eat()
calling a.xyz(): Hello from Animal.xyz()
calling b.xyz(): Hello from Animal.xyz()
calling c.xyz(): Hello from Horse.xyz()
Now a and c point to same instance:
Animal a=Horse@1906bcf8
Horse c=Horse@1906bcf8
calling a.eat(): Hello from Horse.eat()
calling c.eat(): Hello from Horse.eat()
calling a.xyz(): Hello from Animal.xyz()
calling c.xyz(): Hello from Horse.xyz()
$
=== конец примера запуска TestAnimals ===
public class TestAnimals {
public static void main( String [] args ) {
Animal a = new Animal( );
Animal b = new Horse( );
Horse c = new Horse( );
System.out.println("Animal a="+a);
System.out.println("Animal b="+b);
System.out.println("Horse c="+c);
System.out.print("calling a.eat(): "); a.eat();
System.out.print("calling b.eat(): "); b.eat();
System.out.print("calling c.eat(): "); c.eat();
System.out.print("calling a.xyz(): "); a.xyz();
System.out.print("calling b.xyz(): "); b.xyz();
System.out.print("calling c.xyz(): "); c.xyz();
a=c;
System.out.println("Now a and c point to same instance: ");
System.out.println("Animal a="+a);
System.out.println("Horse c="+c);
System.out.print("calling a.eat(): "); a.eat();
System.out.print("calling c.eat(): "); c.eat();
System.out.print("calling a.xyz(): "); a.xyz();
System.out.print("calling c.xyz(): "); c.xyz();
}
}
public class Animal {
public void eat() {
System.out.println("Hello from Animal.eat()");
}
static public void xyz() {
System.out.println("Hello from Animal.xyz()");
}
}
class Horse extends Animal {
public void eat() {
System.out.println("Hello from Horse.eat()");
}
static public void xyz() {
System.out.println("Hello from Horse.xyz()");
}
}
person
jgreve
schedule
13.01.2016
//RESULT
комментарии в книге тоже? В таком случае текст противоречит комментариям. Теперь, если бы методы былиstatic
, тогда я действительно считаю, что оба вызова будут выполнятьsuperclass method
- person Miserable Variable   schedule 13.01.2016javap
, чтобы класс понял. - person Miserable Variable   schedule 13.01.2016