Java的Class类是Java反射机制的基础,通过Class类我们可以获取关于一个类的相关信息。
java.lang.Class是一个比较特殊的类,它用于封装被加载到Jvm中的类(包括类和接口)的信息。当一个类或者接口被加载到Jvm时便会产生一个与之关联的java.lang.Class对象,可以通过这个Class对象来对被封装类的信息进行详细的访问。
Java中,每个类(class)都有一个对应Class对象。也就是说,当我们编写一个类,编译完成之后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
Class类是继承Object类,它是一个final类。Class 类的实例表示:正在运行的 Java 应用程序中的类和接口。
获取某一类的Class对象有三种途径:
一、Object的getClass方法(Object是所有类的基类)。所有Java对象都具备这个方法,该方法用于返回该类在Class类所对应的一个Class对象。
public class Demo1 {
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date();
Class c1 =date1.getClass();
Class c2 =date2.getClass();
Class c3 = date2.getClass();
/**
* 对象.getClass():用于返回该类在Class类里面对应的实例。
*/
System.out.println(c1.getClass());
/**
* 需要注意的是,代码中的date1和date2和date3的getClass方法返回了相同的Class对象(c1==c2和c2==
* c3的值为true)。这是因为, 对于相同的类,JVM只会载入一次,而与该类对应的Class对象也只会存在一个,无论该类实例化了多少对象。
*/
System.out.println((c1==c2)&&(c2==c3));
}
}
控制台输出:
class java.lang.Class
true
另外,需要强调的是,当一个对象被其父类的引用或其实现的接口类型的引用所指向的时,getClass方法返回的是与对象实际所属类关联的Class对象。例如:
List list = new ArrayList();
System.out.println(list.getClass().getName()); // java.util.ArrayList
上面的代码中,语句list.getClass()方法返回的是list所指向对象实际所属类java.util.ArrayList对应的 Class对象而并未java.util.List所对应的Class对象。有些时候可以通过这个方法了解一个对象的运行时类型,例如:
HashSet set = new HashSet();
Iterator it = set.iterator();
System.out.println(it.getClass().getName()); //java.util.HashMap$KeyIterator
从代码可以看出,HashSet的iterator方法返回的是实现了Iterator接口的HashMap内部类(KeyIterator)对象。
因为抽象类和接口不可能实例化对象,因此不能通过Object的getClass方法获得与抽象类和接口关联的Class对象。
二、使用.class的方式
类名.class方式即可返回该类在Class类所对应的一个Class实例。
Class clazz = String.class;
System.out.println(clazz.getName()); // java.lang.String
这个方法可以直接获得与指定类关联的Class对象,而并不需要有该类的对象存在。
三、使用Class.forName(String className);
Class里面有个著名的static方法forName
private static Class forName(String className) throws ClassNotFoundException
该方法可以根据字符串所指定的类名获取该类关联的Class对象。如果该类还没被加载到Jvm中,该方法将会把它加载到Jvm中。
该方法声明抛出ClassNotFoundException异常。顾名思义,当该方法无法获取需要装入的类时(例如,在当前类路径中不存在这个类),就会抛出这个异常。
public class TestReflection {
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
String str = "com.reflect.demo.T";
/**
* 反射的具有这么一个特点:我们可以在运行期间动态加载一个类进来,也可以动态的new一个对象出来,
* 可以动态的调用这个对象的方法以及动态了解这个对象的内部结构;
*/
// class.forName();会把一个类load到内存中
Class c = Class.forName(str);// 是把类名为str的类加载到内存中
//System.out.println(c.getClassLoader());
//c.newInstance();
}
}
class T {
static {
System.out.println("T is Loading!");
}
int i;
String s;
public T() {
System.out.println("T construct!");
}
public void m1(int i) {
this.i = i;
}
public String getS() {
return s;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public void setS(String s) {
this.s = s;
}
}
控制台会有如下输出:
T is Loading!
Class.forName("T")首先会将T类装入JVM,并返回与之关联的Class对象。JVM装入T类后对其进行初始化,调用了其static块中的代码。需要注意的是:forName方法的参数是类的完整限定名(即包含包名)。
区别于前面两种获取Class对象的方法。使用Class.forName方法所要获取的与之对应的Class对象的类可以通过字符串的方式给定。该方法通常用于在程序运行时根据类名动态的载入该类并获得与之对应的Class对象。三种方法生成CLASS对象是一样的,因为CLASS在JVM的名称是一样的,但是三种生成的方法略有不同:静态的方法属性初始化,是在加载类的时候初始 化。而非静态方法属性初始化,是new类实例对象的时候加载。当我们编写一个新的JAVA类时,JVM就会帮我们编译成CLASS对象,存放在同名 的.class文件中,在运行时,当需要生成这个类的对象时,JVM就会检查此类是否装载到内存中,会没有装载,就把.class装载到内存中,若装载 过,则根据.class生成对象。