tutorial example ejemplo java classloader

example - ¿El ClassLoader de Java carga clases internas?



classloader java example (5)

Si tengo una declaración de clase interna como:

Class A { public static class B { } }

seguido por:

Class<?> implClass = getClass().getClassLoader().loadClass("A");

¿Se cargará también la clase interna A $ B? ¿Qué pasa si la clase interna B no fue declarada como "estática"?


El siguiente código es ejecutable y puede ilustrar algunas de las otras respuestas:

public class Outer { private static final String TEST01 = "I''m TEST01"; static { System.out.println("1 - Initializing class Outer, where TEST01 = " + TEST01); } public static void main(String[] args) { System.out.println("2 - TEST01 --> " + TEST01 ); System.out.println("3 - Inner.class --> " + Inner.class); System.out.println("5 - Inner.info() --> " + Inner.info() ); } private static class Inner { static { System.out.println("4 - Initializing class Inner"); } public static String info() { return "I''m a method in Inner"; } } }

Por favor, preste atención a los números de secuencia, especialmente en esta línea:

System.out.println("5 - Inner.info() --> " + Inner.info() );

Cuando ejecute el programa, verá el siguiente resultado en la consola:

1 - Initializing class Outer, where TEST01 = I''m TEST01 2 - TEST01 --> I''m TEST01 3 - Inner.class --> class com.javasd.designpatterns.tests.Outer$Inner 4 - Initializing class Inner 5 - Inner.info() --> I''m a method in Inner

Un poco más de detalle para cada paso:

1 - ''Externo'' se inicializa cuando ejecuta el programa. La variable estática TEST01 se inicializa antes del bloque estático. El interior no está inicializado.

2 - Se llama al método ''main'' y muestra el valor de ''TEST01''; entonces,

3 - El System.out muestra la referencia a ''Interno''. El interior no está inicializado , pero se cargó (por eso tiene una referencia en el modelo de memoria).

4 - Aquí está la parte más interesante. Debido a que System.out necesita acceder al método ''info ()'' en ''Inner'' (Inner.info ()), la clase ''Inner'' debe inicializarse antes de devolver el resultado del método ''info ()''. Es por eso que este es el paso 4.

5 - Finalmente, System.out tiene todos los datos que necesita mostrar, y luego se muestra la última línea en la consola.

Por lo tanto, como lo señaló @ sotirios-delimanolis ( ¿El Java ClassLoader carga las clases internas? ) Cargar una clase es diferente de inicializarla.


Las clases internas, es decir, la class B , no pueden existir fuera de la clase principal. Necesitas construir la clase padre, es decir, la class A primero.

y si elimina la estática de su clase interna, es decir, para la clase interna no estática , debe pasar la clase principal durante la construcción de la clase interna.

Object a = Class.forName("A").newInstance(); //object of outer class //object of inner class Object b = implClass.getDeclaredConstructor(new Class[] { a.getClass() }) .newInstance(new Object[] { a });


No, en ningún caso se cargará la clase anidada.


Un ClassLoader no cargará una clase, a menos que se haya solicitado (por ejemplo, utilizando loadClass ). Mientras se carga una clase, un ClassLoader solicitará clases referenciadas.

Como su clase A no hace referencia a AB , AB no se cargará, sea estático o no. (Para ser honesto, A hace referencia a AB , pero no de una manera que haga que el ClassLoader cargue AB ).

Si agrega un campo de tipo AB o usa el tipo AB de otra manera (por ejemplo, como un tipo de retorno de método), en realidad se hará referencia en A.class y, por lo tanto, se cargará.


Una vez que se compila el código, no existe una clase interna . Si miras los resultados de javac , verás dos archivos:

A.class A$B.class

Así que la clase B no se carga cuando se carga A, B pasa a estar definido en A

Editar

Por ejemplo, dados estos dos archivos,

package kuporific; public class A { private static class B {} private class C {} }

y un archivo build.gradle (por conveniencia):

apply plugin: ''java''

En primer lugar, construir ejecutando gradle build . Luego, descomprima el archivo JAR resultante (ubicado en build/libs ):

├── META-INF │   └── MANIFEST.MF └── kuporific ├── A$B.class ├── A$C.class └── A.class

Al abrir cada archivo (en IntelliJ, por ejemplo), se revela lo que el compilador ha hecho:

  • A.class :

    package kuporific; public class A { public A() { } private class C { public C() { } } private static class B { public B() { } } }

  • A$B.class

    package kuporific; class A$B { private A$B() { } }

  • A$C.class

    package kuporific; import kuporific.A; class A$C { private A$C(A this$0) { this.this$0 = this$0; } }

Darse cuenta de

  1. A$B no tiene una referencia a su padre, A , mientras que A$C sí. Esto se debe a que la primera es una clase interna estática, y la segunda no lo es, y
  2. Tanto A$B como A$C ahora son clases privadas del paquete .

Así es como las clases internas no estáticas pueden hacer referencia directamente a los campos y métodos de su instancia principal, y viceversa. (Todos los campos privados de la clase padre a los que se hace referencia en una clase interna también se convierten en paquetes privados)

A continuación, veamos qué efecto tiene cargar la clase A en A$B y A$C

Primero, agregue la siguiente clase de Java:

package kuporific; public class Main { public static void main(String[] args) throws ClassNotFoundException { Main.class.getClassLoader().loadClass("kuporific.A"); } }

Ahora agregue lo siguiente al archivo build.gradle :

apply plugin: ''application'' mainClassName = ''kuporific.Main'' applicationDefaultJvmArgs = ["-verbose:class"]

La -verbose:class genera todas las clases cargadas por la JVM (vea Java - Obtenga una lista de todas las clases cargadas en la JVM ).

Ejecute gradle run en la línea de comando (que ejecuta el método main de Main ); La salida (con mis notas agregadas) es

:compileJava :processResources UP-TO-DATE :classes :run [Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] # Lots of omitted output... [Loaded kuporific.Main from file:/tmp/build/classes/main/] ^ here! [Loaded sun.launcher.LauncherHelper$FXHelper from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.Class$MethodArray from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded kuporific.A from file:/tmp/build/classes/main/] ^ here! [Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] BUILD SUCCESSFUL Total time: 6.502 secs

Podemos ver cuándo se kuporific.Main y kuporific.A , y no vemos kuporific.A$B o kuporific.A$C está cargando.