Aquí está mi código:
class A {
static A obj = new A();
static int num1;
static int num2=0;
private A() {
num1++;
num2++;
}
public static A getInstance() {
return obj;
}
}
public class Main{
public static void main(String[] arg) {
A obj = A.getInstance();
System.out.println(obj.num1);
System.out.println(obj.num2);
}
}
El resultado es 1 0
, pero no puedo entenderlo.
¿Alguien me lo puede explicar?
Respuestas:
En Java tienen lugar dos fases: 1. Identificación, 2. Ejecución
En la fase de identificación , todas las variables estáticas se detectan y se inicializan con valores predeterminados.
Entonces ahora los valores son:
A obj=null
num1=0
num2=0
La segunda fase, la ejecución , comienza de arriba a abajo. En Java, la ejecución comienza desde los primeros miembros estáticos.
Aquí está su primera variable estática
static A obj = new A();
, por lo que primero creará el objeto de esa variable y llamará al constructor, de ahí el valor denum1
y senum2
convierte1
.Y luego, nuevamente,
static int num2=0;
se ejecutará, lo que hacenum2 = 0;
.Ahora, suponga que su constructor es así:
Esto arrojará un
NullPointerException
comoobj
todavía no tiene una referenciaclass A
.fuente
static A obj = new A();
abajostatic int num2=0;
y debería obtener 1 y 1.A obj = new A(); int num1; int num2 = 0;
Obtiene convertido en esto:A obj; int num1; int num2; obj = new A(); num2 = 0;
. Java hace esto, por lo quenum1, num2
se define en el momento en que llega alnew A()
constructor.Lo que el
static
modificador significa cuando se aplica a una declaración de variable es que la variable es una variable de clase en lugar de una variable de instancia. En otras palabras ... solo hay unanum1
variable, y solo unanum2
variable.(Aparte: una variable estática es como una variable global en algunos otros idiomas, excepto que su nombre no es visible en todas partes. Incluso si se declara como a
public static
, el nombre no calificado solo es visible si se declara en la clase actual o una superclase , o si se importa mediante una importación estática. Esa es la distinción. Un verdadero global es visible sin calificación en cualquier lugar).Entonces, cuando se refiere a
obj.num1
yobj.num2
, en realidad se refiere a las variables estáticas cuyas designaciones reales sonA.num1
yA.num2
. Y de manera similar, cuando el constructor incrementanum1
ynum2
, está incrementando las mismas variables (respectivamente).La arruga confusa en su ejemplo está en la inicialización de la clase. Una clase se inicializa primero por defecto inicializando todas las variables estáticas y luego ejecutando los inicializadores estáticos declarados (y los bloques de inicializadores estáticos) en el orden en que aparecen en la clase. En este caso, tienes esto:
Sucede así:
Las estáticas comienzan con sus valores iniciales predeterminados;
A.obj
esnull
yA.num1
/A.num2
son cero.La primera declaración (
A.obj
) crea una instancia deA()
, y el constructor paraA
incrementosA.num1
yA.num2
. Cuando la declaración se completa,A.num1
yA.num2
son ambos1
, y seA.obj
refiere a laA
instancia recién construida .La segunda declaración (
A.num1
) no tiene inicializador, por loA.num1
que no cambia.La tercera declaración (
A.num2
) tiene un inicializador que asigna cero aA.num2
.Por lo tanto, al final de la inicialización de la clase,
A.num1
es1
yA.num2
es0
... y eso es lo que muestran sus declaraciones impresas.Este comportamiento confuso se debe en realidad al hecho de que está creando una instancia antes de que se complete la inicialización estática, y que el constructor que está utilizando depende y modifica una estática que aún no se ha inicializado. Esto es algo que debes evitar hacer en código real.
fuente
1,0 es correcto.
Cuando se carga la clase, todos los datos estáticos se inicializan o se declaran. Por defecto, int es 0.
static int num1;
no hace nadastatic int num2=0;
esto escribe 0 en num2fuente
Se debe al orden de los inicializadores estáticos. Las expresiones estáticas de las clases se evalúan en orden descendente.
El primero en ser llamado es el constructor de
A
, que establecenum1
ynum2
ambos en 1:static A obj = new A();
Luego,
se llama y establece num2 = 0 nuevamente.
Por eso
num1
es 1 ynum2
es 0.Como nota al margen, un constructor no debe modificar variables estáticas, eso es un diseño muy malo. En su lugar, pruebe un enfoque diferente para implementar un Singleton en Java .
fuente
Se puede encontrar una sección en JLS: §12.4.2 .
Entonces, las tres variables estáticas se inicializarán una por una en orden textual.
Entonces
Si cambio el orden a:
El resultado será
1,1
.Tenga en cuenta que
static int num1;
no es un inicializador de variable porque ( §8.3.2 ):Y esta variable de clase se inicializa cuando se crea la clase. Esto sucede primero ( §4.12.5 ).
fuente
Quizás ayude pensar en ello de esta manera.
Las clases son planos de objetos.
Los objetos pueden tener variables cuando se instancian.
Las clases también pueden tener variables. Estos se declaran estáticos. Por lo tanto, se establecen en la clase en lugar de en las instancias del objeto.
Solo puede tener uno de cualquier clase en una aplicación, por lo que es una especie de almacenamiento global específicamente para esa clase. Por supuesto, se puede acceder a estas variables estáticas y modificarlas desde cualquier lugar de su aplicación (suponiendo que sean públicas).
Este es un ejemplo de una clase "Perro" que usa una variable estática para rastrear el número de instancias que ha creado.
La clase "Perro" es la nube, mientras que las casillas naranjas son instancias de "Perro".
Lee mas
¡Espero que esto ayude!
Si te apetece una trivia, esta idea fue presentada por primera vez por Platón.
fuente
La palabra clave estática se utiliza en Java principalmente para la gestión de memoria. Podemos aplicar palabras clave estáticas con variables, métodos, bloques y clases anidadas. La palabra clave estática pertenece a la clase que a la instancia de la clase. Para una breve explicación sobre la palabra clave estática:
http://www.javatpoint.com/static-keyword-in-java
fuente
Muchas de las respuestas anteriores son correctas. Pero realmente para ilustrar lo que está sucediendo, he realizado algunas pequeñas modificaciones a continuación.
Como se mencionó varias veces anteriormente, lo que sucede es que se crea una instancia de la clase A antes de que la clase A esté completamente cargada. Por tanto, no se observa lo que se considera el "comportamiento" normal. Esto no es muy diferente a llamar a métodos desde un constructor que se puede anular. En ese caso, las variables de instancia pueden no estar en un estado intuitivo. En este ejemplo, las variables de clase no están en un estado intuitivo.
La salida es
fuente
java no inicializa el valor de ningún miembro de datos estático o no estático hasta que no se llama, pero lo crea.
de modo que aquí, cuando se llame num1 y num2 en main, se inicializará con valores
num1 = 0 + 1; y
num2 = 0;
fuente