【问题标题】:What is the difference between "Class.forName()" and "Class.forName().newInstance()"?“Class.forName()”和“Class.forName().newInstance()”有什么区别?
【发布时间】:2011-01-06 18:02:49
【问题描述】:

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

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

【问题讨论】:

    标签: java class object instantiation


    【解决方案1】:

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

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

    然后,调用clazz.newInstance() 会创建这个Class 对象所代表的类的新实例。该类被实例化为带有空参数列表的new 表达式。 换句话说,这里实际上等效于new Demo() 并返回Demo 的新实例。

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

    Hi!
    

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

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

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

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

    (...) 加载了一个 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 驱动程序的新自动加载功能不再需要所有这些。见JDBC 4.0 enhancements in Java SE 6

    【讨论】:

    • 在上面的站点中是这样写的:“调用Class.forName会自动创建一个驱动的实例并注册到DriverManager,所以你不需要创建一个类的实例。如果您要创建自己的实例,您将创建一个不必要的副本,但这不会造成任何伤害。”这意味着通过 Class.forName 您将创建一个实例,如果您想创建另一个实例,它将创建一个不必要的实例所以 Calss.forName() 和 Class.forName().newInstance() 都会创建一个实例司机!!
    • JDBC 驱动程序是“特殊的”,它们是用静态初始化块编写的,其中创建一个实例并作为DriverManager.registerDriver 的参数传递。在 JDBC 驱动程序上调用 Class.forName 会导致其初始化,从而执行静态块。以java2s.com/Open-Source/Java-Document/Database-DBMS/… 为例。因此,由于驱动程序内部,这实际上是一个特殊情况。
    • 我注意到在another answer 中,强烈建议不要使用 Class.newInstance()。推荐使用 Class.getConstructor(),然后依次使用 Constructor.newInstance()。它避免掩盖可能的异常。
    • “newInstance 允许实例化一个你直到运行时才知道的类”让我很开心。谢谢。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      但也有 错误 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()

      【讨论】:

        【解决方案4】:

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

        Class.forName("Somthing");
        

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

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

        【讨论】:

        • 优秀的答案!简洁明了!
        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

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

              【讨论】:

                【解决方案9】:

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

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

                但是,如果您想访问或调用您的类方法(您在运行时提供的类),那么您需要拥有它的对象,以便 Class 类的 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() 创建对象。

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-06-06
                  • 2013-10-25
                  • 1970-01-01
                  • 2013-02-08
                  • 1970-01-01
                  • 2023-03-14
                  • 2012-09-02
                  • 2013-11-09
                  相关资源
                  最近更新 更多