Спадкування: Чи код з надкласу практично * скопійований * у підклас, чи його * посилається на підклас *?


10

Клас Sub- це підклас класу Sup. Що це означає практично? Або іншими словами, яке практичне значення "спадкування"?

Варіант 1: Код з Sup фактично копіюється в Sub. (як у "копіювати-вставити", але без скопійованого коду, який візуально відображається в підкласі).

Приклад: methodA()метод, спочатку в Sup. Sub розширює Sup, тому methodA()його (практично) копіюють на Sub. Тепер у Sub є метод, названий methodA(). Він ідентичний Sup methodA()у кожному рядку коду, але повністю належить до Sub - і не залежить від Sup або не пов'язаний із Sup.

Варіант 2: Код з Sup фактично не скопійований у Sub. Це ще тільки в суперкласі. Але до цього коду можна отримати доступ через підклас і може бути використаний підкласом.

Приклад: methodA()метод в Sup. Sub розширює Sup, так що тепер methodA()можна отримати через Sub наступним чином: subInstance.methodA(). Але це насправді посилатиметься methodA()на надклас. Що означає, що methodA () буде працювати в контексті надкласу, навіть якщо його викликав підклас.

Запитання: Який із двох варіантів насправді працює? Якщо жодного з них немає, опишіть, як вони насправді працюють.


Це легко перевірити на собі - написати код, вивчити файли класів (навіть контрольна сума це зробить), змінити суперклас, знову скласти, знову подивитися файли класів. Ви також можете прочитати Розділ 3. Складання для віртуальної машини Java специфікації JVM буде корисним для розуміння (особливо, розділ 3.7).

@MichaelT " практично скопійовано" - це ключове слово. Крім того, навіть якщо код буквально копіювався, це може статися лише після завантаження класу.

@delnan було б цікаво, якби Hotspot (або інші оптимізатори виконання) в якийсь момент вбудували код, але це стане детальною інформацією про реалізацію JVM, яка може відрізнятися від одного JVM до іншого і тому не вдасться відповісти правильно. Найкраще, що можна зробити, - це переглянути скомплектований байт-код (і використання специфічного використання коду, який описує, що відбувається насправді)

Відповіді:


13

Варіант 2.

На байт-код посилається динамічно під час виконання: ось чому, наприклад, трапляються LinkageErrors .

Наприклад, припустимо, що ви складаєте два класи:

public class Parent {
  public void doSomething(String x) { ... }
}

public class Child extends Parent {
  @Override
  public void doSomething(String x) {
    super.doSomething(x);
    ...
  }
}

Тепер модифікуйте та перекомпілюйте батьківський клас, не змінюючи чи перекомпілюючи дочірній клас :

public class Parent {
  public void doSomething(Collection<?> x) { ... }
}

Нарешті, запустіть програму, яка використовує дочірній клас. Ви отримаєте NoSuchMethodError :

Викидається, якщо програма намагається викликати вказаний метод класу (статичний або екземпляр), і цей клас більше не має визначення цього методу.

Зазвичай ця помилка виявляється компілятором; ця помилка може виникнути лише під час виконання, якщо визначення класу несумісно змінено.


7

Почнемо з двох простих класів:

package com.michaelt.so.supers;

public class Sup {
    int methodA(int a, int b) {
        return a + b;
    }
}

і потім

package com.michaelt.so.supers;

public class Sub extends Sup {
    @Override
    int methodA(int a, int b) {
        return super.methodA(a, b);
    }
}

Метод компіляціїA і перегляд байтового коду виходить:

  methodA(II)I
   L0
    LINENUMBER 6 L0
    ALOAD 0
    ILOAD 1
    ILOAD 2
    INVOKESPECIAL com/michaelt/so/supers/Sup.methodA (II)I
    IRETURN
   L1
    LOCALVARIABLE this Lcom/michaelt/so/supers/Sub; L0 L1 0
    LOCALVARIABLE a I L0 L1 1
    LOCALVARIABLE b I L0 L1 2
    MAXSTACK = 3
    MAXLOCALS = 3

І ви можете побачити прямо там, за допомогою методу invokespecial, він робить пошук по методу класу SupA ().

Invokespecial опкод має наступну логіку:

  • Якщо C містить декларацію для методу екземпляра з тим самим іменем та дескриптором, що і розв'язаний метод, тоді цей метод буде застосовано. Процедура пошуку припиняється.
  • В іншому випадку, якщо C має надклас, ця сама процедура пошуку виконується рекурсивно, використовуючи прямий надклас C. Метод, який слід викликати, є результатом рекурсивного виклику цієї процедури пошуку.
  • Інакше піднімається AbstractMethodError.

У цьому випадку не існує жодного методу екземпляра з тим самим іменем та дескриптором у своєму класі, тому перша куля не збирається. Однак друга куля буде - є суперклас, і він викликає метод супер.

Компілятор не вказує на це рядки, і немає копії джерела Sup у класі.

Однак історія ще не закінчена. Це просто складений код. Як тільки код потрапить у JVM, HotSpot може долучитися.

На жаль, я про це не знаю багато, тому я звернусь до авторитету з цього приводу і перейду до Inlining на Яві, де сказано, що HotSpot може вбудовувати методи (навіть не завершальні методи).

Переходячи до документів , зазначається, що якщо виклик певного методу стає гарячою точкою замість того, щоб щоразу шукати цю інформацію, цю інформацію можна вводити - ефективно копіюючи код із Sup methodA () у Sub methodA ().

Це робиться під час виконання, в пам'яті, виходячи з того, як поводиться програма та які оптимізації необхідні для прискорення продуктивності.

Як зазначено в HotSpot Internals для OpenJDK, "Методи часто вказуються. Статичні, приватні, остаточні та / або" спеціальні "виклики легко вводити".

Якщо ви заглибитесь у параметри для JVM, ви знайдете варіант -XX:MaxInlineSize=35(35 за замовчуванням), який є максимальною кількістю байтів, які можна вкласти. Я зазначу, що саме тому Java любить мати безліч методів, оскільки їх можна легко вписати. Ці маленькі методи стають швидшими, коли їх називають більше, тому що їх можна накреслити. І хоча можна грати з цим числом і збільшувати його, це може призвести до того, що інші оптимізації будуть менш ефективними. (пов'язане питання ТАК: Стратегія вбудованого проекту HotSpot JIT, яка вказує на ряд інших варіантів зазирнути до внутрішніх тексту вставки, що робить HotSpot).

Отже, ні - код не вказується під час компіляції. І так, код дуже добре може бути накреслений під час виконання, якщо цього вимагають оптимізації роботи.

І все, що я писав про вбудовування HotSpot, стосується лише HotSpot JVM, розповсюдженого Oracle. Якщо ви подивитеся на список віртуальних машин Вікіпедії, то є багато більше, ніж просто HotSpot, і спосіб, яким ці JVM обробляють вбудовані вставки, може бути зовсім іншим, ніж те, що я описав вище. Apache Harmony, Dalvik, ART - там все може працювати інакше.


0

код не копіюється, доступ до нього здійснюється за посиланням:

  • підклас посилається на його методи та надклас
  • суперклас посилається на свої методи

компілятори можуть оптимізувати, як це представлено / виконано в пам'яті, але це в основному структура

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.