Class.forName()
和 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!
newInstance
与传统的 new
的最大区别在于,newInstance
允许你在运行时实例化一个你在编写代码时并不知道的类,使得你的代码更加动态。
一个典型的例子是JDBC API,它会在运行时加载所需的确切驱动程序。EJB容器、Servlet容器也是其他很好的例子:它们使用动态运行时加载来加载和创建在运行时之前根本不知道的组件。
事实上,如果你想进一步了解,请查看 Ted Neward 的论文《理解 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 驱动程序实现的结果。
顺便提一下,自从 Java 7 引入了 JDBC 4.0(作为默认包),以及 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:no suitable driver"。再说一遍,这是JDBC驱动程序中的错误,而不是你自己的代码问题。现在,没有一个JDBC驱动程序应该包含这个错误。因此,你可以(也应该)省略newInstance()
。1:如果你只对类的静态块感兴趣,那么仅加载该类即可,并且会执行静态块,所需的全部内容为:
Class.forName("Somthing");
2:如果您有兴趣加载该类,执行其静态块,并希望访问其非静态部分,则需要一个实例,然后您需要:
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")。如果没有静态代码,我们可以使用 Class.forname().newInstance("someName"),因为它将加载对象级别的代码块(非静态)到内存中。
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
这个静态块中的
语句只会被打印一次,而不是三次。
Class.forName()
-->forName()是Class类的静态方法,它返回用于反射的Class类对象,而不是用户类对象,因此您只能在其上调用Class类方法,如getMethods()
、getConstructors()
等。
如果您只关心运行您的(运行时给定的)类的静态块,并仅获取有关方法、构造函数、修饰符等的信息,则可以使用使用Class.forName()
获得的对象来完成。
但是,如果您想要访问或调用您的类方法(在运行时给定的类),则需要拥有其对象,因此Class类的newInstance方法为您执行此操作。它创建该类的新实例并将其返回给您。您只需要将其强制转换为您的类即可。
例如:假设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()
创建对象。
现在您可以访问所有类中可见的字段和方法。
DriverManager.registerDriver
。在JDBC驱动程序上调用Class.forName
会导致其初始化,从而执行静态块。请参阅http://www.java2s.com/Open-Source/Java-Document/Database-DBMS/db-derby-10.2/org/apache/derby/jdbc/ClientDriver.java.htm以获得示例。因此,这实际上是由于驱动程序内部的一个特殊情况。 - Pascal Thivent