Ereditarietà: il codice dalla superclasse è virtualmente * copiato * in sottoclasse o è * riferito alla sottoclasse *?

10

La classe Sub è una sottoclasse della classe Sup . Cosa significa in pratica? O in altre parole, qual è il significato pratico di "ereditarietà"?

Opzione 1: Il codice di Sup è praticamente copiato in Sub. (come in "copia-incolla", ma senza il codice copiato visivamente visto nella sottoclasse).

Esempio: methodA() è un metodo originariamente in Sup. Sub estende Sup, quindi methodA() è (praticamente) incollato su Sub. Ora Sub ha un metodo chiamato methodA() . È identico al% co_de di Sup in ogni riga di codice, ma appartiene interamente a Sub - e non dipende da Sup o è correlato a Sup in alcun modo.

Opzione 2: Il codice di Sup non è in realtà copiato in Sub. È ancora solo nella superclasse. Ma quel codice è accessibile attraverso la sottoclasse e può essere utilizzato dalla sottoclasse.

Esempio: methodA() è un metodo in Sup. Sub estende Sup, quindi ora methodA() è accessibile tramite Sub in questo modo: methodA() . Ma in realtà invocherà subInstance.methodA() nella superclasse. Il che significa che methodA () funzionerà nel contesto della superclasse, anche se è stato chiamato dalla sottoclasse.

Domanda: quale delle due opzioni è davvero come funzionano le cose? Se nessuno di questi è, per favore descrivi come funzionano queste cose.

    
posta Aviv Cohn 15.04.2014 - 22:29
fonte

3 risposte

13

Opzione 2.

Il bytecode viene fatto riferimento dinamicamente al runtime: questo è il motivo, ad esempio, LinkageErrors si verificano.

Ad esempio, supponi di compilare due classi:

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

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

Ora modifica e ricompila la classe genitrice senza modificare o ricompilare la classe figlia :

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

Infine, esegui un programma che utilizza la classe figlio. Riceverai un NoSuchMethodError :

Thrown if an application tries to call a specified method of a class (either static or instance), and that class no longer has a definition of that method.

Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.

    
risposta data 15.04.2014 - 22:43
fonte
7

Iniziamo con due semplici classi:

package com.michaelt.so.supers;

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

e poi

package com.michaelt.so.supers;

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

Compilando il metodoA e osservando il codice byte si ottiene:

  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

E puoi vedere proprio lì con il metodo invokespecial che fa la ricerca contro il metodo della classe SupA ().

Il invokespecial opcode ha il seguente logica:

  • Se C contiene una dichiarazione per un metodo di istanza con lo stesso nome e descrittore del metodo risolto, questo metodo verrà invocato. La procedura di ricerca termina.
  • Altrimenti, se C ha una superclasse, questa stessa procedura di ricerca viene eseguita in modo ricorsivo usando la superclasse diretta di C. Il metodo da invocare è il risultato del richiamo ricorsivo di questa procedura di ricerca.
  • In caso contrario, viene sollevato un AbstractMethodError.

In questo caso, non esiste un metodo di istanza con lo stesso nome e descrittore nella sua classe, quindi il primo punto non sparerà. Il secondo proiettile tuttavia - c'è una superclasse e invoca il metodo del superA.

Il compilatore non lo incorpora e non c'è una copia della fonte di Sup nella classe.

Tuttavia la storia non è ancora finita. Questo è solo il codice compilato . Una volta che il codice raggiunge la JVM, HotSpot può essere coinvolto.

Sfortunatamente, non ne so molto, quindi mi rivolgerò all'autorità in materia e andrò a Inlining in Java dove si dice che HotSpot può metodi in linea (anche metodi non definitivi).

Andando alla documentazione si osserva che se una particolare chiamata di metodo diventa un hot spot invece di fare quella ricerca ogni volta, questa informazione può essere inline - copiando in modo efficace il codice dal metodo SupA () nel metodo SubA ().

Questo è fatto in fase di runtime, in memoria, in base a come si comporta l'applicazione e quali sono le ottimizzazioni necessarie per velocizzare le prestazioni.

Come indicato in HotSpot Internals for OpenJDK "i metodi sono spesso in linea. Statico, privato, finale e / o le invocazioni "speciali" sono facili da allineare. "

Se apri le opzioni per la JVM trova un'opzione di -XX:MaxInlineSize=35 (35 è il valore predefinito) che è il numero massimo di byte che possono essere sottolineati. Sottolineerò che questo è il motivo per cui a Java piace avere molti piccoli metodi, perché possono essere facilmente integrati. Questi piccoli metodi diventano più veloci quando vengono chiamati più perché possono essere in linea. E mentre si può giocare con quel numero e renderlo più grande, le altre ottimizzazioni potrebbero risultare meno efficaci. (domanda SO correlata: Strategia di inlinea JIT HotSpot che indica una serie di altre opzioni per dare un'occhiata agli interni di inlining di quell'HotSpot sta facendo).

Quindi no - il codice non è in linea al momento della compilazione. E, sì, il codice potrebbe essere ben delineato in fase di esecuzione se le ottimizzazioni delle prestazioni lo richiedono.

E tutto ciò che ho scritto sull'integrazione di HotSpot si applica solo a HotSpot JVM distribuito da Oracle. Se guardi l'elenco di wikipedia delle macchine virtuali Java ci sono molti più di un semplice HotSpot e il modo in cui le JVM gestiscono l'inlining può essere completamente diverso da quello che ho descritto sopra. Apache Harmony, Dalvik, ART - le cose potrebbero funzionare diversamente là.

    
risposta data 16.04.2014 - 04:29
fonte
0

il codice non viene copiato, vi si accede per riferimento:

  • la sottoclasse fa riferimento ai suoi metodi e alla superclasse
  • la superclasse referenzia i suoi metodi

I compilatori possono ottimizzare come viene rappresentato / eseguito in memoria, ma questa è fondamentalmente la struttura

    
risposta data 15.04.2014 - 22:41
fonte

Leggi altre domande sui tag