2011-09-06 15 views
5
public class Base { 
public Base() { 
    x = 0; 
    bar(); 
} 

public Base(int x) { 
    this.x = x; 
    foo(); 
} 

public void foo() { 
    System.out.println("Base.foo : " + x); 
} 

private void bar() { 
    System.out.println("Base.bar:" + x.toString()); 
} 

protected Integer x; 
} 

    public class Derived extends Base { 
     public Derived() { 
     bar(); 
     } 
     public Derived(int x, int y) { 
     super(x); 
     this.y = y; 
     } 
     public void foo() { 
     System.out.println("Derived.foo : " + x + ", " + y); 
     } 
     public void bar() { 
     System.out.println("Derived.bar:" + x.toString() + ", " + y.toString()); 
     } 
     private Integer y; 


     public static void main(String[] args) { 
     Base b = new Derived(10, 20); 
     } 
} 

¿Por qué imprime "Derived.foo:" 10, nulll y no 20 en lugar de nulo? y es una variable privada de Derived, y se inicializó con 20. está en su alcance ... entonces ¿por qué es nulo?Java: ¿por qué imprime nulo?

Respuesta

7

Porque se llama primero al superconstructor (super(x)). Este super constructor llama al método foo. A continuación, el constructor Derivado inicializa y a 20 (this.y = y). Por eso, cuando se llama a foo, y aún no se ha inicializado.

Es una mala práctica llamar a métodos invalidantes en los constructores, porque, como acabas de notar, puede invocar métodos anulados en objetos parcialmente construidos.

+0

+1. Bien explicado. – Mikaveli

+2

+1, especialmente para advertencias de mala práctica: _puede llamar a métodos anulados en objetos parcialmente construidos_. –

2

El println proviene de foo método que se llama de 's constructor, que se llama a partir Derived' Base constructor s (a través de super) antes de inicializar y. Entonces el valor nulo es esperado.

1

Cuando ingresa al constructor de la superclase y aún no ha creado una instancia. Entonces, la llamada al foo() tendrá un nully.

2

Lo hace porque el constructor de la superclase (Base) llama al Derived.foo() antes de que se haya establecido la variable de miembro y.

Así que aquí es el orden básico de las operaciones:

main(...) 
Derived(10,20) (start constructor) 
Base(10) (start constructor) 
this.x = x 
foo() (calls Derived.foo() which prints the message you see) 

Luego, después de que

this.y = y 
1

Se imprime por Derived.foo() provocada por la llamada Super-Constructor antes de que se inicializa con 20 después. Por lo tanto, al imprimir, sigue siendo nulo.

1

Todas las respuestas son correctas, pero la próxima vez puede poner un punto de interrupción en la función donde está imprimiendo algo. Luego puede simplemente examinar la pila de llamadas y seguir el código leyendo las partes que están siendo llamadas.

De esta manera podría haber rastreado que y no se inicializaría.

¡Buena suerte! Roel

+0

Necesito aprender cómo hacer eso, ¡Gracias! – Numerator

2

Si se siguen las llamadas del constructor que hace que sea más claro:

  1. Llama al Derived(int x, int y) constructor
  2. llama al constructor súper Base(int x)
  3. Establece el x variables
  4. Las llamadas al anulado foo() método
  5. Realiza el println()
  6. Entonces establece la variable de y

Como se puede ver, la variable y se está configurado después de intentar imprimir el valor. Es por eso que está viendo el valor no inicializado de null.

Llamar a métodos invalidables desde un constructor es un error común, ya que puede permitir que las variables no inicializadas escapen antes de que el objeto esté completamente construido.

1

porque y no se ha inicializado cuando se llama a foo() en el constructor de la clase Base.

cadena va principal -> Derivado (x, y) -> Base (x) -> inicializar x, foo(). Sin embargo, como está iniciando la llamada dentro de Derived, que anula a foo(), foo() derivado es ejecutado por el intérprete.

Cuestiones relacionadas