java 反射笔记
1.反射的概念
Reflection(反射)是被视为动态语言
的关键,反射机制允许程序在运行期间
借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
Java反射机制提供的功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
优点:
-
提高了Java程序的灵活性和扩展性,
降低了耦合性
,提高自适应
能力 -
允许程序创建和控制任何类的对象,无需提前
硬编码
目标类
缺点:
- 反射的
性能较低
。 -
反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
-
反射会
模糊
程序内部逻辑,可读性较差
。
2.理解Class类并获取Class实例
要想解剖
一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API:
- java.lang.Class
- java.lang.reflect.*
所以,Class对象是反射的根源。
2.1 理解Class
在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
Class可以理解为描述类的类,可以用它获取任意类的构造器,属性和方法
2.2 获取Class类的实例(四种方法)
方式1:要求编译期间已知类型
前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
实例:
Class clazz = String.class;
方式2:获取对象的运行时类型
前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
实例:
Class clazz = "xxxobject".getClass();
方式3:可以获取编译期间未知的类型
前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
实例:
Class clazz = Class.forName("java.lang.String");
方式4:其他方式
前提:可以用系统类加载对象或自定义加载器对象加载指定路径下的类型
实例:
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass("类的全类名");
再举例:
public void test1() throws ClassNotFoundException {
// 1、调用运行时类的静态属性:class
Class<User> clazz1 = User.class;
System.out.println(clazz1);
// 2、调用运行时类的getClass()方法
User user1 = new User();
Class clazz2 = user1.getClass();
System.out.println(clazz2);
System.out.println(clazz1 == clazz2); // true
// 3、调用Class的的静态方法forName(String className),比较常用
String className = "chapter15_reflect.exer02_class.User"; // 全类名
Class clazz3 = Class.forName(className);
System.out.println(clazz3);
// 4、使用类的加载器
Class clazz4 = ClassLoader.getSystemClassLoader().loadClass("chapter15_reflect.exer02_class.User");
System.out.println(clazz4);
System.out.println(clazz3==clazz4); // true
}
2.3 哪些类型可以有Class对象
简言之,所有Java类型!
(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类 (2)interface:接口 (3)[]:数组 (4)enum:枚举 (5)annotation:注解@interface (6)primitive type:基本数据类型 (7)void
public void test2(){
Class clazz1 = Object.class;
Class clazz2 = Comparable.class;
Class clazz3 = String[].class;
Class clazz4 = int[][].class;
Class clazz5 = ElementType.class;
Class clazz6 = Override.class;
Class clazz7 = int.class;
Class clazz8 = void.class;
Class clazz9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class clazzA = a.getClass();
Class clazzB = b.getClass();
System.out.println(clazzA);
System.out.println(clazzB);
// 只要元素的维度和类型一样,就是同一个Class
System.out.println(clazzA == clazzB); // true
}
3 类的加载和ClassLoader的理解
3.1 类的生命周期
类在内存中完整的生命周期:加载-->使用-->卸载。其中加载过程又分为:装载、链接、初始化三个阶段。
3.2 类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、链接、初始化三个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载。
类的加载又分为三个阶段:
(1)装载(Loading)
将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成
(2)链接(Linking)
①验证Verify:确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。
②准备Prepare:正式为类变量(static)分配内存并设置类变量默认初始值
的阶段,这些内存都将在方法区中进行分配。
③解析Resolve:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
(3)初始化(Initialization)
-
执行
类构造器<clinit>()方法
的过程。类构造器<clinit>()方法
是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。 -
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
-
虚拟机会保证一个
类的<clinit>()方法
在多线程环境中被正确加锁和同步。
3.3 类加载器ClassLoader
类加载器的作用
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类加载器的分类(以JDK8为例)
JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)
和自定义类加载器(User-Defined ClassLoader)
。
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。无论类加载器的类型如何划分,在程序中我们最常见的类加载器结构主要是如下情况:
4.反射的应用
有了Class对象,能做什么?
4.1 应用1:创建运行时类的对象
这是反射机制应用最多的地方。创建运行时类的对象有两种方式:
方式1:直接调用Class对象的newInstance()方法
要 求: 1)类必须有一个无参数的构造器。2)类的构造器的访问权限需要足够。
方式2:通过获取构造器对象来进行实例化
方式一的步骤:
1)获取该类型的Class对象 2)调用Class对象的newInstance()方法创建对象
方式二的步骤:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器 2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。 3)通过Constructor实例化对象。
如果构造器的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)