2

我正在努力理解 class.forName 的使用。实际上我正在做一个项目,我有一个接受字符串的字段。类路径!我在 class.forName 中传递了这个字符串——没有例外……所以 class.forName 可以找到指定的类。之后我想调用指定的类。但是我的问题来了。首先,我必须分隔字符串才能取类的名称......对吗?另外,如果我这样做, Class requestedClass = Class.forName(path); AClass newClassInstance = (AClass)requestedClass.newInstance();
我有一个异常 InstantiationException,因为必须在类的构造函数中传递一些参数。有什么解决办法吗?对我来说,使用 if 语句并调用相应的类听起来更明智,但如果有很多可能的类,这种方法将是一场噩梦。谢谢!

4

4 回答 4

3

它很有用,因为它允许您指定要加载的类,该类在编译代码时不存在。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对象,如果你真的需要,它反过来让你通过将参数传递给它的构造函数来实例化一个类。

于 2012-07-04T22:19:19.310 回答
2

您将看到的另一个重要用途是加载 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));
于 2012-07-04T23:08:29.500 回答
1

您可以使用以下方法通过反射调用参数化构造函数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 个类中的一个,则应该可以以相同的方式实例化所有类,无论是构造函数签名还是工厂方法。)

于 2012-07-04T22:30:03.817 回答
1

正如已经指出的那样,它非常有用,因为您可以加载在编译时不存在的类,例如插件或扩展。

您的问题有一个简单的解决方案。简单地获取您正在搜索的构造函数并提供预期的参数。例如:

MyClass a = (MyClass) Class.forName("com.test.MyClass")
    .getConstructor(new Class[] { String.class }).newInstance("Test");
于 2012-07-04T22:22:57.593 回答