2011-05-10 31 views
12

Estoy experimentando con el uso de diferentes cargadores de clases para cargar una clase en particular, y ver si las variables estáticas en esa clase pueden tener instancias diferentes.Múltiples instancias de variables estáticas

Básicamente, estoy tratando de escribir el código de lo que Stephen C ha mencionado en this answer.

Éstos son mis clases:

CustomClassLoader.java

class CustomClassLoader extends ClassLoader 
{ 
    public Class loadClass(String classname) throws ClassNotFoundException { 
     return super.loadClass(classname, true); 
    } 
} 

Test.java (que contiene el controlador)

class Test { 
     public static void main(String[] args) throws Exception { 
       CustomClassLoader c1 = new CustomClassLoader(); 
       CustomClassLoader c2 = new CustomClassLoader(); 
       Class m1, m2; 

       m1 = c1.loadClass("A"); 
       m2 = c2.loadClass("A"); 

       m1.getField("b").set(null, 10); 

       System.out.println(m1.getField("b").get(null)); 
       System.out.println(m2.getField("b").get(null)); 
     } 

} 

A.java (que contiene la variable estática)

class A { 
     public static int b = 5; 
} 

Cuando ejecuto la clase de prueba, me sale el siguiente resultado:

$ java Test 
10 
10 

que esperaba que la salida sea 10 y 5. ¿Cómo puedo hacer que el código crear dos instancias de mi variable estática?

Nota: Estoy haciendo esto solo para la experimentación y el aprendizaje, pero me gustaría saber si podría haber alguna aplicación en el mundo real de esto.

Respuesta

7

Parece que la clase "A" está siendo cargada por el cargador de clases principal, en lugar de su CustomClassLoader (porque llama a super.loadClass).

La siguiente enmienda no probada debería permitirle definir la clase "A" utilizando su propio cargador de clases (mientras delega todo lo demás en el cargador principal).

Disculpas por el horrible montaje donde supongo que el único inputStream.read() leerá todo! Pero con suerte puedes ver lo que quiero decir.

public Class loadClass(String classname) throws ClassNotFoundException { 
    if (classname.equals("A")) { 
     InputStream is = getResourceAsStream("A.class"); 
     byte[] bodge = new byte[8192]; // Should read until EOF 
     try { 
      int len = is.read(bodge); 
      return defineClass("A", bodge, 0, len); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    return super.loadClass(classname, true); 
} 

Es probable que luego termina con ClasscastExceptions o algo similar ...

+0

Gracias, Paul. Intentaré cargar las clases sin llamar a 'super.loadClass()'. ¿Podría recomendar algún buen recurso que lo explique? – AbdullahC

+0

He actualizado mi respuesta arriba. Me temo que no conozco ningún buen tutorial aparte de http://www.javaworld.com/javaworld/jw-10-1996/jw-10-indepth.html. –

+0

Tu código se compila perfectamente, pero obtengo la siguiente excepción en las líneas que establecen u obtienen 'b':' Excepción en el hilo "principal" java.lang.IllegalAccessException: Class Test no puede acceder a un miembro de la clase A con modificadores "public estático "'. – AbdullahC

6

Su problema es que new CustomClassLoader() crea un cargador de clases que tratará de delegar las clases de carga para el cargador de clases del sistema - y que será lo mismo para ambas instancias. Su CustomClassLoader tampoco puede cargar las clases. Intente usar un URLClassLoader y pase null como padre.

En cuanto a las aplicaciones del mundo real: es esencial para los contenedores web Java y servidores de aplicaciones al permitir que diferentes aplicaciones estén completamente aisladas entre sí, aunque pueden estar utilizando muchas de las mismas clases.

+0

Gracias por su comentario adicional sobre las aplicaciones del mundo real. – AbdullahC

0

Si mira el origen de ClassLoader o incluso los javadocs, descubrirá que, de forma predeterminada, el cargador de clases delega en el sistema predeterminado ClassLoader, que de hecho se comparte entre las instancias.

+0

Ummm ... a menos que use un cargador de clases como URLClassLoader sin un cargador padre. Hago esto mucho para crear entornos de tiempo de ejecución aislados dentro de jvms (principalmente para pruebas). – Ajax

0

Tuve el mismo problema (pruebas de integración) y lo intenté con el enfoque de @Michael Borgwardt. Aquí un código de ejemplo:

URLClassLoader classLoader1 = new URLClassLoader(new URL[]{new URL("file:///path/to/jar/my-classes.jar")}, null); 
URLClassLoader classLoader2 = new URLClassLoader(new URL[]{new URL("file:///path/to/jar/my-classes.jar")}, null); 

// Load with classLoader1 
Class<?> myClass1 = classLoader1.loadClass("MyClass"); 
Constructor<?> constructor1 = myClass1.getConstructor(); 
Object instance1 = constructor1.newInstance(); 

// Load with classLoader2 
Class<?> myClass2 = classLoader2.loadClass("MyClass"); 
Constructor<?> constructor2 = myClass2.getConstructor(); 
Object instance2 = constructor2.newInstance(); 

// Load with system classloader 
MyClass myClass = new MyClass(); 

// ...