En Java, ¿es posible saber si una clase ya se ha cargado?

¿Es posible saber si se ha cargado una clase Java sin intentar cargarla? Class.forName intenta cargar la clase, pero no quiero este efecto secundario. ¿Hay otra manera?

(No quiero anular el cargador de clases. Estoy buscando un método relativamente simple).

(Gracias a Aleksi) Este código:

 public class TestLoaded { public static void main(String[] args) throws Exception { java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[] { String.class }); m.setAccessible(true); ClassLoader cl = ClassLoader.getSystemClassLoader(); Object test1 = m.invoke(cl, "TestLoaded$ClassToTest"); System.out.println(test1 != null); ClassToTest.reportLoaded(); Object test2 = m.invoke(cl, "TestLoaded$ClassToTest"); System.out.println(test2 != null); } static class ClassToTest { static { System.out.println("Loading " + ClassToTest.class.getName()); } static void reportLoaded() { System.out.println("Loaded"); } } } 

Produce:

 false Loading TestLoaded$ClassToTest Loaded true 

Tenga en cuenta que las clases de ejemplo no están en un paquete. El nombre binario completo es obligatorio.

Un ejemplo de un nombre binario es "java.security.KeyStore$Builder$FileBuilder$1"

Puede usar el método findLoadedClass (String) en ClassLoader. Devuelve null si la clase no está cargada.

Una forma de hacerlo sería escribir un agente de Java utilizando la API de instrumentación . Esto le permitiría registrar la carga de clases por parte de la JVM.

 public class ClassLoadedAgent implements ClassFileTransformer { private static ClassLoadedAgent AGENT = null; /** Agent "main" equivalent */ public static void premain(String agentArguments, Instrumentation instrumentation) { AGENT = new ClassLoadedAgent(); for (Class clazz : instrumentation.getAllLoadedClasses()) { AGENT.add(clazz); } instrumentation.addTransformer(AGENT); } private final Map> classMap = new WeakHashMap>(); private void add(Class clazz) { add(clazz.getClassLoader(), clazz.getName()); } private void add(ClassLoader loader, String className) { synchronized (classMap) { System.out.println("loaded: " + className); Set set = classMap.get(loader); if (set == null) { set = new HashSet(); classMap.put(loader, set); } set.add(className); } } private boolean isLoaded(String className, ClassLoader loader) { synchronized (classMap) { Set set = classMap.get(loader); if (set == null) { return false; } return set.contains(className); } } @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { add(loader, className); return classfileBuffer; } public static boolean isClassLoaded(String className, ClassLoader loader) { if (AGENT == null) { throw new IllegalStateException("Agent not initialized"); } if (loader == null || className == null) { throw new IllegalArgumentException(); } while (loader != null) { if (AGENT.isLoaded(className, loader)) { return true; } loader = loader.getParent(); } return false; } } 

META-INF / MANIFEST.MF:

 Manifest-Version: 1.0 Premain-Class: myinstrument.ClassLoadedAgent 

La desventaja es que debe cargar el agente cuando inicia la JVM:

 java -javaagent:myagent.jar ....etcetera 

Si usted tiene el control de la fuente de las clases que le interesan, ya sea que estén cargadas o no (lo cual dudo, pero no dice en su pregunta), entonces podría registrar su carga en un inicializador estático .

 public class TestLoaded { public static boolean loaded = false; public static void main(String[] args) throws ClassNotFoundException { System.out.println(loaded); ClassToTest.reportLoaded(); System.out.println(loaded); } static class ClassToTest { static { System.out.println("Loading"); TestLoaded.loaded = true; } static void reportLoaded() { System.out.println("Loaded"); } } } 

Salida:

 false Loading Loaded true 

Recientemente tuve un problema similar, donde sospechaba que las clases se estaban cargando (probablemente a través de -classpath o algo similar) por mis usuarios que entraban en conflicto con las clases que estaba cargando más adelante en mi propio cargador de clases.

Después de probar algunas de las cosas mencionadas aquí, lo siguiente pareció ser el truco para mí. No estoy seguro de si funciona para cualquier circunstancia, es posible que solo funcione para clases Java cargadas desde archivos jar.

 InputStream is = getResourceAsStream(name); 

Donde name es la ruta al archivo de clase como com/blah/blah/blah/foo.class .

getResourceAsStream devolvió null cuando la clase no se había cargado en mi cargador de clases, o el cargador de clases del sistema, y ​​devolvió el valor no nulo cuando la clase ya se había cargado.