atributo estático final privado vs atributo final privado
En Java, ¿cuál es la diferencia entre:
private final static int NUMBER = 10;
y
private final int NUMBER = 10;
Ambos son private
y final
, la diferencia es el static
atributo.
¿Que es mejor? ¿Y por qué?
En general, static
significa "asociado con el tipo en sí, en lugar de una instancia del tipo".
Eso significa que puede hacer referencia a una variable estática sin haber creado nunca una instancia del tipo, y cualquier código que haga referencia a la variable se refiere exactamente a los mismos datos. Compare esto con una variable de instancia: en ese caso, hay una versión independiente de la variable por instancia de la clase. Así por ejemplo:
Test x = new Test();
Test y = new Test();
x.instanceVariable = 10;
y.instanceVariable = 20;
System.out.println(x.instanceVariable);
imprime 10: y.instanceVariable
y x.instanceVariable
están separados, porque x
y y
se refieren a objetos diferentes.
Puede hacer referencia a miembros estáticos mediante referencias, aunque es una mala idea hacerlo. Si lo hiciéramos:
Test x = new Test();
Test y = new Test();
x.staticVariable = 10;
y.staticVariable = 20;
System.out.println(x.staticVariable);
entonces eso imprimiría 20; solo hay una variable, no una por instancia. Hubiera sido más claro escribir esto como:
Test x = new Test();
Test y = new Test();
Test.staticVariable = 10;
Test.staticVariable = 20;
System.out.println(Test.staticVariable);
Eso hace que el comportamiento sea mucho más obvio. Los IDE modernos normalmente sugerirán cambiar la segunda lista por la tercera.
No hay razón para tener una declaración en línea que inicialice el valor como la siguiente, ya que cada instancia tendrá el suyo propio NUMBER
pero siempre con el mismo valor (es inmutable y se inicializa con un literal). Esto es lo mismo que tener una sola final static
variable para todas las instancias.
private final int NUMBER = 10;
Por lo tanto, si no puede cambiar, no tiene sentido tener una copia por instancia.
Pero tiene sentido si se inicializa en un constructor como este:
// No initialization when is declared
private final int number;
public MyClass(int n) {
// The variable can be assigned in the constructor, but then
// not modified later.
number = n;
}
Ahora, para cada instancia de MyClass
, podemos tener un valor diferente pero inmutable de number
.
Una static
variable permanece en la memoria durante toda la vida útil de la aplicación y se inicializa durante la carga de la clase. Una no static
variable se inicializa cada vez que se construye un new
objeto. Generalmente es mejor usar:
private static final int NUMBER = 10;
¿Por qué? Esto reduce la huella de memoria por instancia. Posiblemente también sea favorable para los aciertos de caché. Y simplemente tiene sentido: static
debe usarse para cosas que se comparten entre todas las instancias (también conocidas como objetos) de un determinado tipo (también conocido como class
).
Para final , se le pueden asignar diferentes valores en tiempo de ejecución cuando se inicializa. Por ejemplo
class Test{
public final int a;
}
Test t1 = new Test();
t1.a = 10;
Test t2 = new Test();
t2.a = 20; //fixed
Por lo tanto, cada instancia tiene un valor diferente de campo a .
Para static final , todas las instancias comparten el mismo valor y no se pueden modificar después de la primera inicialización.
class TestStatic{
public static final int a = 0;
}
TestStatic t1 = new TestStatic();
t1.a = 10; // ERROR, CAN'T BE ALTERED AFTER THE FIRST
TestStatic t2 = new TestStatic();
t1.a = 20; // ERROR, CAN'T BE ALTERED AFTER THE FIRST INITIALIZATION.
estático significa "asociado con la clase"; sin él, la variable se asocia con cada instancia de la clase. Si es estático, significa que sólo tendrás uno en la memoria; si no, tendrás uno para cada instancia que crees. estática significa que la variable permanecerá en la memoria mientras la clase esté cargada; sin él, la variable se puede registrar cuando su instancia lo es.
Al leer las respuestas, no encontré ninguna prueba real que vaya al grano. Aquí están mis 2 centavos:
public class ConstTest
{
private final int value = 10;
private static final int valueStatic = 20;
private final File valueObject = new File("");
private static final File valueObjectStatic = new File("");
public void printAddresses() {
System.out.println("final int address " +
ObjectUtils.identityToString(value));
System.out.println("final static int address " +
ObjectUtils.identityToString(valueStatic));
System.out.println("final file address " +
ObjectUtils.identityToString(valueObject));
System.out.println("final static file address " +
ObjectUtils.identityToString(valueObjectStatic));
}
public static void main(final String args[]) {
final ConstTest firstObj = new ConstTest();
final ConstTest sndObj = new ConstTest();
firstObj.printAdresses();
sndObj.printAdresses();
}
}
Resultados para el primer objeto:
final int address java.lang.Integer@6d9efb05
final static int address java.lang.Integer@60723d7c
final file address java.io.File@6c22c95b
final static file address java.io.File@5fd1acd3
Resultados para el segundo objeto:
final int address java.lang.Integer@6d9efb05
final static int address java.lang.Integer@60723d7c
final file address java.io.File@3ea981ca
final static file address java.io.File@5fd1acd3
Conclusión :
Como pensaba, Java hace una diferencia entre los tipos primitivos y otros. Los tipos primitivos en Java siempre están "almacenados en caché", lo mismo para las cadenas literales (no para los nuevos objetos String), por lo que no hay diferencia entre miembros estáticos y no estáticos.
Sin embargo, existe una duplicación de memoria para miembros no estáticos si no son instancias de un tipo primitivo.
Cambiar el valor de valueStatic a 10 irá aún más lejos ya que Java dará las mismas direcciones a las dos variables int.