反射(Reflection)是Java提供一种在运行时,动态操作类的方法。
在java中,无论是类(Class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type),void类型都包含一个特殊的实例对象,就是java.lang.Class类实例。
java.lang.Class类没有public的构造函数,java.lang.Class类实例只能有JVM自动产生,每次JVM加载Class的时候,JVM就会为其自动生成一个java.lang.Class类实例。
在java中,可以有两个方法获取类的Class实例。
1. Object类有个getClass()方法,可以获取类的java.lang.Class实例。
2. 每个类有个public的静态变量class。即使是int,long,void这些内置类型也是有class静态变量的,但数组是个比较特殊的,只能通过getClass()方法获取Class变量。如果对数组变量进行.class操作,则会有Unknown Class编译错误。
public class ClassTest {
public static void main(String[] args) {
Class testClass1 = ClassTest.class;
System.out.println("Class name:"+testClass1.getName());
//Class name:com.shuanghu.learn.reflection.ClassTest
Class testClass2 = ClassTest.class;
System.out.println("Class name:"+testClass2.getName());
//Class name:com.shuanghu.learn.reflection.ClassTest
int [] arr = new int[2];
System.out.println("Class name:"+arr.getClass());
//Class name:class [I
//arr.class;
//Unknown Class
}
}
通过java.lang.Class类可以判断一个类的类型。主要可以判断一个类是否是内部类(member class),局部类(local class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type)等类型。java.lang.Class提供了很多成员函数来支持这些判断。
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
enum TestEnum{
}
interface TestInterface{
}
@Target(ElementType.METHOD)
@interface TestAnnotation {
String value() default "";
}
public class ClassTest {
class InnerClass{
}
public static void main(String[] args) {
class LocalClass{
}
System.out.println("是否枚举(enum):" + TestEnum.class.isEnum());
System.out.println("是否接口(interface):" + TestInterface.class.isInterface());
System.out.println("是否内置类型(Primitive type):" + int.class.isPrimitive());
System.out.println("是否是内部类:" + InnerClass.class.isMemberClass());
System.out.println("是否local类:" + LocalClass.class.isLocalClass());
System.out.println("是否是注解:" + TestAnnotation.class.isAnnotation());
int [] intArr = new int[2];
//数组特殊,只支持getClass()获取Class,不支持.class这种静态变量的方式
System.out.println("是否数组:" + intArr.getClass().isAnnotation());
}
}
通过java.lang.Class类可以加载类。在Java中,只有在需要使用一个类的时候,这个类才会被加载。所以,不是所以定义了的类,都会被JVM加载。
java.lang.Class类提供了两个静态方法
public static Class> forName(String name) throws ClassNotFoundException;
public static Class> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException;
java.lang.Class的forName通过传入一个类的全名,将类加载到虚拟机。这两个方法的区别就是,单参数forName方法默认是进行初始化的,会执行类的static区域,其实就是执行了forName(className, true,ClassLoader.getClassLoader(Reflection.getCallerClass()))。三参数的forName方法,可以指定类的加载器,以及是否初始化。
package com.shuanghu.learn.reflection;
class DemoLoad1{
static {
System.out.println("This is DemoLoad1 class.");
}
}
class DemoLoad2{
static {
System.out.println("This is DemoLoad2 class.");
}
}
class DemoLoad3{
static {
System.out.println("This is DemoLoad3 class.");
}
}
public class ClassTest {
public static void test() throws ClassNotFoundException {
DemoLoad1 c1 = null;
DemoLoad2 c2 = null;
DemoLoad3 c3 = null;
System.out.println("Begin load class DemoLoad1...");
Class.forName("com.shuanghu.learn.reflection.DemoLoad1");
Class.forName("com.shuanghu.learn.reflection.DemoLoad2", false, Thread.currentThread().getContextClassLoader());
System.out.println("Begin init class DemoLoad2...");
new DemoLoad2();
}
public static void main(String[] args) throws ClassNotFoundException {
test();
}
}
执行上面的代码,会有如下结果:
Begin load class DemoLoad1...
This is DemoLoad1 class.
Begin init class DemoLoad2...
This is DemoLoad2 class.
从上面结果可以看出,虽然我们定义了三个类DemoLoad1,DemoLoad2和DemoLoad3,但实际上,只有两个类被JVM加载了,即DemoLoad1和DemoLoad2。从执行结果里看,如果仅仅声明类的变量,不进行实际对象的赋值,JVM是不会真实加载类的,如DemoLoad3。单参数版本的forName函数,在类被加载后,即会立马执行类static区域;三参数版本的forName函数,则可以控制类static区域不执行。
Class.forName最常见的一个应用就是jdbc里,通过Class.forName(driverClass)加载不同的java数据库驱动。
java中创建对象,最常见的方式就是使用new关键字创建。但new并不是唯一的方式。
java.lang.Class有个newInstance方法,可以生成新的对象。但使用newInstance方法生成对象,必须有默认构造函数,或者public,protected的无参构造函数。如果类只有private构造函数,则用newInstance时,会抛出IllegalAccessException异常;如果类只有有参构造函数,则会抛出InstantiationException异常。
class DemoClass1{
protected DemoClass1(){
System.out.println("Demo class1 is constructing");
}
}
class DemoClass2{
private DemoClass2(){
System.out.println("Demo class2 is constructing");
}
}
class DemoClass3{
private DemoClass3(String param){
System.out.println("Demo class3 is constructing.Param:"+param);
}
}
public class ClassTest {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
DemoClass1.class.newInstance();
DemoClass2.class.newInstance();
DemoClass3.class.newInstance();
}
}
虽然newInstance方法会抛出IllegalAccessException和InstantiationException异常,但不代表,这两种情况下就无法通过class创建对象了,后面会有其他方法解决这个问题。
仅仅通过java.lang.Class类是无法描述java类的全部信息的。所以,java的java.lang.reflect包里,提供了更多的类。
类名称 | 作用 |
---|---|
java.lang.reflect.Array | 描述java的array数组,可以动态访问和新建数据 |
java.lang.reflect.Constructor | 描述java中类的构造函数 |
java.lang.reflect.Field | 描述java类的成员变量 |
java.lang.reflect.Method | 描述java类的方法 |
上面这些描述类信息的类,java.lang.Class都提供了相应的获取成员函数。