最近的Java学习中又学习了一遍工厂模式,其中写到这样一句代码Class.forName(className).newInstance(),当时没注意,但是最后程序报错了,在调试的过程中注意到该句有问题,于是开启了百度之旅,这次旅途还真是收获不少


一:Class类的简介

       首先声明的是Class类是Java的一个类,与我们平时自定义的类一样,只不过名字较特殊,也是继承了Object类,官网是这样定义的:

public final class Class<T>
extends Object
implements Serializable, GenericDeclaration, Type, AnnotatedElement

       从代码中可看出,它是一个静态类,继承了Object类,实现了接口。
       Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。JVM通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
       Class 没有公共构造方法。Class 对象是在加载类时由JVM以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
       虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,JVM首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入
       基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
       每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
       一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。用一段代码来表示:

Date date1 = new Date();  
Date date2 = new Date();  
Class c1 = date1.getClass();  
Class c2 = date2.getClass();  
System.out.println(c1 == c2); // true

对于相同的类,JVM只会载入一次,而与该类对应的Class对象也只会存在一个,无论该类实例化了多少对象。实上,Class对象就是用来创建类的所有的“普通”对象的。 类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象(恰当地说,是被保存在一个同名的.class文件中)。在运行时,当我们想生成这个类的对象时,运行这个程序的 Java虚拟机(JVM)首先检查这个类的Class对象是否已经加载。如果尚未加载,JVM就会根据类名查找.class文件,并将其载入。 一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有(实例)对象


二:得到Class类型对象的3种方法

  1. 调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法
  2. 使用Class类的中静态forName()方法获得与字符串对应的Class对象
  3. 如果T是一个Java类型,那么T.class就代表了匹配的类对象
public class TestClass {  
    public static void main(String[] args)  
    {  
        try {  
            // 测试Class.forName()  
            Class testTypeForName = Class.forName("TestClassType");  
            System.out.println("testForName---" + testTypeForName);  
            // 测试类名.class  
            Class testTypeClass = TestClassType.class;  
            System.out.println("testTypeClass---" + testTypeClass);  
            // 测试Object.getClass()  
            TestClassType testGetClass = new TestClassType();  
            System.out.println("testGetClass---" + testGetClass.getClass());  
        } catch (ClassNotFoundException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
}  

class TestClassType {  
    // 构造函数  
    public TestClassType() {  
        System.out.println("----构造函数---");  
    }  
    // 静态的参数初始化  
    static {  
        System.out.println("---静态的参数初始化---");  
    }  
    // 非静态的参数初始化  
    {  
        System.out.println("----非静态的参数初始化---");  
    }  

}

运行结果:

---静态的参数初始化---  
testForName---class TestClassType  
testTypeClass---class TestClassType  
----非静态的参数初始化---  
----构造函数---  
testGetClass---class TestClassType

由上面结果可看出:

  • forName()调用的是静态的参数初始化;new是先调用非静态的参数初始化,然后调用构造函数
  • 生成Class对象是一样的,其实在JVM中只生成了一个Class对象,
  • 只打印一次“静态的参数初始化”,静态的方法属性初始化时,是在加载类的时候初始化;非静态方法是new类实例对象的时候初始化的

三:常用方法

Class.forName(字符串)

  • 字符串内容为:包名.类名(即完整的类路径)
  • 返回的是一个类
  • 首先Java中任何class都要装载在JVM上才能运行,这句话作用就是用来在JVM中查找并加载指定的类

newInstance()

  • 创建对象
  • 上面讲到Class.forName(字符串)来装载类的,都知道类在使用时必须创建对象后才能使用,该句话用来创建对象的
  • 与new的区别
  • 直观看来它是一个方法,new是一个关键字
  • new创建一个类时,这个类可以没有被加载,但是可能需要classloader来加载;newInstance()方法必须保证该类已加载,而且已连接了
  • new是动态加载,newInstance()静态加载
  • new是强类型,效率教高,能调用任何public构造;newInstance()是弱类型,效率较低,只能调用无参构造

四:应用情景

Java的Class类是Java反射机制的基础,通过Class类可获得关于这个类的相关信息

  • 加载数据库驱动时
JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();
//加载驱动  
Class.forName(jdbcConfig.getDriverName());
conn = DriverManager.getConnection(jdbcConfig.getUrl(),jdbcConfig.getUserName(),jdbcConfig.getPassword());
  • 使用工厂模式时
    使用工厂模式时,通常会根据读取到的配置文件里的信息来创建具体的工厂,然后通过该具体工厂创建具体的产品,当想使用别的工厂生产别的产品时,只需改一下配置文件即可,这样提高了软件的可伸缩性、可扩展性
String className = XmlConfigReader.getInstance().getDaoFactory("item-dao-factory");
ItemDaoFactory factory = null;
//实例化一个工厂
factory = (ItemDaoFactory)Class.forName(className).newInstance();

总结

  • 可以说工厂模式学了很多遍了,但是这一次的学习又有了新认识,看来知识要不断应用
  • 理解明白一些底层代码还是非常重要的,明白了运行机制,感觉思路顺畅了好多