反射
IT那个小笔记
类的加载时机
当程序要使用某个类时,如果该类还未被加载到内存中,系统会通过加载,连接,初始化三步来实现对这个类进行初始化
加载
连接
初始化
类什么时候会被加载
类加载器
什么是类加载器classLoader
类加载器分类
什么是反射
创建一个对象的三个阶段
内省
反射
无论是内省还是反射,首先都要获取字节码文件
获取字节码文件的三种方式
/* 第一种用Object类的getClass方法 */
Test t = new Test();
Class tc = t.getClass();
/* 第二种用Class类的静态方法forName */
Class tc = Class.forName("Test类的全限定名称");
/* 第三种通过静态属性class */
Class tc =Test.class;
字节码使用
用字节码来创建对象
1.通过无参构造创建对象
Class tc = Test.class;
Test t = (Test)tc.newInstance()
2.通过有参构造创建对象
Constructor c = tc.getConstructor(Integer.class,String.class);
Test t = (Test)c.newInstance(18,"张三");
因为在反射阶段操作的都是字节码,不知道具体的类型,只有在创建对象的时候才去给实际参数
用字节码获取字段
/* 获取公共字段 */
Class tc = Test.class;
Test t = (Test)tc.newInstance();
Field f = tc.getField("字段名");// 获取字段
f.set(t,值);// 给某个对象的此字段设值
/* 获取私有字段 */
Class tc = Test.class;
Test t = (Test)tc.newInstance();
Field f = tc.getDeclareField("字段名");// 获取私有字段
f.setAccessible(true);// 将私有解开才能访问进去
f.set(t,值);
获取方法也是一样
/* 获取方法 */
Method m = tc.getMethod("方法名");
m.invoke(对象);
/* 有参 */
Method m = tc.getMethod("方法名",Integer.class,String.class);
m.invoke(对象,16,"张三");
/* 私有 */
Method m = tc.getDeclareMethod("方法名");
m.setAccessible(true);
m.invoke(对象);
越过数组的泛型检查
数组如果定义好了泛型就不能添加泛型以外的类型
可以通过反射来去实现添加以外的类型
在一个Integer泛型的数组当中添加字符串类型
/* */
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
/*
设置泛型后就只能添加相应类型的元素
否则编译器就会提示错误,不能去编译
但我们知道泛型,实际上在字节码中并不存在
仅仅是在编译中的语法,让你遵守不能添加别的
在运行时实际上并没有规定,只是你这样写了不然通不过编译
所以我们可以通过获取字节码来跨过泛型
*/
Class ac = Class.forName("java.util.ArrayList");// 获取列表集合的字节码
Method m = ac.getMethod("add",Object.class);// 获取add方法还有一个参数类型填Object
m.invoke(list,"张三");// 这个时候就添加上去了
/*
虽然list对象在被创建时设了泛型为整数,
但只是在编译前、代码编写时有效
实际上并不存在,所以通过字节码获取后就可以添加了
*/