2015-04-01 2 views
2

Когда я читал Мышление в Java (4-е издание) недавно я столкнулся с проблемой связывания метода в Java. Сначала давайте рассмотрим два определения из книги:Метод привязки в Java

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

вы можете найти эти определения в связывании из главы полиморфизм раздел Method-вызова. (Страницы 281-282)

Свидетельствовать, что я написал следующий код:

public class Test3{ 
    public static void main(String[] args) { 
     BindingTest_Sub sub1 = new BindingTest_Sub(); 
     BindingTest_Base sub2 = new BindingTest_Sub(); 

     sub1.ovrLd(new Integer(1));  // statement 1 
     sub2.ovrLd(new Integer(2));  // statement 2 
     sub2.ovrRd();      // statement 3 
    } 
} 

class BindingTest_Base { 
    void ovrLd(Object obj){ 
     System.out.println("BindingTest_Base ovrLd()"); 
    } 
    void ovrRd(){ 
     System.out.println("BindingTest_Base ovrRd()"); 
    } 
} 

class BindingTest_Sub extends BindingTest_Base{ 
    void ovrLd(Integer i){ 
     System.out.println("BindingTest_Sub ovrLd()"); 
    } 
    void ovrRd(){ 
     System.out.println("BindingTest_Sub ovrRd()"); 
    } 
} 

Результат выполнения является:

BindingTest_Sub ovrLd() 
BindingTest_Base ovrLd() 
BindingTest_Sub ovrRd() 

На основании этого результата, я следующие вопросы:

  1. В соответствии с определением из книги, поскольку все мои методы наследуются, Java будет использовать позднюю привязку (динамическое связывание) для всех трех утверждений. Тем не менее, я читал о некоторых других статьях, в которых говорилось, что Java использует статическое связывание при работе с перегрузкой. Это кажется противоречивым, потому что явно утверждение 1 перегружает.
  2. Я не совсем понимаю, почему Java назвал ovrLd() базового класса в инструкции 2. Если он использовал динамическую привязку, он должен вызывать overLd() подкласс, потому что JVM времени выполнения должен быть ясным, что sub2 является экземпляром Класс BindingTest_Sub. С другой стороны, если он использовал статическую привязку, он также должен был бы вызвать overLd() подкласса, потому что компилятор может заметить, что тип данного аргумента является Integer. Можете ли вы сказать мне, какая работа была выполнена компилятором или JVM, когда он заключает с оператором 2.
  3. Результат утверждения 3 имеет смысл для меня. Но все же мне интересно, как компилятор распознает его (ovrRd()) как переопределяющий метод. Другими словами, как компилятор знает, что существует еще один класс, который имеет метод, который переопределяет этот ovrRd().

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

+2

В книге указано, что «все привязки метода в Java используют позднюю привязку, если этот метод не является статическим или окончательным»? В самом деле? Если это так, это неправильно.Java выполняет разрешение перегрузки во время компиляции, но затем находит правильную * реализацию * во время выполнения на основе цели вызова. –

+0

@JonSkeet Да, я не изменил ни слова. Если это неточно, как вы упомянули, какой тип привязки использует Java при работе с обычным вызовом метода? (ни метод перегрузки, ни метод переопределения) – Yunhe

+0

Его важно посмотреть на тип времени компиляции ссылки на объект; sub2 является ссылкой на базовый класс, поэтому без кастования вызовет методы базовых классов (и компилятор будет __only__ разрешать базовые методы) –

ответ

1

TL; DR; Вы не перегружаете ovrLd(Object), а не во время выполнения. Компилятор использует информацию типа времени компиляции, чтобы решить, какой из них является лучшим виртуальным методом. sub1 и sub2 имеют разные типы времени компиляции. А тип sub1 имеет различное лучшее совпадение для ovrLb(Integer).

Объяснение.Вам интересно:

  • если sub1.ovrLd(new Integer(1)) звонки BindingTest_Sub.ovrLd(Integer)
  • то почему sub2.ovrLd(new Integer(2)) звонит BindingTest_Base.ovrLd(Object)

В этом случае он работает так:

Компилятор использует время компиляции введите информацию переменной, чтобы решить, какой метод вызывать. Тип времени компиляции sub2 - BindingTest_Base, во время выполнения вы назначаете ему BindingTest_Sub, но это не относится к компилятору.

Единственный способ от BindingTest_Base, что соответствует параметрам этого вызова: BindingTest_Base.ovrLd(Object)

Так компилятор выдает виртуальный вызов orvLd(Object)

Теперь метод выполнения для виртуального вызова определяется на основе полная подпись вызываемого метода (имя + параметры). И нет никакой перегрузки для ovrLd(Object) в BindingTest_Sub Так вызывается метод базового класса.

С sub1 Компилятор имеет более подробную информацию. sub1 's тип времени компиляции - BintdingTest_Sub, и существует способ, который соответствует ovrLd(Integer) в этом классе.

Глядя на байткод вы можете увидеть это ясно:

aload 1 // sub1 
// ... blah blah blah creating the integer 
// the last opcode issued by the compiler for "statement 1" 
INVOKEVIRTUAL com/ea/orbit/actors/samples/helloworld/BindingTest_Sub.ovrLd (Ljava/lang/Integer;)V 

aload 2 // sub2 
// ... blah blah blah creating the integer 
// the last opcode issued by the compiler for "statement 2" 
INVOKEVIRTUAL com/ea/orbit/actors/samples/helloworld/BindingTest_Base.ovrLd (Ljava/lang/Object;)V 
1

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

Автор сказал, что Вся привязка метода в Java использует позднюю привязку, если только этот метод не является статическим или окончательным.

Я думаю, что это правда, но неоднозначно. Неопределенность от термина позднего связывания. По моему мнению, привязка здесь означает определение конкретной реализации метода, а не . Разрешение метода (разрешено символом в таблице символов). Другими словами, компилятор просто ссылается на метод на символ, но там, где в памяти этот символ указывает, не определено.

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