反射简介
RTTI(Run-Time Type Identification)运行时类型识别,作用是在运行时识别一个对象的类型和类的信息。
主要有两种方式:一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。
Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
反射相关的主要API
java.lang.Class
:代表一个类
java.lang.reflect.Method
:代表类的方法
java.lang.reflect.Field
:代表类的成员变量
java.lang.reflect.Constructor
:代表类的构造器
类加载
类的加载过程:
-
加载:class字节码加载到内存,并将这些静态数据转换成方法区的运行时数据结构,然后生成代表这个类的java.lang.Class对象。
-
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
包括:验证、准备、解析
-
初始化:执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
}
}
class A {
static int m = 100;
static {
System.out.println("A类静态代码块初始化");
m = 300;
}
public A() {
System.out.println("A类的无参构造初始化");
}
}
输出结果为:
A类静态代码块初始化 A类的无参构造初始化 100
发生类的初始化的时间
-
类的主动引用(一定会发生类的初始化):
-
当虚拟机启动,先初始化main方法所在的类
-
new一个类的对象
-
调用类的静态成员(除了final常量)和静态方法
-
使用java.lang.reflect包的方法对类进行反射调用
-
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
-
-
类的主动引用(一定会发生类的初始化):
-
当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
-
通过数组定义类引用,不会触发此类的初始化
-
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
-
类加载器
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
Class类
-
Class类,存在于JDK的
java.lang
包中。Class类的实例表示java应用运行时的类(class ans enum
)或接口(interface and annotation
)。 -
每个java类运行时都在JVM里表现为一个class对象。数组同样也被映射为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
-
每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
-
Class类只存私有构造函数,因此对应Class对象只能又JVM创建和加载。
-
Class类的对象的作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要。
反射的使用
Class类及其用法
在类加载的时候,JVM会创建一个class对象
class对象是可以说是反射中最常用的,获取class对象的方式的主要有三种
-
根据类名:类名.class
-
根据对象:对象.getClass()
-
根据全限定类名:Class.forName(全限定类名)
forName() //获取Class对象的一个引用 //例如引用的类还没有加载,即该类的第一个对象没有生成,就加载了这个类。 Object-getClass() //返回表示该对象的实际类型的Class引用。 getName() //获取全限定类名(包括包名) getSimpleName() //获取类名(不包括包名) getCanonicalName() //获取全限定类名(包括包名) //返回更容易理解的表示,主要用于toString或log打印 //大多数情况下和getName一样,但是对于内部类、数组等类型的表示形式就不同了 isInterface() //判断Class对象是否是一个接口 getInterfaces() //返回Class对象所引用的类所实现的所有接口 getSuperclass() //返回Class对象所引用的类所继承的直接基类。 //应用该方法可在运行时发现一个对象完整的继承结构。 newInstance() //返回一个Oject对象,是实现“虚拟构造器”的一种途径。 //使用该方法创建的类,必须带有无参的构造器。 getFields() //获得某个类的所有公共(public)的字段,包括继承自父类的公共字段 //类似的还有getMethods和getConstructors。 getDeclaredFields() //获得某个类的自己声明的字段 //即包括public、private和proteced,默认但是不包括父类声明的任何字段。 //类似的还有getDeclaredMethods和getDeclaredConstructors。
Constructor类及其用法
Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。