2012-08-15 15 views
15

Mi código es:Java explicación de salida fragmento de código requiere

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); 
    } 
} 

Salida:

b 3 

favor explicar a mí, ¿por qué es la salida para esta pregunta "b3" y no "b 13 "¿ya que el método ha sido anulado?

+0

Esta es una pregunta con respecto a SCJP, ¿verdad? –

+0

yep @PrakharMohan ¿también has aparecido por lo mismo? todavía no lo he ... :) –

Respuesta

14

No se puede reemplazar las variables en Java, por lo tanto, que en realidad tienen dos variables de a - uno en Foo y uno en Bar. Por otro lado, el método addFive() es polimórfico, por lo que modifica Bar.a (se llama Bar.addFive(), a pesar de que el tipo estático de f es Foo).

Pero al final accede a f.a y esta referencia se resuelve durante la compilación utilizando el tipo conocido de f, que es Foo. Y por lo tanto, Foo.a nunca fue tocado.

BTW variables no finales en Java debería nunca ser público.

+4

Bueno, excepto si tiene 'GridBagConstraints' u objetos similares. Pero para un principiante, ** nunca ** es un consejo apropiado :) –

18

F es una referencia de tipo Foo, y las variables no son polimórficos por lo f.a se refiere a la variable de Foo cuales es 3

Cómo verificarlo?

Para probar esto se puede quitar a variable a partir de Foo, se le dará compila error de tiempo

Nota: variable miembro Hacer private y utilizar métodos de acceso para acceder a ellos


también Ver

+10

Para extender esta respuesta un poco, esta es una de las razones por las que a menudo es más seguro usar getters en lugar de acceder directamente a las variables. –

+0

@DaveNewton por favor dime algo sobre getters? no he escuchado esto antes –

+2

Este problema podría evitarse silenciosamente si se usa una práctica de programación común en Java: las variables deben ser privadas (solo la propia clase puede usarlas). Después de eso, como se dijo anteriormente, usando los métodos getters y setters para acceder/modificarlo. – axcdnt

1

Dado que usted está haciendo f.a obtendrá el valor de a de la clase Foo. si hubiera llamado a un método para obtener el valor, por ejemplo, getA(), habría obtenido el valor de la clase Bar.

11

Con esta pregunta, el examen SCJP evalúa su conocimiento de lo que se conoce como que oculta. El examinador complicó deliberadamente las cosas para tratar de hacerle creer que el comportamiento del programa depende solo del polimorfismo, lo cual no es cierto.

Tratemos de aclarar un poco las cosas al eliminar el método 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); 
    } 
} 

Ahora las cosas son un poco menos confusas.El método main declara una variable de tipo Foo, a la que se le asigna un objeto de tipo Bar en tiempo de ejecución. Esto es posible ya que Bar hereda de Foo. El programa se refiere al campo público a de la variable de tipo Foo.

El error aquí sería creer que el mismo tipo de concepto conocido como anulando se aplica a los campos de clase. Pero no hay tal concepto para los campos: el campo público a de clase Bar no primordial es el campo público a de clase Foo pero sí lo que se llama ocultar. Como su nombre lo indica, significa que en el ámbito de la clase Bar, a se referirá al campo propio de Bar que no tiene nada que ver con el de Foo. (JLS 8.4.8 - Inheritance, Overriding, and Hiding)

Entonces, cuando estamos escribiendo f.a, ¿a qué a nos referimos? Recuerde que la resolución del campo a se realiza en compilando el tiempo usando el tipo de declaración del objeto f, que es Foo. Como consecuencia, el programa imprime '3'.

Ahora, agreguemos un método addFive() en la clase Foo y anule en la clase Bar como en la pregunta del examen. Aquí se aplica el polimorfismo, por lo tanto, la llamada f.addFive() se resuelve utilizando no el tiempo de compilación, pero el tipo de tiempo de ejecución del objeto f, que es Bar, y por lo tanto se imprime 'b'.

Pero todavía hay algo que debemos entender: ¿por qué el campo a, que se incrementó en 5 unidades, todavía se adhiere al valor '3'? Aquí ocultando está jugando. Debido a que este es el método de la clase Bar que se llama, y ​​porque en la clase Bar, cada a se refiere al campo público Bara, este es realmente el campo Bar que se incrementa.

1) Ahora, una pregunta subsidiaria: cómo podríamos acceder ámbito público de la Bar 's a del método main? Podemos hacer eso con algo como:

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

que fuerzan el compilador para resolver el miembro campo a de f como Bar 's a campo.

Esto imprimiría 'b 13' en nuestro ejemplo.

2) Sin embargo, otra pregunta: ¿Cómo podríamos evitar escondido en addFive() método de la clase Bar para referirse no a a campo de la Bar 's, pero a su campo eponimous superclase?Simplemente añadiendo la palabra clave super frente a la referencia de campo hace el truco:

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

Esto imprimiría 'b 8' en nuestro ejemplo.

Tenga en cuenta que la declaración inicial

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

podría ser refinado para

porque cuando el compilador está resolviendo el campo a, comenzará a buscar en el ámbito de inclusión más cercano dentro de la método addFive(), y encuentre la instancia de clase Bar, eliminando la necesidad de utilizar explícitamente this.

Pero, bueno, this fue probablemente una pista para que el examinado resolviera esta pregunta del examen.

+0

buena explicación Alex – Pratswinz

Cuestiones relacionadas