setborder borderfactory java order final

java - setborder - borderfactory



Comportamiento extraño de Java con calificadores estáticos y finales (4)

Estos son los pasos tomados cuando ejecuta su programa:

  1. Antes de poder ejecutar main , la clase Test debe inicializarse ejecutando inicializadores estáticos en orden de aparición.
  2. Para inicializar el campo me , comience a ejecutar la new Test() .
  3. Imprime el valor de I Como el tipo de campo es Integer , lo que parece una constante de tiempo de compilación 4 convierte en un valor calculado ( Integer.valueOf(4) ). El inicializador de este campo aún no se ha ejecutado, imprimiendo el valor inicial null .
  4. Imprime el valor de S Dado que se inicializa con una constante de tiempo de compilación, este valor se incorpora al sitio de referencia, imprimiendo abc .
  5. new Test() , ahora se ejecuta el inicializador para I

Lección: si confía en singletons estáticos inicializados con entusiasmo, coloque la declaración de singleton como la última declaración de campo estático, o recurra a un bloque de inicializador estático que se produce después de todas las demás declaraciones estáticas. Eso hará que la clase parezca totalmente inicializada en el código de construcción del singleton.

Esta pregunta ya tiene una respuesta aquí:

En nuestro equipo encontramos un comportamiento extraño en el que utilizamos calificadores static y final . Esta es nuestra clase de prueba:

public class Test { public static final Test me = new Test(); public static final Integer I = 4; public static final String S = "abc"; public Test() { System.out.println(I); System.out.println(S); } public static Test getInstance() { return me; } public static void main(String[] args) { Test.getInstance(); } }

Cuando ejecutamos el método main , obtenemos un resultado de:

null abc

Lo entendería si escribiera valores null ambas ocasiones, ya que el código de los miembros de la clase estática se ejecuta de arriba a abajo.

¿Alguien puede explicar por qué está ocurriendo este comportamiento?


Su Test compila en:

public class Test { public static final Test me; public static final Integer I; public static final String S = "abc"; static { me = new Test(); I = Integer.valueOf(4); } public Test() { System.out.println(I); System.out.println("abc"); } public static Test getInstance() { return me; } public static void main(String[] args) { Test.getInstance(); } }

Como puede ver, se llama al constructor de Test antes de I se inicialice. Es por eso que imprime "null" para I Si tuviera que cambiar el orden de declaración para me y para me , obtendría el resultado esperado porque se inicializaría antes de que se invoque el constructor. También puede cambiar el tipo de I de Integer a int .

Debido a que 4 necesita autoboxing (es decir, envuelto en un objeto Integer ), no es una constante de tiempo de compilación y es parte del bloque de inicializador estático. Sin embargo, si el tipo fuera int , el número 4 sería una constante de tiempo de compilación, por lo que no necesitaría ser inicializado explícitamente. Como "abc" es una constante de tiempo de compilación, el valor de S se imprime como se esperaba.

Si reemplazaras,

public static final String S = "abc";

con,

public static final String S = new String("abc");

Entonces notarías que la salida de S es "null" . ¿Por qué pasa eso? Por la misma razón por la I también genera "null" . Los campos como estos que tienen valores literales constantes (que no necesitan autoboxing, como String ) se atribuyen con el atributo "ConstantValue" cuando se compilan, lo que significa que su valor puede resolverse simplemente mirando el grupo constante de la clase, sin necesidad para ejecutar cualquier código.


Tiene un comportamiento extraño debido al tipo de datos Integer . Con respecto a JLS 12.4.2, los campos estáticos se inicializan en el orden en que lo escribe, PERO las constantes de tiempo de compilación se inicializan primero.

Si no usa el tipo de contenedor Integer sino el tipo int , obtendrá el comportamiento que desea.


S es una constante en tiempo de compilación, siguiendo las reglas de JLS 15.28 . Por lo tanto, cualquier aparición de S en el código se reemplaza con el valor que se conoce en tiempo de compilación.

Si cambia el tipo de I a int , también verá lo mismo para eso.