![](/img/trans.png)
[英]Difference between calling a class constructor and using Class.forName().newInstance
[英]What is the difference between “Class.forName()” and “Class.forName().newInstance()”?
Class.forName()
和Class.forName().newInstance()
什么区别?
我不明白其中的显着差异(我已经阅读了一些关于它们的内容!)。 你能帮帮我吗?
也许一个演示如何使用这两种方法的示例将帮助您更好地理解事物。 因此,请考虑以下类:
package test;
public class Demo {
public Demo() {
System.out.println("Hi!");
}
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("test.Demo");
Demo demo = (Demo) clazz.newInstance();
}
}
正如在其 javadoc 中所解释的,调用Class.forName(String)
返回与具有给定字符串名称的类或接口关联的Class
对象,即它返回test.Demo.class
,它会影响Class
类型的clazz
变量。
然后,调用clazz.newInstance()
创建此Class
对象表示的类的新实例。 类被实例化,就好像由一个带有空参数列表的new
表达式一样。 换句话说,这在这里实际上相当于一个new Demo()
并返回一个新的Demo
实例。
运行这个Demo
类会打印以下输出:
Hi!
与传统new
的最大区别在于newInstance
允许实例化一个您直到运行时才知道的类,从而使您的代码更具动态性。
一个典型的例子是 JDBC API,它在运行时加载执行工作所需的确切驱动程序。 EJB 容器、Servlet 容器是其他很好的例子:它们使用动态运行时加载来加载和创建在运行时之前不知道的组件。
实际上,如果您想更进一步,请查看我在上面段落中释义的 Ted Neward 论文Understanding Class.forName() 。
编辑(回答作为评论发布的 OP 中的一个问题):JDBC 驱动程序的情况有点特殊。 如JDBC API 入门的DriverManager章节所述:
(...)
Driver
类被加载,因此自动注册到DriverManager
,以两种方式之一:
通过调用方法
Class.forName
。 这显式加载驱动程序类。 由于它不依赖于任何外部设置,因此推荐使用这种加载驱动程序的方式来使用DriverManager
框架。 以下代码加载类acme.db.Driver
:Class.forName("acme.db.Driver");
如果已编写
acme.db.Driver
以便加载它会导致创建一个实例,并使用该实例作为参数调用DriverManager.registerDriver
(它应该这样做),则它位于DriverManager
的驱动程序列表中,并且可用于创建连接。
- (……)
在这两种情况下,新加载的
Driver
类有责任通过调用DriverManager.registerDriver
来注册自己。 如前所述,这应该在加载类时自动完成。
要在初始化期间注册自己,JDBC 驱动程序通常使用静态初始化块,如下所示:
package acme.db;
public class Driver {
static {
java.sql.DriverManager.registerDriver(new Driver());
}
...
}
调用Class.forName("acme.db.Driver")
会导致acme.db.Driver
类的初始化,从而导致静态初始化块的执行。 而Class.forName("acme.db.Driver")
确实会“创建”一个实例,但这只是(好的)JDBC Driver 是如何实现的结果。
作为旁注,我要提到 JDBC 4.0(自 Java 7 以来作为默认包添加)和 JDBC 4.0 驱动程序的新自动加载功能不再需要所有这些。 请参阅Java SE 6 中的 JDBC 4.0 增强功能。
Class.forName() 为您提供类对象,这对于反射很有用。 该对象具有的方法是由 Java 定义的,而不是由编写类的程序员定义的。 它们对每个班级都是一样的。 调用 newInstance() 为您提供该类的实例(即调用Class.forName("ExampleClass").newInstance()
相当于调用new ExampleClass()
),您可以在其上调用类定义的方法,访问可见字段等。
在 JDBC 世界中,通常的做法(根据 JDBC API)是使用Class#forName()
加载 JDBC 驱动程序。 JDBC 驱动程序应该在DriverManager
一个静态块中注册自己:
package com.dbvendor.jdbc;
import java.sql.Driver;
import java.sql.DriverManager;
public class MyDriver implements Driver {
static {
DriverManager.registerDriver(new MyDriver());
}
public MyDriver() {
//
}
}
调用Class#forName()
将执行所有静态初始值设定项。 这样DriverManager
就可以在getConnection()
期间通过连接 URL 在已注册的驱动程序中找到关联的驱动程序,大致如下所示:
public static Connection getConnection(String url) throws SQLException {
for (Driver driver : registeredDrivers) {
if (driver.acceptsURL(url)) {
return driver.connect(url);
}
}
throw new SQLException("No suitable driver");
}
但也有错误的JDBC 驱动程序,从org.gjt.mm.mysql.Driver
作为众所周知的例子开始,它错误地在构造函数而不是静态块中注册自己:
package com.dbvendor.jdbc;
import java.sql.Driver;
import java.sql.DriverManager;
public class BadDriver implements Driver {
public BadDriver() {
DriverManager.registerDriver(this);
}
}
让它动态工作的唯一方法是之后调用newInstance()
! 否则,您将第一眼看到无法解释的“SQLException:没有合适的驱动程序”。 再次强调,这是 JDBC 驱动程序中的错误,而不是您自己的代码中的错误。 如今,没有任何 JDBC 驱动程序应该包含此错误。 所以你可以(也应该)把newInstance()
放在newInstance()
。
1:如果你只对类的静态块感兴趣,加载类只会做,并且会执行静态块,那么你需要的是:
Class.forName("Somthing");
2:如果您有兴趣加载类,执行其静态块并希望访问其非静态部分,那么您需要一个实例,然后您需要:
Class.forName("Somthing").newInstance();
Class.forName() 获取对 Class 的引用,Class.forName().newInstance() 尝试使用 Class 的无参数构造函数返回一个新实例。
“Class.forName()”返回给定名称的类类型。 “newInstance()”确实返回此类的一个实例。
在类型上,您不能直接调用任何实例方法,而只能对类使用反射。 如果要使用类的对象,则必须创建它的实例(与调用“new MyClass()”相同)。
“Class.forName()”示例
Class myClass = Class.forName("test.MyClass");
System.out.println("Number of public methods: " + myClass.getMethods().length);
“Class.forName().newInstance()”的示例
MyClass myClass = (MyClass) Class.forName("test.MyClass").newInstance();
System.out.println("String representation of MyClass instance: " + myClass.toString());
只是添加到上面的答案,当我们有一个静态代码(即代码块是独立于实例的)需要存在于内存中时,我们可以返回该类,因此我们将使用 Class.forname("someName") else 如果我们没有静态代码,我们可以使用 Class.forname().newInstance("someName") 因为它会将对象级代码块(非静态)加载到内存中
无论您调用 Class.forName() 方法多少次,静态块只执行一次而不是多次:
package forNameMethodDemo;
public class MainClass {
public static void main(String[] args) throws Exception {
Class.forName("forNameMethodDemo.DemoClass");
Class.forName("forNameMethodDemo.DemoClass");
Class.forName("forNameMethodDemo.DemoClass");
DemoClass demoClass = (DemoClass)Class.forName("forNameMethodDemo.DemoClass").newInstance();
}
}
public class DemoClass {
static {
System.out.println("in Static block");
}
{
System.out.println("in Instance block");
}
}
输出将是:
in Static block
in Instance block
这个in Static block
语句中只打印一次而不是三次。
Class.forName()
-->forName() 是 Class 类的静态方法,它返回用于反射的 Class 类对象而不是用户类对象,因此您只能在其上调用 Class 类方法,如getMethods()
、 getConstructors()
等。
如果您只关心运行您的(运行时给定的)类的静态块并且只获取您的类的方法、构造函数、修饰符等的信息,您可以使用您使用Class.forName()
获得的这个对象
但是如果你想访问或调用你的类方法(你在运行时给出的类)那么你需要有它的对象,所以类类的 newInstance 方法为你做。它创建类的新实例并将其返回给你.你只需要将它类型转换到你的班级。
ex-: 假设 Employee 是你的班级
Class a=Class.forName(args[0]);
//args[0]=cmd line argument to give class at runtime.
Employee ob1=a.newInstance();
a.newInstance()
类似于使用new Employee()
创建对象。
现在您可以访问所有类可见的字段和方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.