在 Java 中,是否可以知道类是否已经加载?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/482633/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In Java, is it possible to know whether a class has already been loaded?
提问by Hosam Aly
Is it possible to know whether a Java class has been loaded, withoutattempting to load it? Class.forName
attempts to load the class, but I don't want this side effect. Is there another way?
是否可以在不尝试加载的情况下知道 Java 类是否已加载?Class.forName
尝试加载类,但我不想要这种副作用。还有其他方法吗?
(I don't want to override the class loader. I'm looking for a relatively simple method.)
(我不想覆盖类加载器。我正在寻找一种相对简单的方法。)
采纳答案by Stephen Denne
(Thanks to Aleksi) This code:
(感谢 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");
}
}
}
Produces:
产生:
false Loading TestLoaded$ClassToTest Loaded true
false Loading TestLoaded$ClassToTest Loaded true
Note that the example classes are not in a package. The full binary nameis required.
请注意,示例类不在包中。需要完整的二进制名称。
An example of a binary name is "java.security.KeyStore$Builder$FileBuilder$1"
二进制名称的一个例子是 "java.security.KeyStore$Builder$FileBuilder$1"
回答by Aleksi Yrttiaho
You can use the findLoadedClass(String)method in ClassLoader. It returns null if the class is not loaded.
您可以在 ClassLoader 中使用findLoadedClass(String)方法。如果类未加载,则返回 null。
回答by Stephen Denne
Ifyou're in control of the source of the classes for which you are interested in whether they are loaded or not (which I doubt, but you don't state in your question), then you could register your load in a static initializer.
如果您控制着您感兴趣的类的源是否已加载(我对此表示怀疑,但您没有在问题中说明),那么您可以在静态初始化程序中注册您的负载.
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");
}
}
}
Output:
输出:
false Loading Loaded true
false Loading Loaded true
回答by McDowell
One way to do this would be to write a Java agent using the instrumentation API. This would allow you to record the loading of classes by the JVM.
一种方法是使用检测 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;
}
}
META-INF/MANIFEST.MF:
META-INF/MANIFEST.MF:
Manifest-Version: 1.0
Premain-Class: myinstrument.ClassLoadedAgent
The downside is that you have to load the agent when you start the JVM:
缺点是你必须在启动 JVM 时加载代理:
java -javaagent:myagent.jar ....etcetera
回答by Eric Van Bezooijen
I had a similar problem recently, where I suspected that classes were being loaded (presumably by way of -classpath or something similar) by my users that conflicted with classes I was loading later on into my own classloader.
我最近遇到了类似的问题,我怀疑我的用户正在加载类(大概是通过 -classpath 或类似的方式),这些类与我稍后加载到我自己的类加载器中的类相冲突。
After trying a few of the things mentioned here, the following seemed to do the trick for me. I'm not sure if it works for every circumstance, it may only work for java classes loaded from jar files.
在尝试了这里提到的一些事情之后,以下内容似乎对我有用。我不确定它是否适用于所有情况,它可能只适用于从 jar 文件加载的 java 类。
InputStream is = getResourceAsStream(name);
Where name
is the path to the class file such as com/blah/blah/blah/foo.class
.
name
类文件的路径在哪里,例如com/blah/blah/blah/foo.class
.
getResourceAsStream
returned null
when the class had not been loaded into my class loader, or the system class loader, and returned non-null when the class had already been loaded.
getResourceAsStream
回来null
上课的时候没有被加载到我的类加载器或系统类加载器,当已经加载的类返回非空。