45

是否可以在不尝试加载的情况下知道是否已加载 Java 类?Class.forName尝试加载类,但我不想要这种副作用。还有其他方法吗?

(我不想重写类加载器。我正在寻找一个相对简单的方法。)

4

5 回答 5

44

(感谢 Aleksi)这段代码:

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");
          }
     }
}

产生:

false
Loading TestLoaded$ClassToTest
Loaded
true

请注意,示例类不在包中。需要完整的二进制名称

二进制名称的一个例子是"java.security.KeyStore$Builder$FileBuilder$1"

于 2009-01-27T10:34:56.890 回答
29

您可以使用 ClassLoader 中的findLoadedClass(String)方法。如果类未加载,则返回 null。

于 2009-01-27T08:29:23.790 回答
6

一种方法是使用检测 API编写 Java 代理。这将允许您记录 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<ClassLoader, Set<String>> classMap = new WeakHashMap<ClassLoader, Set<String>>();

    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<String> set = classMap.get(loader);
            if (set == null) {
                set = new HashSet<String>();
                classMap.put(loader, set);
            }
            set.add(className);
        }
    }

    private boolean isLoaded(String className, ClassLoader loader) {
        synchronized (classMap) {
            Set<String> 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;
    }

}

元信息/清单.MF:

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

缺点是您必须在启动 JVM 时加载代理:

java -javaagent:myagent.jar ....etcetera
于 2009-01-27T12:00:02.693 回答
5

如果您可以控制您对是否加载它们感兴趣的类的源(我对此表示怀疑,但您没有在问题中说明),那么您可以在静态初始化程序中注册您的负载.

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");
        }
    }
}

输出:

false
Loading
Loaded
true
于 2009-01-27T10:09:53.073 回答
1

我最近遇到了类似的问题,我怀疑我的用户正在加载类(可能是通过 -classpath 或类似的方式),这些类与我稍后加载到我自己的类加载器中的类相冲突。

在尝试了这里提到的一些事情之后,以下似乎对我有用。我不确定它是否适用于所有情况,它可能只适用于从 jar 文件加载的 java 类。

InputStream is = getResourceAsStream(name);

类文件的路径在哪里name,例如com/blah/blah/blah/foo.class.

getResourceAsStream当类尚未加载到我的类加载器或系统类加载器中时返回null,当类已经加载时返回非空。

于 2012-10-02T01:03:59.703 回答