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


165

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

我不了解其中的重大区别(我已经阅读了一些有关它们的信息!)。请你帮助我好吗?

Answers:


247

也许一个示例演示如何使用两种方法将有助于您更好地理解事物。因此,请考虑以下类:

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中所述,调用将返回与具有给定字符串名称的类或接口关联对象,即它返回受type变量影响的对象。Class.forName(String) Classtest.Demo.classclazzClass

然后,调用创建此 对象表示的类的新实例如同通过带有空参数列表表达式实例化该类换句话说,这实际上等于a 并返回的新实例。clazz.newInstance() Classnewnew Demo()Demo

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

Hi!

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

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

实际上,如果您想走得更远,请看一下Ted Neward的论文《理解Class.forName()》,我在上一段中对此进行了解释。

编辑(从发表为评论的OP中回答问题):JDBC驱动程序的情况有点特殊。如《 JDBC API入门》中DriverManager章节所述

(...)Driver加载了一个类,因此通过以下DriverManager两种方式之一自动向进行注册:

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

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

    如果acme.db.Driver编写了这样的代码以便加载将导致创建一个实例,并且还 DriverManager.registerDriver以该实例作为参数进行调用(应该这样做),则该 DriverManager驱动程序位于的驱动程序列表中,可用于创建连接。

  2. (...)

在这两种情况下,新加载的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增强功能



2
在上面的站点中写道:“调用Class.forName会自动创建一个驱动程序实例,并在DriverManager中注册它,因此您无需创建该类的实例。如果要创建自己的实例, ,那么您将创建不必要的副本,但这不会造成任何危害。” 这意味着通过Class.forName您将自动创建一个实例,如果要创建另一个实例,它将创建一个不必要的实例,因此Calss.forName()和Class.forName()。newInstance()都将创建该实例。司机!!
Johanna 2010年

10
JDBC驱动程序是“特殊的”,它们是用一个静态初始化块编写的,在该块中创建了一个实例并作为的参数传递DriverManager.registerDriver。调用Class.forNameJDBC驱动程序会导致其初始化,进而导致静态块的执行。请查看java2s.com/Open-Source/Java-Document/Database-DBMS/…作为示例。因此,实际上这是驱动程序内部的一个特殊情况。
Pascal Thivent,2010年

1
我注意到在另一个答案中,强烈建议不要使用Class.newInstance()。建议使用Class.getConstructor(),然后依次使用Constructor.newInstance()。它避免了掩盖可能的异常。
LS

“ newInstance允许实例化一个直到运行时才知道的类”。谢谢。
热情的Code

37

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


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

}

调用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");
}

但是,还有一些错误的 JDBC驱动程序,从org.gjt.mm.mysql.Driver众所周知的示例开始,该驱动程序错误地将自身注册在了Constructor中,而不是在静态块中:

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()


17

1:如果您只对类的静态块感兴趣,则仅加载类会执行静态块,然后您需要做的就是:

Class.forName("Somthing");

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

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

很好的答案!简洁明了!
gaurav

6

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


3

“ Class.forName()”返回给定名称的Class-Type。“ 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());

3

只是添加到上面的答案中,当我们有一个静态代码(即代码块与实例无关)需要在内存中存在时,我们可以让该类返回,因此我们将使用Class.forname(“ someName”)否则,如果没有静态代码,我们可以使用Class.forname()。newInstance(“ someName”),因为它将对象级别的代码块(非静态)加载到内存中


1

无论您调用Class.forName()方法多少次,仅在执行静态块后就不会多次:

包forNameMethodDemo;

公共类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();
    }

}

公共类DemoClass {

static {
    System.out.println("in Static block");
}

{
    System.out.println("in Instance block");
}

}

输出将是:

in Static block in Instance block

in Static block声明仅打印一次,而不打印三遍。


0

Class.forName()-> forName()是Class类的静态方法,它返回用于反射的Class类对象,而不是用户类对象,因此您只能像getMethods(),getConstructors()等那样调用Class类方法。

如果您只关心运行(给定运行时)类的静态块,并且仅获取类的方法,构造函数,修饰符等信息,则可以使用通过Class.forName()获得的此对象

但是,如果您要访问或调用您的类方法(在运行时给定的类),则需要具有其对象,以便Class类的newInstance方法可以为您执行此操作。它将创建该类的新实例并将其返回给您您只需将其类型转换为您的班级。

例如:假设Employee是你的班级,那么

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

// args [0] = cmd行参数,以在运行时提供类。

员工ob1 = a.newInstance();

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.