最近的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种方法
- 调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法
- 使用Class类的中静态forName()方法获得与字符串对应的Class对象
- 如果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();
总结
- 可以说工厂模式学了很多遍了,但是这一次的学习又有了新认识,看来知识要不断应用
- 理解明白一些底层代码还是非常重要的,明白了运行机制,感觉思路顺畅了好多