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

时间:2010-01-19 10:11:38

标签: java class

Class.forName()Class.forName().newInstance()之间的区别是什么?

我不明白其中的显着差异(我已经读过一些关于它们的内容!)。你能帮我吗?

9 个答案:

答案 0 :(得分:232)

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

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.classclazz类型的Class变量影响。

然后,调用clazz.newInstance() 会创建一个由此Class对象表示的类的新实例。该类实例化为具有空参数列表的new表达式。换句话说,这实际上等同于new Demo()并返回{{1}的新实例}。

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

Demo

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

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

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

编辑(回答OP发布的评论中的问题):JDBC驱动程序的情况有点特殊。正如DriverManagerGetting Started with the JDBC API章节中所述:

  

(...)加载了newInstance类,并且   因此自动注册   使用Driver,其中一个   方法:

     
      
  1. 调用方法DriverManager。这明确加载   司机班。既然没有   这种方式取决于任何外部设置   建议加载驱动程序   一个使用Class.forName   框架。以下代码加载   班级DriverManager

    acme.db.Driver
         

    如果已写入Class.forName("acme.db.Driver"); 以便加载它会导致acme.db.Driver   要创建的实例以及调用   DriverManager.registerDriver   实例作为参数(因为它   应该这样做,然后它就在了   DriverManager的驱动程序和列表   可用于创建连接。

  2.   
  3. (...)

  4.         

    在这两种情况下,新加载的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驱动程序的新自动加载功能不再需要这一切。请参阅JDBC 4.0 enhancements in Java SE 6

答案 1 :(得分:35)

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

答案 2 :(得分:28)

在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()将执行所有static initializers。这样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");
}

但是还有 buggy 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驱动程序中的 bug ,而不是您自己的代码。如今,没有一个JDBC驱动程序应该包含这个bug。所以你可以(而且应该)离开newInstance()

答案 3 :(得分:14)

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

Class.forName("Somthing");

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

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

答案 4 :(得分:6)

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

答案 5 :(得分: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());

答案 6 :(得分:3)

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

答案 7 :(得分:1)

无论您调用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语句仅打印一次,而不打印三遍。

答案 8 :(得分:0)

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

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

但是如果你想访问或调用你的类方法(你在运行时给出的类)那么你需要拥有它的对象,所以Class类的newInstance方法为你做。它创建类的新实例并返回它给你。你只需要把它输入你的班级。

ex-:假设员工是你的班级

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

// args [0] = cmd行参数,用于在运行时赋予类。

员工ob1 = a.newInstance();

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

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