众所周知,java一切皆对象。但是如果平时注重业务开发,而比较少关注很多开源框架如Spring、Hibernate等的源码,我们一般会比较少看到Class对象的身影。今天我们就来看看这个神秘的Class对象。
每个类都对应一个Class对象。虽然我们一般不会直接操作这个对象,其实这个对象非常常见,我们Java文件编译后的class文件,保存的就是这个类的Class对象。 我们定义了几个类,我们就能看到编译后的目录(通常在target目录)下有几个class文件。 1.比如我们定义一个Employee类,那么我们就看到有一个Employee.class文件;
package com.sky.javase; public class Employee { }2.比如我们定义一个public的Book类,和一个BookMark类,那么就得到两个class文件:Book.class、BookMark.class
package com.sky.javase; public class Book { } class BookMark{ }3.比如我们定义一个Person类和一个内部类Head,那么也得到两个class文件:Person.class、Person$Head.class(尤其注意内部类的命名规则)
package com.sky.javase; public class Person { class Head{ } }总之:一个萝卜一个坑,定义几个类,就有几个class文件。这些class文件,保存的就是我们定义类的Class对象。当这个类的Class对象被加载到内存,它就被用来创建这个类的所有对象。、
根据实际情况,我们可以有多种方式获取。 在示例开始前,我们定义了一个类Student,分别写了一个静态代码块和一个构造代码块。当类被加载就会调用静态代码块,当创建对象,就会调用构造代码块。我们通过输出来观察三种方式的差异。
class Student{ static{ System.out.println("这是静态代码块!"); } { System.out.println("这是构造代码块!"); } }1.如果你刚好有一个实例对象,直接调用它的getClass方法(继承至Object):
public class ReflectTest { @Test public void reflect_getClass(){ Student stu = new Student(); Class<?> sClass = stu.getClass(); } }输出结果: 这是静态代码块! 这是构造代码块!
2.如果没有实例对象,只有类,可以直接通过类的字面常量class获取:
public class ReflectTest { @Test public void reflect_class(){ Class<?> clz = Student.class; } }输出结果:无任何输出!
3.特殊情况下,我们在编码时不知道具体的类或实例,那么我们可以通过Class的forName静态方法:
public class ReflectTest { @Test public void reflect_forName(){ try { Class<?> clz = Class.forName("com.sky.javase.Student"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }输出结果: 这是静态代码块!
获取某个类的Class对象可以有多种方式。但“巧妇难为无米之炊”,不管上面三种方式的哪一种,都需要和这个类密切相关的东西,要么类的一个实例对象,要么是这个类,要么是类的完全限定名(包名+类名,类加载器会去类路径下找)。