在Java中,是否可以知道是否已经加载了类?

在Java中,是否可以知道是否已经加载了类?,java,classloader,Java,Classloader,在不尝试加载Java类的情况下,是否可以知道是否已经加载了Java类Class.forName尝试加载该类,但我不希望出现这种副作用。还有别的办法吗 (我不想重写类加载器。我正在寻找一个相对简单的方法。)您可以在类加载器中使用该方法。如果未加载该类,则返回null。如果您控制着您感兴趣的类的源(我怀疑是否加载了这些类,但您在问题中没有说明),则可以在静态初始值设定项中注册您的加载 public class TestLoaded { public static boolean loaded

在不尝试加载Java类的情况下,是否可以知道是否已经加载了Java类
Class.forName
尝试加载该类,但我不希望出现这种副作用。还有别的办法吗


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

您可以在类加载器中使用该方法。如果未加载该类,则返回null。

如果您控制着您感兴趣的类的源(我怀疑是否加载了这些类,但您在问题中没有说明),则可以在静态初始值设定项中注册您的加载

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

(感谢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");
          }
     }
}
产生:

请注意,示例类不在包中。必须填写完整的表格


二进制名称的一个示例是“java.security.KeyStore$Builder$FileBuilder$1”

一种方法是使用。这将允许您记录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;
    }

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

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

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

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

InputStream is = getResourceAsStream(name);
其中
name
是类文件的路径,例如
com/blah/blah/blah/foo.class


getResourceAsStream
在类未加载到我的类加载器或系统类加载器时返回
null
,在类已加载时返回非null。

不幸的是,findLoadedClasses受到保护,这意味着您必须将ClassLoader子类化才能访问它。是否可以通过反射调用此方法?(禁止安全检查)
方法m=ClassLoader.class.getDeclaredMethod(“findLoadedClass”,新数组[]{String.class});m、 setAccessible(true);m、 调用(t,“Stirng”)谢谢。对于我自己的课程来说,这确实是一个很好的方法。但我希望找到更通用的东西,以防我无法控制类。你把类加载和初始化搞混了。当您执行类似于
objecto=ClassToTest.class的操作时然后类被加载但未初始化。我猜,OP不在乎;我只是想说清楚。只是附带说明一下:记住在调用方法时使用规范名称package.subpackage.ClassName。此答案没有说明此要求,因为该类不在包中。感谢添加此注释。虽然您确实需要这个包,但需要的是“二进制名称”。示例中显示的类的规范名称是“TestLoaded.ClassToTest”,它不同于二进制名称。。。我将编辑答案以澄清/链接。谢谢@spdenne和@Aleksi。我曾尝试过这样做,但失败了,尽管我不知道为什么。你上面的代码对我有用。作为补充说明,您可能需要查看McDowell关于仪器的回复。非常感谢。Thread.currentThread().getClassLoader()应用于获取当前类加载器。并非所有类都是由系统类加载器加载的。这将在没有permit-invalike-access的情况下停止使用java平台模块系统。谢谢!这是我第一次接触到仪器API。那么你建议用哪种方式呢?类加载器上的指令插入或反射。findLoadedClass?使用最适合您的应用程序的指令插入或反射。我不希望两者都能在100%的情况下工作。您能解释一下清单文件中的
myinstrument
是什么意思吗?这将检查类资源是否在类路径上(尽管缺少前导的
/
)。它不会检查该类是否由当前类装入器装入
Manifest-Version: 1.0 
Premain-Class: myinstrument.ClassLoadedAgent
java -javaagent:myagent.jar ....etcetera
InputStream is = getResourceAsStream(name);