Cosa succede realmente nell'ereditarietà (java)? [chiuso]

-5
class A {
    int a;
    int b;

    A(int i,int z) {
        a = i;
    }

    void abc() {     
        System.out.println("a is " + a);
        System.out.println("b is " + b);
    }
}
class B extends A {
    int a = 55;
    int b = 66;

    B() {
        super(14,13);
        super.b = 99;
    }
}
public class test {    
    public static void main(String[] argue) {
          B obj = new B();
          obj.abc();
    }
}

uscita:

a is 14
b is 99

Vorrei fare riferimento al codice precedente per le mie domande. Non ho alcuni concetti OOP di base vividi. Per favore, chiarisci i miei dubbi riguardo alle seguenti domande:

  1. Che cosa è successo realmente quando la classe B ha esteso A? Il codice di A copiato in B, ogni variabile definita in A è ora definita anche in B e ogni definizione di metodo in A è ora anche in B?
  2. Poiché una classe è solo un modello, elenca gli attributi che l'oggetto avrà e la memoria verrà allocata solo per ogni definizione di variabile in una classe quando viene creato l'oggetto. Ora se dici che quando la classe B ha esteso A il codice dalla classe A non è stato copiato in B, cioè le variabili (int a, int b) originariamente definite in A, le loro definizioni non sono incollate nella classe B. Quando l'oggetto della classe B viene creato alloca la memoria per le variabili 'a' e 'b' nella classe B, non di classe A, poiché la loro definizione non viene copiata in B. Come può la classe B usare le variabili di super classe 'a' e 'b' anche se la super classe non è stata istanziata, ovvero 'a' e 'b' non esistono in memoria poiché la classe definisce semplicemente un modello. Il compilatore alloca parti di memoria separate per 'a' e 'b' su considerazioni speciali per la classe B in quanto ha esteso la classe A? Che dire del metodo definito nella classe A?
  3. Mi aspettavo obj.abc () ( l'ultima istruzione nel metodo principale ) da stampare:

    a is 55
    b is 66
    

    come il metodo abc () era originariamente definito in classe A e ora la classe B eredita A, quindi ha ereditato abc (). Quindi, se avessi chiamato abc () con un riferimento dell'oggetto della classe B, avrebbe dovuto stampare i valori di 'a' e 'b' nella classe B i.e.

     a is 55
     b is 66
    
posta With A SpiRIT 06.12.2015 - 07:37
fonte

1 risposta

2

L'output che descrivi non corrisponde al codice che hai postato. In realtà stampa

a is 14
b is 99

Capisco perché succede dopo. Ma prima, riscriverò il tuo codice in un modulo che mostri le relazioni tipicamente trovate nell'eredità:

public class A {
    int a;
    int b;

    A(int i, int z) {
        a = i;
        b = z;
    }

    void abc() {
        System.out.println("a is " + a);
        System.out.println("b is " + b);
    }
}

public class B extends A {
    B() {
        super(14, 13);
    }
}

public static void main(String[] argv) throws Exception {
    B obj = new B();
    obj.abc();
}

Che dà l'output

a is 14
b is 13

Questa è un'eredità nella sua forma più semplice e ha le seguenti caratteristiche:

  • Un'istanza di B contiene due variabili membro, a e b . Queste variabili sono definite dalla classe A .
  • Quando invoca il metodo abc() su un'istanza di B , JRE determina che il metodo è effettivamente definito nella classe A . Questo è noto come dispatch dinamico ed è una delle caratteristiche chiave dell'ereditarietà.

Ora una modifica:

public class B extends A {
    B() {
        super(14, 13);
    }

    @Override
    void abc() {
        System.out.println("b is " + b);
        System.out.println("a is " + a);
    }
}

Qui ho sovrascritto il metodo abc() nella classe B . Quando JVM esamina la classe, determina che B ha definito il metodo, quindi richiama tale implementazione e non ricerca un'implementazione in A .

Poiché B estende A e le variabili membro a e b non sono dichiarate private da A , B ha accesso a tali variabili. L'output è:

b is 13
a is 14

Un'altra modifica:

public class B extends A {
    B() {
        super(14, 13);
        super.b = 99;
    }

    @Override
    void abc() {
        System.out.println("b is " + b);
        System.out.println("a is " + a);
    }
}

Ciò evidenzia la catena di costruzione. In una gerarchia di ereditarietà, un costruttore di classi deve chiamare un costruttore di superclasse. Quindi, B() chiama esplicitamente A(int,int) , che chiama implicitamente Object() .

L'ordine è importante: immediatamente dopo la chiamata super(14,13) , il valore della variabile membro b è 13. Tuttavia, il costruttore B() modifica quel valore, quindi il programma stampa

b is 99
a is 14

Ho lasciato il compito come super.b = 99 , ma in questo caso potrebbe anche essere stato appena b = 99 , perché la classe B ha accesso alla variabile definita da A . Tuttavia, questo modulo diventa importante nella prossima revisione.

Portiamo questo programma un passo più vicino al tuo originale:

public class B extends A {

    int a = 55;
    int b = 72;

    B() {
        super(14, 13);
        super.b = 99;
    }

    @Override
    void abc() {
        System.out.println("b is " + b);
        System.out.println("a is " + a);
    }
}

In questo caso sto ridefinendo le variabili a e b nella classe B . Queste definizioni shadow le definizioni della classe A . Qui è dove diventa complicato - e perché, nel codice di produzione, lo shadowing è un odore di codice.

  • Ora ci sono quattro variabili nell'istanza della classe. Due sono definiti dalla classe A e due sono definiti dalla classe B .
  • Il codice nella classe B ignorerà le variabili definite da A
  • Salvo prefisso esplicitamente il riferimento alla variabile con super

Quindi, dopo la costruzione, ecco le quattro variabili:

  • a , definito da A , con il valore 14
  • b , definito da A , con il valore 99
  • a , definito da B , con il valore 55
  • b , definito da B , con il valore 72

Si noti che la variabile b definita da A è stata esplicitamente modificata dal costruttore di B , dopo è stato assegnato dal costruttore di A , perché B ' costruttore di s riferito ad esso con super .

Tuttavia, la definizione del metodo abc() in B non prefissa tali variabili con super , quindi stamperà i valori delle variabili definite da B :

b is 72
a is 55

Ultimo passo: rimuovo la definizione di funzione abc() dalla classe B .

public class B extends A {

    int a = 55;
    int b = 72;

    B() {
        super(14, 13);
        super.b = 99;
    }
}

Ora il JRE richiama di nuovo la funzione definita in A . Tuttavia, questa implementazione ha solo accesso alle due variabili che sono anche definite da A ; non ha conoscenza delle due variabili definite da B anche se esistono all'interno dell'istanza dell'oggetto. Quindi l'output è

a is 14
b is 99

Perché non b 13 ? Poiché il costruttore di B ha fatto esplicito riferimento alla variabile definita da A .

Chiunque senta il bisogno di aggiungere riferimenti alle specifiche dovrebbe sentirsi libero di farlo; Non penso siano necessari.

    
risposta data 06.12.2015 - 17:43
fonte

Leggi altre domande sui tag