繁体   English   中英

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

[英]What is the difference between “Class.forName()” and “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!

与传统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");

如果已编写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 Driver 是如何实现的结果。

作为旁注,我要提到 JDBC 4.0(自 Java 7 以来作为默认包添加)和 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:没有合适的驱动程序”。 再次强调,这是 JDBC 驱动程序中的错误,而不是您自己的代码中的错误。 如今,没有任何 JDBC 驱动程序应该包含此错误。 所以你可以(也应该)把newInstance()放在newInstance()

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

Class.forName("Somthing");

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") else 如果我们没有静态代码,我们可以使用 Class.forname().newInstance("someName") 因为它会将对象级代码块(非静态)加载到内存中

无论您调用 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语句中只打印一次而不是三次。

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

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

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

ex-: 假设 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()创建对象。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM