Java反射 反射机制 Java的反射机制是指在运行状态中,对于任何一个类都能知道其所有的属性和方法,并且对任意一个对象都能调用他的任意方法,这种动态获取信息以及调用对象的方法称为Java的反射机制。
反射机制是java反序列化的重中之重,Java安全可以从反序列化漏洞开始说起,反序列化漏洞⼜可以从反射开始说起。万物有阴就有阳,有反射肯定也会有“正射”,正射就是我们正常通过new去实例化类,然后用实例化好的对象进行操作。反射可以获取对象的类,类可以通过反射获取所有方法,通过反射修改属性和调用方法。
通过反射获取类的三种方式:
代码还是用presion类,然后新建一个文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.lang.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Arrays;public class RefeltionTest { public static void main (String[] args) throws Exception { Presion p = new Presion(); System.out.println(p); } }
先来看getClass()跟进方法看
返回该Object的运行时间类,被final修饰,会返回类型类,类型类指的是代表一个类型的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.lang.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Arrays;public class RefeltionTest { public static void main (String[] args) throws Exception { Presion p = new Presion(); Class c = p.getClass(); System.out.println(c); } }
返回了对象p的类
我们在定义一个类的时候会用到一个class的关键字,这个关键字和Class的关系就是 写完一个类编译保存到硬盘里面时会有一个presion.class对象,定义的类名称不同 .class文件名也不同,此时的这个Class对应的就是这个.class对象,包含了与类有关的信息。
跟进去看之后发现这个Class是实现了序列化接口的,Class里面还有一个重要的方法forName();
forName的参数是类名,返回你传入类名的原型类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import java.lang.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Arrays;public class RefeltionTest { public static void main (String[] args) throws Exception { Presion p = new Presion(); Class c = Class.forName("Presion" ); System.out.println(c); } }
第三种就是使用.class方法获取类
通过反射创建对象的两种方式:
Class对象的newInstance()方法; 默认调用无参构造器
若没有无参构造器,则先getConstructor(Class…args);,con.newInstance(Object…args);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.lang.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Arrays;public class RefeltionTest { public static void main (String[] args) throws Exception { Presion p = new Presion(); Constructor constructor = c.getConstructor(String.class ,int .class ) ; Presion presion = (Presion) constructor.newInstance("ly0n" ,21 ); System.out.println(presion); } }
为什么传入的是String.class,int.class可以跟进getConstructor看,他的参数就是Class<?>,跟进方法去看代码里面有注释可以理解然后对照一下就明白了
代码执行完后就会生成一个对象
通过反射获得及修改对象的属性值:
Class对象getField(String 属性名)方法
f.get(Object obj) 获取具体对象的属性值
f.set(Object obj,value) 更改属性
加了s也就是复数的意思,然后类型就变成数组,就是获取对象所有的属性值单不包括私有属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.lang.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Arrays;public class RefeltionTest { public static void main (String[] args) throws Exception { Presion p = new Presion(); Class c = p.getClass(); Field[] presionfields = c.getFields(); for (Field f:presionfields){ System.out.println(f); } } }
得到的结果
在presion中是有两个属性的
这也就是刚刚上面说的不包括私有属性的原因,要获取私有属性就需要getDeclaredFields方法
更改属性的值具体代码实现
1 2 3 4 5 6 Field namefield2 = c.getField("name" ); namefield2.set(presion,"lsdasd" ); Field namefield1 = c.getDeclaredField("age" ); namefield1.setAccessible(true ); namefield1.set(presion,20 ); System.out.println(presion);
通过反射调用方法
Class对象getMethod(String 方法名,Class…args);
m.invoke(Object 对象名,Object…args);
使用getMethod方法和使用getField方法大同小异
尝试获取类里面的方法并打印出来
1 2 3 4 Method[] getMethod = c.getMethods(); for (Method m:getMethod){ System.out.println(m); }
包含了挺多继承下来的方法。也包括自己写的一个方法
使用getMethod去获取单个方法,需要注意的点是getMethod的参数是两个一个是方法名,一个是Class类
1 2 Method getMethod = c.getMethod("action" , String.class ) ; getMethod.invoke(presion,"asdasdasd" );
成功调用了action方法