Java 使用 ClassLoader 和 Class.forName 加载类的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4285855/
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
Difference between Loading a class using ClassLoader and Class.forName
提问by Ebbu Abraham
Below are 2 code snippets
下面是2个代码片段
The first one uses ClassLoader class to load a specified class
第一个使用 ClassLoader 类加载指定的类
ClassLoader cls = ClassLoader.getSystemClassLoader();
Class someClass = cls.loadClass("TargetClass");
ClassLoader cls = ClassLoader.getSystemClassLoader();
Class someClass = cls.loadClass("TargetClass");
The second one uses Class.forName() to load a specified class
第二个使用 Class.forName() 加载指定的类
Class cls = Class.forName("TargetClass");
Class cls = Class.forName("TargetClass");
What is the difference between the above said approaches. Which one serves for which purpose?
上述方法之间有什么区别。哪个用于哪个目的?
采纳答案by Martijn Verburg
Quick answer (without code samples)
快速解答(不含代码示例)
With the explicit ClassLoader cls = <a ClassLoader>;
approach you have the flexibility of loading the class from a ClassLoader that is notyour default ClassLoader. In your case you're using the default System ClassLoader, so it gives the similar overall result (with an instantiation of the final object difference) as the Class.forName(String name)
call, but you couldreference another ClassLoader instead.
使用显式ClassLoader cls = <a ClassLoader>;
方法,您可以灵活地从不是默认 ClassLoader 的 ClassLoader 加载类。在您的情况下,您使用的是默认的 System ClassLoader,因此它提供了与Class.forName(String name)
调用类似的总体结果(带有最终对象差异的实例化),但您可以改为引用另一个 ClassLoader。
That said, you can also use Class.forName(String name, boolean initialize, ClassLoader loader)
as long as you know what that ClassLoader is.
也就是说,Class.forName(String name, boolean initialize, ClassLoader loader)
只要您知道 ClassLoader 是什么,您也可以使用。
For example, your EAR based application has its own ClassLoader with a version of an XML Parsing library wrapped inside of it. Your code normally uses those classes, but in one instance you need to grab a deserialisation class from an earlier version of the library (that the Application Server happens to be holding in itsoverall ClassLoader). So you could reference that Application Server ClassLoader instead.
例如,基于 EAR 的应用程序有自己的类加载器,其中包含一个 XML 解析库版本。您的代码通常使用这些类,但在一个实例中,您需要从库的早期版本(应用程序服务器恰好在其整个类加载器中保存)中获取反序列化类。因此,您可以改为引用该 Application Server ClassLoader。
Unfortunately until we get project Jigsaw (JDK 8) this is used more often than we'd like :-)
不幸的是,在我们获得 Jigsaw (JDK 8) 项目之前,它的使用频率比我们想要的要多:-)
回答by axtavt
ClassLoader.loadClass()
uses the specified classloader (the system classloader in your case), whereas Class.forName()
uses classloader of the current class.
ClassLoader.loadClass()
使用指定的类加载器(在您的情况下是系统类加载器),而Class.forName()
使用当前类的类加载器。
Class.forName()
can be used when you don't care about particular classloader and want the same classloading behaviour as for statically referenced classes.
Class.forName()
当您不关心特定的类加载器并希望与静态引用的类具有相同的类加载行为时,可以使用它。
回答by Neeme Praks
In your concrete case:
在您的具体情况下:
ClassLoader cls = ClassLoader.getSystemClassLoader();
Class someClass = cls.loadClass("TargetClass");
Above code will load TargetClass
ALWAYSwith system classloader.
上面的代码将TargetClass
始终使用系统类加载器加载。
Class cls = Class.forName("TargetClass");
The second code snippet will load (and initialise) TargetClass
with the classloader that was used to load the class that is executing that line of code. If that class was loaded with system classloader, the two approaches are identical (except for class initialisation, as explained in an excellent answer by Bruno).
第二代码段将加载(并初始化)TargetClass
用的是使用加载正在执行该代码行该类的类加载器。如果该类是使用系统类加载器加载的,则这两种方法是相同的(除了类初始化,如 Bruno 的出色回答中所述)。
Which one to use?For loading and inspecting classes with reflection, I recommend to use specific class loader (ClassLoader.loadClass()
) - it puts you in control and helps to avoid potentially obscure issues between different environments.
使用哪一种?对于使用反射加载和检查类,我建议使用特定的类加载器 ( ClassLoader.loadClass()
) - 它使您能够控制并帮助避免不同环境之间潜在的模糊问题。
If you need to load AND initialise, use Class.forName(String, true, ClassLoader)
.
如果您需要加载和初始化,请使用Class.forName(String, true, ClassLoader)
.
How to find the right class loader?It depends on your environment:
如何找到合适的类加载器?这取决于您的环境:
- if you are running a command-line application, you could just use system classloaderor the class loader that loaded your application classes (
Class.getClassLoader()
). - if you are running inside a managed environment(JavaEE, servlet container, etc) then the best would be to check current thread context class loaderfirst and then fall back to options given in previous point.
- or just use your own custom class loader(if you are into that sort of thing)
- 如果您正在运行命令行应用程序,则可以只使用系统类加载器或加载应用程序类的类加载器 (
Class.getClassLoader()
)。 - 如果您在托管环境(JavaEE、servlet 容器等)中运行,那么最好首先检查当前线程上下文类加载器,然后返回到前一点给出的选项。
- 或者只是使用你自己的自定义类加载器(如果你喜欢那种东西)
In general, the most fool-proofand tested would be to use ClassUtils.forName()
from Spring (see JavaDoc).
通常,最简单且经过测试的方法是ClassUtils.forName()
从 Spring使用(请参阅JavaDoc)。
More in-depth explanation:
更深入的解释:
The most common form of
Class.forName()
, the one that takes a singleString
parameter, always uses the caller's classloader. This is the classloader that loads the code executing theforName()
method. By comparison,ClassLoader.loadClass()
is an instance method and requires you to select a particular classloader, which may or may not be the loader that loads that calling code. If picking a specific loader to load the class is important to your design, you should useClassLoader.loadClass()
or the three-parameter version offorName()
added in Java 2 Platform, Standard Edition (J2SE):Class.forName(String, boolean, ClassLoader)
.
的最常见形式
Class.forName()
,即采用单个String
参数的形式,始终使用调用者的类加载器。这是加载执行forName()
方法的代码的类加载器。相比之下,ClassLoader.loadClass()
是一个实例方法,需要您选择一个特定的类加载器,它可能是也可能不是加载调用代码的加载器。如果选择特定的加载器来加载类对您的设计很重要,则您应该使用ClassLoader.loadClass()
或forName()
在Java 2 Platform, Standard Edition (J2SE)中添加的三参数版本:Class.forName(String, boolean, ClassLoader)
。
Source: What is the difference between Class.forName()
and ClassLoader.loadClass()
?
来源:和 和有什么区别?Class.forName()
ClassLoader.loadClass()
Also, SPR-2611highlights one interesting obscure corner case when using Class.forName(String, boolean, ClassLoader)
.
此外,SPR-2611在使用Class.forName(String, boolean, ClassLoader)
.
As seen in that Spring issue, using ClassLoader.loadClass() is the recommended approach(when you need to load classes from specific class loader).
正如在那个 Spring 问题中看到的,使用 ClassLoader.loadClass() 是推荐的方法(当您需要从特定的类加载器加载类时)。
回答by Buhake Sindi
The 2nd approach loads a class using a ClassLoader
第二种方法使用一个类加载一个类 ClassLoader
public static Class<?> forName(String className)
throws ClassNotFoundException {
return forName0(className, true, ClassLoader.getCallerClassLoader());
This is what the JavaDoc says:
这是 JavaDoc 所说的:
forName(String name, boolean initialize, ClassLoader loader)
The specified class loader is used to load the class or interface. If the parameter loader is null, the class is loaded through the bootstrap class loader.
指定的类加载器用于加载类或接口。如果参数加载器为空,则通过引导类加载器加载该类。
So, the 2nd option uses the System ClassLoader (which is, in essence, what it does in the first option).
所以,第二个选项使用系统类加载器(本质上,它在第一个选项中所做的)。
回答by Michael Borgwardt
From the API doc:
来自API 文档:
Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of the current class.
调用这个方法相当于:
Class.forName(className, true, currentLoader)
其中 currentLoader 表示当前类的定义类加载器。
So the main difference is in which classloader will be used (it may or may not be the same as the system classloader). The overloaded method would also allow you to specify the classloader to use explicitly.
所以主要的区别在于将使用哪个类加载器(它可能与系统类加载器相同,也可能不同)。重载方法还允许您指定要显式使用的类加载器。
回答by Gerhard Presser
there's also a difference when loading array-types. I think classloader.loadClass(clazz)
cannot handle array-types, but Class.forName(clazz,true,classloader)
can.
加载数组类型时也有区别。我认为classloader.loadClass(clazz)
不能处理数组类型,但Class.forName(clazz,true,classloader)
可以。
回答by Bruno Reis
The other answers are very complete in that they explore other overloads of Class.forName(...)
, and talk about the possibility to use different ClassLoaders.
其他答案非常完整,因为他们探索了 的其他重载Class.forName(...)
,并讨论了使用不同类加载器的可能性。
However they fail to answer your direct question: "What is the difference between the above said approaches?", which deals with one specific overload of Class.forName(...)
. And they miss one very important difference. Class initialization.
但是,他们无法回答您的直接问题:“上述方法之间有什么区别?”,它涉及Class.forName(...)
. 他们错过了一个非常重要的区别。类初始化。
Consider the following class:
考虑以下类:
public class A {
static { System.out.println("time = " + System.currentTimeMillis()); }
}
Now consider the following two methods:
现在考虑以下两种方法:
public class Main1 {
public static void main(String... args) throws Throwable {
final Class<?> c = Class.forName("A");
}
}
public class Main2 {
public static void main(String... args) throws Throwable {
ClassLoader.getSystemClassLoader().loadClass("A");
}
}
The first class, Main1
, when run, will produce an output such as
第一个类,Main1
,在运行时,将产生一个输出,如
time = 1313614183558
The other, however, will produce no output at all. That means that the class A
, although loaded, has not been initialized (ie, it's <clinit>
has not been called). Actually, you can even query the class's members through reflection before the initialization!
然而,另一个根本不会产生任何输出。这意味着该类A
虽然已加载,但尚未初始化(即<clinit>
尚未调用)。实际上,您甚至可以在初始化之前通过反射查询类的成员!
Why would you care?
你为什么会在意?
There are classes that performs some kind of important initialization or registration upon initialization.
有些类在初始化时执行某种重要的初始化或注册。
For example, JDBC specifies interfaces that are implemented by different providers. To use MySQL, you usually do Class.forName("com.mysql.jdbc.Driver");
. That is, you load and initializethe class. I've never seen that code, but obviously the static constructor of that class must register the class (or something else) somewhere with JDBC.
例如,JDBC 指定了由不同提供者实现的接口。要使用 MySQL,您通常会执行Class.forName("com.mysql.jdbc.Driver");
. 也就是说,您加载并初始化该类。我从未见过该代码,但显然该类的静态构造函数必须在某处使用 JDBC 注册该类(或其他东西)。
If you did ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");
, you would not be able to use JDBC, since the class, altough loaded, has not been initialized (and then JDBC wouldn't know which implementation to use, just as if you had not loaded the class).
如果您这样做了ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");
,您将无法使用 JDBC,因为该类虽然已加载,但尚未初始化(然后 JDBC 将不知道要使用哪个实现,就像您尚未加载该类一样)。
So, this is the difference between the two methods you asked.
所以,这就是你问的两种方法之间的区别。
回答by Akhilesh Dhar Dubey
ClassLoader.loadClass() always loads the system classloader, whereas Class.forName() loades any class. Lets see this example,
ClassLoader.loadClass() 总是加载系统类加载器,而 Class.forName() 加载任何类。让我们看看这个例子,
package com;
public class TimeA {
public static void main (String args[]) {
try {
final Class c = Class.forName("com.A");
ClassLoader.getSystemClassLoader().loadClass("com.A");
}catch(ClassNotFoundException ex) {
System.out.println(ex.toString());
}
}
}
class A {
static {
System.out.println("time = " + System.currentTimeMillis());
}
}
When yoy run this program, you would get an Exception at ClassLoader.getSystemClassLoader().loadClass("com.A");
当你运行这个程序时,你会得到一个异常 ClassLoader.getSystemClassLoader().loadClass("com.A");
The Output may be:
输出可能是:
time = 1388864219803
java.lang.ClassNotFoundException: com.A
回答by Argho Chatterjee
But the static initilizer block is only executed when we use class.forname("...");
但是静态初始化块只有在我们使用 class.forname("..."); 时才会执行。
I have just tested.
我刚刚测试过。