2012-08-15 13 views
15

Il mio codice è:Java uscita frammento di codice necessaria spiegazione

class Foo { 
    public int a=3; 
    public void addFive() { 
    a+=5; 
    System.out.print("f "); 
    } 
} 

class Bar extends Foo { 
    public int a=8; 
    public void addFive() { 
    this.a += 5; 
    System.out.print("b "); 
    } 
} 

public class TestClass { 
    public static void main(String[]args) { 
    Foo f = new Bar(); 
    f.addFive(); 
    System.out.println(f.a); 
    } 
} 

uscita:

b 3 

Si prega di spiegare a me, perché è l'uscita per questa domanda "b 3" e non "b 13 "poiché il metodo è stato ignorato?

+0

Questa è una domanda riguardante SCJP, giusto? –

+0

yep @PrakharMohan sei apparso anche per lo stesso ??? non ho ancora ... :) –

risposta

14

Non è possibile sostituire le variabili in Java, quindi in realtà hanno due variabili a - uno in Foo e uno in Bar. D'altra parte il metodo addFive() è polimorfico, pertanto modifica Bar.a (Bar.addFive() viene chiamato, nonostante il tipo statico di f sia Foo).

Ma alla fine si accede a f.a e questo riferimento viene risolto durante la compilazione utilizzando il tipo noto di f, ovvero Foo. E quindi Foo.a non è mai stato toccato.

Variabili non finali BTW in Java dovrebbero mai essere pubbliche.

+4

Bene, tranne se si hanno 'GridBagConstraints' o oggetti simili. Ma per un principiante, ** mai ** è un consiglio appropriato :) –

18

F è un riferimento di tipo Foo, e variabili non sono polimorfici così f.a riferisce alla variabile da Foo quali è 3

Come verificare?

Per testare questo è possibile rimuovere a variabile da Foo, che vi darà Errore di compilazione tempo

Nota: variabile membro Fai private e utilizzare funzioni di accesso per accedere a loro


anche Vedi

+10

Per estendere questa risposta un po ', questo è uno dei motivi per cui è spesso più sicuro usare i getter invece di accedere direttamente alle variabili. –

+0

@DaveNewton per favore dimmi qualcosa sui getter? non ho sentito prima –

+2

Questo problema può essere tranquillamente evitato se si utilizza una pratica di programmazione comune in Java: le variabili devono essere private (solo la propria classe può utilizzarla). Dopodiché, come detto sopra, usando i metodi getter e setter per accedervi/modificarlo. – axcdnt

1

Dato che si sta facendo f.a si otterrà il valore di Foo dalla classe . se avevi chiamato un metodo per ottenere il valore, ad esempio getA(), avresti ottenuto il valore dalla classe Bar.

11

Con una tale domanda, l'esame SCJP sta valutando la tua conoscenza di ciò che è noto come che nasconde. L'esaminatore ha deliberatamente complicato le cose per cercare di farti credere che il comportamento del programma dipende solo dal polimorfismo, che non è così.

Proviamo a rendere le cose un po 'più chiare mentre rimuoviamo il metodo addFive().

class Foo { 
    public int a = 3; 
} 

class Bar extends Foo { 
    public int a = 8; 
} 

public class TestClass { 
    public static void main(String[]args) { 
    Foo f = new Bar(); 
    System.out.println(f.a); 
    } 
} 

Ora le cose sono un po 'meno confuse.Il metodo main dichiara una variabile di tipo Foo, a cui viene assegnato un oggetto di tipo Bar in fase di esecuzione. Questo è possibile dal momento che Bar eredita da Foo. Il programma fa quindi riferimento al campo pubblico a della variabile di tipo Foo.

L'errore qui è di credere che lo stesso tipo di concetto noto come che sostituisce si applica ai campi di classe. Ma non c'è un concetto per i campi: il campo pubblico a di classe Bar non imperativa è il campo pubblico a di classe Foo ma fa quello che viene chiamato nasconde. Come suggerisce il nome, significa che nell'ambito della classe Bar, a si riferirà al campo Bar che non ha nulla a che fare con quello di Foo. (JLS 8.4.8 - Inheritance, Overriding, and Hiding)

Quindi, quando stiamo scrivendo f.a, a quale a ci riferiamo? Ricordiamo che la risoluzione del campo a viene eseguita al tempo di compilazione utilizzando il tipo di dichiarazione dell'oggetto f, che è Foo. Di conseguenza, il programma stampa "3".

Ora, aggiungiamo un metodo addFive() nella classe Foo e lo sovrascriviamo nella classe Bar come nella domanda d'esame. Qui si applica il polimorfismo, pertanto la chiamata f.addFive() viene risolta utilizzando non il tempo di compilazione ma il tipo di runtime dell'oggetto f, che è Bar, e quindi è stampato "b".

Ma c'è ancora qualcosa che dobbiamo capire: perché il campo a, che è stato incrementato di 5 unità, si attacca ancora al valore '3'? Qui nascondersi sta giocando in giro. Poiché questo è il metodo della classe Bar che viene chiamato e poiché nella classe Bar, ogni a si riferisce al campo pubblico Bara, questo è in realtà il campo Bar che viene incrementato.

1) Ora, una domanda sussidiaria: come potremmo accedere Campo pubblico s' il Bara dal metodo main? Possiamo farlo con qualcosa di simile:

System.out.println(((Bar)f).a); 

che costringono il compilatore per risolvere il membro campo a della f come Bar s' a campo.

Questo dovrebbe stampare 'b 13' nel nostro esempio.

2) Ancora un'altra domanda: Come potremmo aggirare nasconde in addFive() metodo della classe Bar per riferirsi non a campo s' il Bar, ma al suo campo eponimous superclasse?Basta aggiungere la parola chiave super davanti al riferimento di campo fa il trucco:

public void addFive() { 
    super.a += 5; 
    System.out.print("b "); 
} 

Ciò stampare 'b 8' nel nostro esempio.

Notare che l'affermazione iniziale

public void addFive() { 
    this.a += 5; 
    System.out.print("b "); 
} 

potrebbe essere raffinato per

perché quando il compilatore sta risolvendo il campo a, si inizierà a guardare nel perimetro che racchiude più vicino all'interno del metodo addFive() e trovare l'istanza della classe Bar, eliminando la necessità di utilizzare esplicitamente this.

Ma, beh, this era probabilmente un indizio per il candidato per risolvere questa domanda d'esame!

+0

bella spiegazione Alex – Pratswinz