Para hacer mi pregunta más clara, considere el siguiente caso de uso:
Supongamos que hay un paquete que permite un conjunto de operaciones en una plataforma determinada, por ejemplo, una clase para editar el registro en Windows. Este paquete no existe en otras plataformas, ya que no existe una operación equivalente en otro sistema operativo.
En aras de la simplicidad, considere
ventanas/Registro.java
package windows; public class Registry { static Registry instance = null; static{ System.out.println("print from static block"); } private Registry() { System.out.println("Registry instance created!"); } public static synchronized Registry getInstance() { if (null == instance) { instance = new Registry(); } return instance; } public void foo() { System.out.println("foo called."); } }y la clase donde voy a usar condicionalmente el registro: main/Main.java
package main; import windows.Registry; public class Main { public static void test1(boolean onWindows) { if (onWindows) { Registry instance = Registry.getInstance(); System.out.println("We are on Windows: "); instance.foo(); } else { System.out.println("We are somewhere else!"); } } public static void main(String[] args) { System.out.println("Entered main"); boolean onWindows = args.length > 0 ? Boolean.parseBoolean(args[0]) : false; test1(onWindows); } } La pregunta es, si no se ejecuta explícitamente ninguna función o clase en Main.class , ¿se garantiza que no se ejecute ningún código de Registry.class ?
Pude probar este ejemplo en múltiples plataformas de escritorio y con diferentes versiones de Java, pero me gustaría saber si este comportamiento está documentado y se puede confiar en él, si es el comportamiento esperado también en otras plataformas, como Android o incrustado. versión del JRE.
En caso de que no exista tal garantía, ya que un cargador de clases (quizás personalizado) podría decidir cargar todos los archivos .class en el classpath, ¿puedo suponer que el código funcionará sin java.lang.NoClassDefFoundError si onWindows es falso y elimino Registry.class ? Registry.class del classpath?
El comportamiento que he observado hasta ahora es
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main true Entered main print from static block Registry instance created! We are on Windows: foo called. rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main false Entered main We are somewhere else! rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main false Entered main We are somewhere else! rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main true Entered main Exception in thread "main" java.lang.NoClassDefFoundError: windows/Registry at main.Main.test1(Main.java:9) at main.Main.main(Main.java:20) Caused by: java.lang.ClassNotFoundException: windows.Registry at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ... 2 more ¿Están definidos esos comportamientos (cargador de clases perezoso y eliminación Registry.class )?
La pregunta es, si no se ejecuta explícitamente ninguna función o clase en
Main.class, ¿se garantiza que no se ejecute ningún código deRegistry.class?
Eso no afecta directamente a la carga de clases, sino a la inicialización de clases, que es el primer punto en el que se ejecuta cualquier código de la clase en cuestión. Específicamente, los bloques de inicialización estáticos y los inicializadores de miembros estáticos se ejecutan durante esta etapa. La clase en cuestión ya debe haberse cargado y verificado en este punto, pero podría haberse cargado una cantidad arbitraria de tiempo antes.
Según JLS 12.4.1 ,
Una clase o interfaz T se inicializará inmediatamente antes de la primera ocurrencia de cualquiera de los siguientes:
T es una clase y se crea una instancia de T.
Se invoca un método
staticdeclarado por T.Se asigna un campo
staticdeclarado por T.Se utiliza un campo
staticdeclarado por T y el campo no es una variable constante
Por lo tanto, si nunca crea una instancia de la clase o accede a cualquiera de sus métodos o campos estáticos (excepto para leer un campo estático que es una "variable constante"), nunca se ejecutará ningún código de la clase.
Pero que una clase no se inicialice no significa que no se intentará cargarla . El JLS no prohíbe que las implementaciones carguen clases de forma prospectiva. De hecho, JLS 12.2.1 dice específicamente:
un cargador de clases puede almacenar en caché representaciones binarias de clases e interfaces, obtenerlas previamente en función del uso esperado o cargar juntas un grupo de clases relacionadas .
Por lo tanto, no, no es seguro confiar en que la aplicación representada por su clase main.Main se ejecute sin java.lang.NoClassDefFoundError u otro error de carga cuando la clase windows.Registry no se puede cargar, independientemente de si se puede esperar en realidad. para ser utilizado. Sin embargo, puede , en las circunstancias adecuadas, confiar en que esa clase no se inicialice y, por lo tanto, no se ejecute ningún código.