我正在努力理解 class.forName 的使用。实际上我正在做一个项目,我有一个接受字符串的字段。类路径!我在 class.forName 中传递了这个字符串——没有例外……所以 class.forName 可以找到指定的类。之后我想调用指定的类。但是我的问题来了。首先,我必须分隔字符串才能取类的名称......对吗?另外,如果我这样做,
Class requestedClass = Class.forName(path);
AClass newClassInstance = (AClass)requestedClass.newInstance();
我有一个异常 InstantiationException,因为必须在类的构造函数中传递一些参数。有什么解决办法吗?对我来说,使用 if 语句并调用相应的类听起来更明智,但如果有很多可能的类,这种方法将是一场噩梦。谢谢!
4 回答
它很有用,因为它允许您指定要加载的类,该类在编译代码时不存在。HTML 中的applet
标签使用它来加载java.applet.Applet
. Web 服务器使用它来加载 Java servlet 和其他 Web 组件。EJB 容器、Spring 容器和许多其他程序都使用这种方法来加载组件。如果您使用 Eclipse 或 NetBeans,这些 IDE 会通过使用加载插件Class.forName()
——这使它们能够加载每天创建的新插件。
所有这些魔法的关键是继承`。您需要能够将创建的对象视为接口或父类的实例 - 即,
Applet applet = (Applet) Class.forName(theNameOfSomeAppletClass).newInstance();
然后,您可以theNameOfSomeAppletClass
从 HTML 文件中读取(例如)并创建该类的实例,即使您的代码从未听说过它。
大多数时候,插件 API 只是简单地指定你应该给你的插件类一个无参数的构造函数。但是java.lang.Class
有一个getConstructors()
方法可以让你获取Constructor
对象,如果你真的需要,它反过来让你通过将参数传递给它的构造函数来实例化一个类。
您将看到的另一个重要用途是加载 JDBC 驱动程序。这可能是大多数 java 程序员第一次使用 Class.forName。你会看到是这样的:
Class.forName("oracle.jdbc.OracleDriver");
Connection connection = DriverManager.getConnection("jdbc:oracle:thin:...");
这里发生的是,当 OracleDriver 类被加载(通过 Class.forName)时,它包含一个类似这样的类初始化块(它可能更复杂,但说明了这一点)。该块作为类加载过程的一部分运行。
public class OracleDriver {
static {
DriverManager.registerDriver(new OracleDriver());
}
}
然后当你调用 DriverManager.getConnection 时,它会询问所有已注册的驱动程序是否能够处理给定的 url(代码比较复杂,但我会简化一下):
for (Driver driver : registeredDrivers) {
if (driver.acceptsURL(url)) {
// use this driver
}
}
至于您的实例化问题, Class.newInstance() 方法将始终调用无参数构造函数,如果不存在则抛出异常。如果你想要一个特定的构造函数,你可以在类本身上使用“getConstructor”方法。例如,如果你想要一个接受 String 和 Long 的构造函数:
Class clazz = ...; // figure out which class here
Constructor c = clazz.class.getConstructor(String.class, Long.class);
Object o = c.newInstance("foo", new Long(1L));
您可以使用以下方法通过反射调用参数化构造函数Class.getConstructor()
:
class Foo {
public Foo(String bar) {
System.out.println("bar = " + bar);
}
}
Class<?> fooCls = Class.forName("Foo");
Constructor<?> fooCtor = fooCls.getConstructor(String.class);
Foo foo = (Foo) fooCtor.newInstance("BAR");
如果您需要实例化的类没有无参数构造函数,并且您无法弄清楚要传递给它的参数,那么您就不走运了。(这也是您设计中的一个严重缺陷。如果一个方法需要实例化 N 个类中的一个,则应该可以以相同的方式实例化所有类,无论是构造函数签名还是工厂方法。)
正如已经指出的那样,它非常有用,因为您可以加载在编译时不存在的类,例如插件或扩展。
您的问题有一个简单的解决方案。简单地获取您正在搜索的构造函数并提供预期的参数。例如:
MyClass a = (MyClass) Class.forName("com.test.MyClass")
.getConstructor(new Class[] { String.class }).newInstance("Test");