Tengo un programa como este:
class Test {
final int x;
{
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
Si intento ejecutarlo, variable x might not have been initialized
obtengo un error del compilador como: basado en los valores predeterminados de Java, ¿debería obtener el siguiente resultado correcto?
"Here x is 0".
¿Las variables finales tendrán valores predeterminados?
si cambio mi código así,
class Test {
final int x;
{
printX();
x = 7;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
Estoy obteniendo resultados como:
Here x is 0
Here x is 7
const called
¿Alguien puede explicar este comportamiento?
JLS está diciendo que usted debe asignar el valor por defecto a la variable de instancia en blanco en el constructor (o en el bloque de inicialización que es bastante lo mismo). Por eso aparece el error en el primer caso. Sin embargo, no dice que no pueda acceder a él en el constructor antes. Parece un poco extraño, pero puede acceder a él antes de la asignación y ver el valor predeterminado para int - 0.
UPD. Como lo menciona @ I4mpi, JLS define la regla de que cada valor debe asignarse definitivamente antes de cualquier acceso:
Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.
Sin embargo, también tiene una regla interesante en lo que respecta a constructores y campos:
If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.
Entonces, en el segundo caso, el valor
x
se asigna definitivamente al comienzo del constructor, porque contiene la asignación al final.fuente
final
campo, y si ese código puede ejecutarse tanto antes como después de que se escriba el campo, un compilador en el caso general no tendría forma de sabiendo si alguna vez leería el campo antes de que fuera escrito.Si no inicializa
x
, obtendrá un error en tiempo de compilación yax
que nunca se inicializa.Declarar
x
como final significa que se puede inicializar solo en el constructor o en el bloque inicializador (ya que el compilador copiará este bloque en cada constructor).La razón por la que se
0
imprime antes de que se inicialice la variable se debe al comportamiento definido en el manual (consulte la sección "Valores predeterminados"):Valores predeterminados
Data Type Default Value (for fields) -------------------------------------- byte 0 short 0 int 0 long 0L float 0.0f double 0.0d char '\u0000' String (or any object) null boolean false
fuente
El primer error es que el compilador se queja de que tiene un campo final, pero no hay código para inicializarlo, bastante simple.
En el segundo ejemplo, tiene código para asignarle un valor, pero la secuencia de ejecución significa que hace referencia al campo tanto antes como después de asignarlo.
El valor preasignado de cualquier campo es el valor predeterminado.
fuente
Todos los campos no finales de una clase se inicializan con un valor predeterminado (
0
para tipos de datos numéricos,false
para tipos booleanos ynull
de referencia, a veces llamados objetos complejos). Estos campos se inicializan antes de que un constructor (o bloque de inicialización de instancia) se ejecute independientemente de si los campos se declararon antes o después del constructor.Los campos finales de una clase no tienen un valor predeterminado y deben inicializarse explícitamente solo una vez antes de que un constructor de clases haya terminado su trabajo.
Las variables locales en el interior de un bloque de ejecución (por ejemplo, un método) no tienen un valor predeterminado. Estos campos deben inicializarse explícitamente antes de su primer uso y no importa si la variable local está marcada como final o no.
fuente
Déjame expresarlo con las palabras más simples que pueda.
final
las variables deben inicializarse, esto es obligatorio por la Especificación de idioma. Dicho esto, tenga en cuenta que no es necesario inicializarlo en el momento de la declaración.Es necesario inicializar eso antes de que se inicialice el objeto.
Podemos usar bloques inicializadores para inicializar las variables finales. Ahora, los bloques inicializadores son de dos tipos
static
ynon-static
El bloque que usó es un bloque inicializador no estático. Entonces, cuando crea un objeto, Runtime invocará al constructor y el cual, a su vez, invocará al constructor de la clase principal.
Después de eso, invocará a todos los inicializadores (en su caso, el inicializador no estático).
En su pregunta, caso 1 : incluso después de completar el bloque inicializador, la variable final permanece sin inicializar, lo que es un error que el compilador detectará.
En el caso 2 : el inicializador inicializará la variable final, por lo que el compilador sabe que antes de que se inicialice el objeto, la final ya está inicializada. Por tanto, no se quejará.
Ahora la pregunta es, ¿por qué
x
toma un cero? La razón aquí es que el compilador ya sabe que no hay error y, por lo tanto, al invocar el método init, todos los finales se inicializarán a los valores predeterminados, y se establecerá un indicador que pueden cambiar en una declaración de asignación real similar ax=7
. Vea la invocación de inicio a continuación:fuente
Hasta donde yo sé, el compilador siempre inicializará las variables de clase a los valores predeterminados (incluso las variables finales). Por ejemplo, si inicializara un int a sí mismo, el int se establecería en su valor predeterminado de 0. Vea a continuación:
class Test { final int x; { printX(); x = this.x; printX(); } Test() { System.out.println("const called"); } void printX() { System.out.println("Here x is " + x); } public static void main(String[] args) { Test t = new Test(); } }
Lo anterior imprimiría lo siguiente:
Here x is 0 Here x is 0 const called
fuente
No. No está viendo ese resultado porque, en primer lugar, está obteniendo un error en tiempo de compilación. Las variables finales obtienen un valor predeterminado, pero la Especificación del lenguaje Java (JLS) requiere que las inicialice al final del constructor (LE: estoy incluyendo bloques de inicialización aquí); de lo contrario, obtendrá un error en tiempo de compilación que evitará que su código sea compilado y ejecutado.
Su segundo ejemplo respeta el requisito, por eso (1) su código se compila y (2) obtiene el comportamiento esperado.
En el futuro, intente familiarizarse con JLS. No hay mejor fuente de información sobre el lenguaje Java.
fuente