反射就是在程序运行时动态加载类、方法或属性,在coding阶段是直接不知道对象是谁
普通场景创建一个类的过程是先判断类的Class对象是否加载到了内存中,Class对象已经加载到内存(字节码已经加载)就为实例对象分配内存,根据不同的垃圾收集器分配内存,GC使用复制算法或者标记整理算法内存规整场景下,只需要将指针向空闲的一边移动,这种分配方式称指针碰撞,另外一种内存存在碎片,碎片内存会保存在一个“空闲列表”中,从空闲列表中取出一块可容纳目标对象的内存区域来存储对象。如果Clas对象没有加载到内存中,就执行类的加载过程,经过加载、连接和初始化完成类的加载,再为实例对象分配内存。
反射通过Class对象创建实例对象,并获取类的属性和方法。
通过new对象,经过编译器安全校验,确认具体对象创建叫静态编译;反射这种方式叫做动态编译。
获取字节码Class对象方式:
1 2
| Class<?> mClass = Class.forName("java.util.ArrayList");
|
1 2
| Class<?> mClass = ArrayList.class;
|
1 2
| 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
| Method[] methods= mClass.getMethods();
|
1 2
| 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
| Field[] fields = mClass.getFields(); for (Field field : fields) { System.out.println(field); }
|
1 2 3 4 5
| Field[] fields = mClass.getDeclaredFields(); for (Field field : fields) { System.out.println(field); }
|
创建实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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方法的原因)。
全文完。