“ Class.forName ()”和“ Class.forName () . newInstance ()”有什么区别?

Class.forName()Class.forName().newInstance()有什么不同?

我不明白它们之间有什么显著的区别(我读过一些关于它们的资料)。你能帮帮我吗?

303818 次浏览

ForName ()为您提供类对象,这对于反射非常有用。这个对象的方法是由 Java 定义的,而不是由编写类的程序员定义的。每个班级都是一样的。在其上调用 newInstance () ,就会得到该类的一个实例(即调用 Class.forName("ExampleClass").newInstance()等效于调用 new ExampleClass()) ,在该实例上可以调用该类定义的方法,访问可见字段等。

“ Class.forName ()”返回给定名称的 Class-Type。

在类型上,您不能直接调用任何实例方法,但只能对类使用反射。如果你想使用类的一个对象,你必须创建它的一个实例(就像调用“ 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());

ForName ()获取对 Class 的引用,Class.forName ()。NewInstance ()尝试使用 Class 的 no-arg 构造函数来返回一个新实例。

也许通过一个示例演示如何使用这两种方法可以帮助您更好地理解问题。因此,考虑以下类别:

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对象即返回受 Class类型的 clazz变量影响的 test.Demo.class

然后,调用 clazz.newInstance()创建由此 ABC1对象表示的类的新实例。类的实例化方式类似于带有空参数列表的 new表达式。换句话说,这里实际上等效于 new Demo()并返回 Demo的一个新实例。

因此,运行这个 Demo类会输出以下输出:

Hi!

与传统 new的最大区别在于,newInstance允许实例化一个直到运行时才知道的类,从而使代码更具动态性。

一个典型的例子是 JDBC API,它在运行时加载执行工作所需的确切驱动程序。EJB 容器、 Servlet 容器也是很好的例子: 它们使用动态运行时加载来加载和创建它们在运行时之前不知道的组件。

实际上,如果你想更进一步,看一下 Ted Neward 的论文 理解 Class.forName (),我在上面的段落中已经解释过了。

EDIT (回答作为注释发布的 OP 提出的问题) : JDBC 驱动程序的情况有点特殊。正如 开始使用 JDBC API司机经理章所解释的:

(...)装入 Driver类,并且 因此自动注册 和 DriverManager一起,两个中的一个 方法:

  1. 通过调用方法 Class.forName 驾驶员类。因为它没有 取决于任何外部设置,这种方式 加载驱动程序是建议的 使用 DriverManager 下面的代码加载 课程 acme.db.Driver:

     Class.forName("acme.db.Driver");
    

如果编写了 acme.db.Driver以便加载它会导致 实例,并调用 用 DriverManager.registerDriver 实例作为参数 (作为 应该做) ,然后它是在 DriverManager的司机名单 可用于创建连接。

  1. (...)

在这两种情况下,新加载的 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 驱动程序的结果。

顺便提一下,JDBC 4.0(自 Java 7以来作为默认包添加)和 JDBC 4.0驱动程序的新自动加载特性不再需要所有这些。参见 Java SE 6中的 JDBC 4.0增强

在 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 驱动程序应该包含这个 bug。所以你可以(而且应该)离开 newInstance()

只要添加上述答案,当我们有一个静态代码(即代码块是实例独立的) ,需要存在于内存中,我们可以有类返回,所以我们将使用 Class.forname (“ some Name”) ,如果我们没有静态代码,我们可以去 Class.forname ()。NewInstance (“ some Name”) ,因为它将把对象级代码块(非静态)加载到内存中

1: 如果您只对类的静态块感兴趣,那么只加载该类,并执行静态块,那么您所需要的就是:

Class.forName("Somthing");

2: 如果你对加载类感兴趣,执行它的静态块,也想访问它的非静态部分,那么你需要一个实例,然后你需要:

Class.forName("Somthing").newInstance();

ForName ()是 Class 类的静态方法,它返回用于反射的 Class 类对象,而不是用户类对象,所以你只能调用它上面的 Class 类方法,比如 getMethods()getConstructors()等。

如果你只关心运行类的静态块,并且只获得类的方法、构造函数、修饰符等信息,你可以使用 Class.forName()获得这个对象

但是如果你想要访问或者调用你的 Class 方法(你在运行时给出的类) ,那么你需要它的对象,所以 Class 类的 newInstance 方法会为你做这件事。它会创建类的新实例并返回给你。您只需要将其类型强制转换为您的类。

那么假设员工是你的班级

Class a=Class.forName(args[0]);


//args[0]=cmd line argument to give class at  runtime.


Employee ob1=a.newInstance();

a.newInstance()类似于使用 new Employee()创建对象。

现在您可以访问所有类可见的字段和方法。

不管调用 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语句只打印一次,而不是三次。