2012-03-27 14 views
30

tengo una clase, Super:¿Cuáles son las reglas que dictan la herencia de variables estáticas en Java?

public class Super { 
    public static String foo = "foo"; 
} 

También tengo otra clase, que se extiende SubSuper:

public class Sub extends Super { 
    static { 
     foo = "bar"; 
    } 

    public static void main (String[] args) { 
     System.out.println(Super.foo); 
    } 
} 

Cuando lo ejecuto, imprime bar.
Mi tercer (y último) clase es Testing:

public class Testing { 
    public static void main (String[] args) { 
     System.out.println(Super.foo); 
     System.out.println(Sub.foo); 
     System.out.println(Super.foo); 
    } 
} 

Esta impresora:

foo 
foo 
foo 

No entiendo por qué el contenido de foo varían dependiendo de la clase en la que está accediendo a ella desde . ¿Alguien puede explicar?

+2

Lo que quise decir con esa afirmación es que cuando accedo a 'Testing' me devuelve algo diferente que cuando accedo a él desde' Sub'. – jmgrosen

+0

'@jmgrosen:' Ah, contigo ahora. –

+3

FWIW, tenga en cuenta la importante distinción entre lo que tiene arriba y lo que tendría si 'Sub' contenía' static static String foo = "bar"; '(con lo cual obtiene" foo "," bar "," foo "como usted probablemente espera). –

Respuesta

33

No entiendo por qué los contenidos de foo varían según la clase a la que está accediendo.

Básicamente es una cuestión de inicialización de tipo. El valor de foo se establece en "bar" cuando se inicializa Sub. Sin embargo, en su clase Testing, la referencia a Sub.foo se compila en realidad en una referencia a Super.foo, por lo que no termina inicializando Sub, por lo que foo nunca se convierte en "bar".

Si cambia de código de prueba a:

public class Testing { 
    public static void main (String[] args) { 
     Sub.main(args); 
     System.out.println(Super.foo); 
     System.out.println(Sub.foo); 
     System.out.println(Super.foo); 
    } 
} 

Luego se imprimiría "barra" cuatro veces, debido a que la primera declaración obligaría Sub que ser inicializado, lo que cambiaría el valor de foo. No se trata de dónde se accede en absoluto.

+2

Esto parece correcto, pero ¿sabes por qué 'Sub.foo' está compilado en' Super.foo'? – jmgrosen

+4

@jmgrosen: Sí, porque solo hay una variable, declarada por 'Super'. Como dice maerics, es accesible "a través de" Sub ", pero eso no significa que sea una variable diferente. –

+0

es el mismo caso para los miembros finales también –

27

Las variables estáticas en Java no se heredan, solo existen en la clase que las declara; sin embargo, pueden ser accedidos implícitamente al referirse a una instancia o subclase (o instancia de subclase) de la clase que define la variable estática. (Manipulación de la variable estática es una de las pocas partes confusas del lenguaje Java, en mi humilde opinión.)

En el código de ejemplo, la clase Sub tiene un inicializador estático que cambia el valor de Super.foo (a pesar de que parecía como si cambia su propia instancia heredada de la variable "foo"). Sin embargo, la clase de prueba no carga realmente la clase Sub (¿quizás mediante algún truco de compilación?) Por lo que no ejecuta el inicializador estático, por lo que el valor de "foo" nunca se cambia.

+1

. Todo este acceso implícito y la conversión implícita es malo para el aprendizaje. ** PSA: ** tiene una opción activar codificación y compilación explícita solamente. – NoName

0

Los miembros estáticos no se heredan en Java ya que son las propiedades de la clase y se cargan en el área de clase. no tenían nada que ver con la creación de objetos. sin embargo, solo las clases secundarias pueden acceder a los miembros estáticos de sus clases principales.

0

No importa dónde cambie el valor de una variable estática, es la misma variable foo, en Sub o Super.No importa cuántos objetos new Sub o new Super cree y modifique. El cambio se ve en todas partes, desde Super.foo, Sub.foo, obj.foo comparten la misma pieza de storage.This trabaja con tipos primitivos también: la

class StaticVariable{ 
     public static void main(String[] args){ 
      System.out.println("StaticParent.a = " + StaticParent.a);// a = 2 
      System.out.println("StaticChild.a = " + StaticChild.a);// a = 2 

      StaticParent sp = new StaticParent(); 
      System.out.println("StaticParent sp = new StaticParent(); sp.a = " + sp.a);// a = 2 

      StaticChild sc = new StaticChild(); 
      System.out.println(sc.a);// a = 5 
      System.out.println(sp.a);// a = 5 
      System.out.println(StaticParent.a);// a = 5 
      System.out.println(StaticChild.a);// a = 5 
      sp.increment();//result would be the same if we use StaticParent.increment(); or StaticChild.increment(); 
      System.out.println(sp.a);// a = 6 
      System.out.println(sc.a);// a = 6 
      System.out.println(StaticParent.a);// a = 6 
      System.out.println(StaticChild.a);// a = 6 
      sc.increment(); 
      System.out.println(sc.a);// a = 7 
      System.out.println(sp.a);// a = 7 
      System.out.println(StaticParent.a);// a = 7 
      System.out.println(StaticChild.a);// a = 7 
     } 
} 
class StaticParent{ 
     static int a = 2; 
     static void increment(){ 
      a++; 
     } 
} 
class StaticChild extends StaticParent{ 
     static { a = 5;} 
} 

Se puede hacer referencia a una variable/método estático a través del objeto (como sc.a) o a través de su nombre de clase (como StaticParent.a). Es preferible usar ClassName.staticVariable para enfatizar la naturaleza estática de la variable/método y para dar al compilador mejores oportunidades de optimización.

Cuestiones relacionadas