反射就是在程序运行时动态加载类、方法或属性,在coding阶段是直接不知道对象是谁
普通场景创建一个类的过程是先判断类的Class对象是否加载到了内存中,Class对象已经加载到内存(字节码已经加载)就为实例对象分配内存,根据不同的垃圾收集器分配内存,GC使用复制算法或者标记整理算法内存规整场景下,只需要将指针向空闲的一边移动,这种分配方式称指针碰撞,另外一种内存存在碎片,碎片内存会保存在一个“空闲列表”中,从空闲列表中取出一块可容纳目标对象的内存区域来存储对象。如果Clas对象没有加载到内存中,就执行类的加载过程,经过加载、连接和初始化完成类的加载,再为实例对象分配内存。
反射通过Class对象创建实例对象,并获取类的属性和方法。
通过new对象,经过编译器安全校验,确认具体对象创建叫静态编译;反射这种方式叫做动态编译。

获取字节码Class对象方式:

1
2
//通过forName方法获得(带有包名的真实类路径)
Class<?> mClass = Class.forName("java.util.ArrayList");
1
2
//直接通过类.class
Class<?> mClass = ArrayList.class;
1
2
//通过对象.getClass()
Class<?> mClass = list.getClass();

获取构造函数:

1
2
3
4
5
//得到所有构造函数
Constructor<?>[] constructors = mClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
1
2
//获得特定构造函数
Constructor<ArrayList> constructor = mClass.getConstructor(null);

获取方法:

1
2
//得到共有的public方法 包括继承的方法
Method[] methods= mClass.getMethods();
1
2
//得到类所有方法 包括public private protect
Method[] methods = mClass.getDeclaredMethods();
1
2
3
4
5
6
7
8
9
10
11
//得到特定对象,并执行实例该方法
Method method = mClass.getMethod("add", Object.class);
if (method != null) {
try {
method.invoke(list, 1245);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

获取属性:

1
2
3
4
5
//获取所有共有的public变量,包括继承的属性
Field[] fields = mClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
1
2
3
4
5
//获取所有申明的变量 public private protect
Field[] fields = mClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
1
2
//设置变量值
field.set(list,12l);

创建实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//通过反射得到的构造函数创建ArrayList实例,添加了两个元素ok和12
Constructor<?>[] constructors = mClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}

try {
ArrayList l = (ArrayList) constructors[1].newInstance();
l.add("ok");
l.add(12);
System.out.println(l.size());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}

反射忽略了权限检查、语法检查(上面ArrayList变量l可以添加ok字符串也可以添加整型数12),破坏了封装性。

反射的用途:

1.用在IOC容器,用来创建对象;
2.编译阶段跨模块无法调用的场景;
3.Gson中变量复制(不需要get set方法的原因)。