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

Joh*_*nna 161 java class

Class.forName()和之间有什么区别Class.forName().newInstance()

我不明白这个显着的区别(我已经读过一些关于它们的东西!).请你帮助我好吗?

Pas*_*ent 242

也许举例说明如何使用这两种方法将有助于您更好地理解事物.因此,请考虑以下类:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

正如其javadoc中所解释的那样,调用返回与类或接口关联对象与给定的字符串名称,即它返回受类型变量影响的对象.Class.forName(String) Classtest.Demo.classclazzClass

然后,调用创建此 对象表示的类的新实例.该类被实例化,就像具有空参数列表表达式一样.换句话说,这实际上相当于a 并返回一个新实例.clazz.newInstance() Classnewnew Demo()Demo

Demo因此运行此类将打印以下输出:

Hi!
Run Code Online (Sandbox Code Playgroud)

与传统new方法的最大区别在于,newInstance允许实例化一个在运行时之前不知道的类,从而使代码更具动态性.

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

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

编辑(回答OP发布的评论中的问题):JDBC驱动程序的情况有点特殊.正如JDBC API入门DriverManager章节中所述:

(...)一个Driver类被加载,因此DriverManager以两种方式之一自动注册:

  1. 通过调用方法Class.forName.这显式加载了驱动程序类.由于它不依赖于任何外部设置,因此建议使用这种加载驱动程序的方式来使用DriverManager 框架.以下代码加载类acme.db.Driver:

    Class.forName("acme.db.Driver");
    
    Run Code Online (Sandbox Code Playgroud)

    如果acme.db.Driver已经写入以便加载它会导致创建实例并且还 DriverManager.registerDriver使用该实例作为参数进行调用(应该这样做),那么它就位于 DriverManager驱动程序列表中并可用于创建连接.

  2. (......)

在这两种情况下,新加载的Driver类都有责任通过调用来注册自己DriverManager.registerDriver.如上所述,这应该在加载类时自动完成.

要在初始化期间注册自己,JDBC驱动程序通常使用如下静态初始化块:

package acme.db;

public class Driver {

    static {
        java.sql.DriverManager.registerDriver(new Driver());
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

调用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驱动程序是"特殊的",它们使用静态初始化块编写,其中创建实例并将其作为`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.因此,这实际上是由于驱动程序内部的特殊情况. (10认同)
  • 在上面的站点中写道:"调用Class.forName会自动创建一个驱动程序实例并将其注册到DriverManager,因此您不需要创建该类的实例.如果您要创建自己的实例,你会创造一个不必要的重复,但它不会造成伤害." 它意味着通过Class.forName你将创建一个实例,并且如果你想创建另一个它将创建一个不必要的实例所以Calss.forName()和Class.forName().newInstance()将创建一个实例司机! (2认同)

Tho*_*zer 36

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


Bal*_*usC 29

在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() {
        //
    }

}
Run Code Online (Sandbox Code Playgroud)

调用Class#forName()将执行所有静态初始化程序.这样,DriverManager可以通过连接URL在已注册的驱动程序中找到关联的驱动程序,在此期间getConnection()大致如下所示:

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");
}
Run Code Online (Sandbox Code Playgroud)

但也有一些错误的 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);
    }

}
Run Code Online (Sandbox Code Playgroud)

让它动态工作的唯一方法是newInstance()事后调用!否则你将乍一看无法解释的"SQLException:没有合适的驱动程序".再一次,这是JDBC驱动程序中的错误,而不是您自己的代码中的错误.如今,没有一个JDBC驱动程序应该包含这个bug.所以你可以(而且应该)离开newInstance().


Hus*_*ri' 15

1:如果你只对类的静态块感兴趣,那么加载类只会这样做,并且会执行静态块,然后你只需要:

Class.forName("Somthing");
Run Code Online (Sandbox Code Playgroud)

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

Class.forName("Somthing").newInstance();
Run Code Online (Sandbox Code Playgroud)


Gop*_*opi 6

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