参考《Java安全漫谈》
反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行状态的时候才动态加载类,对于任意一个类都能够知道这个类所有的属性和方法,并且对于任意一个对象,都能够调用它的方法/访问属性。这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。
java反射机制组成需要重点注意一下的类:
java.lang.reflect.Field
:类的属性对象;
java.lang.reflect.Method 类方法对象
在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识。这个信息会跟踪每个对象所属的类。虚拟机利用运行时类型信息选择要执行的正确方法。
可以使用一个特殊的Java类访问这些信息。保存这些信息的类名为Class
。
能得到Class类,也就是 java.lang.class对象 的三种方法:
System.out.println(cla.class);
System.out.println(cla.getClass());
Class> cla = Class.forName("a.cat"); //包名下的cat类
System.out.println(Class.forName("a.cat"));
一般使用第三种(Class.forName) 方法去动态加载类 ,且使用forName就不需要导入其他类,可以加载我们任意的类。 而使用 .class属性,则需要导入类的包,依赖性强,且容易抛出编译错误。
而使用实例化对象的 getClass() 方法,需要本身创建一个对象,是静态的,就体现不出反射机制意义了,所以我们在获取class对象中, 一般都会使用 Class.forName去获取
拿到了类之后,就可以通过反射来获取该类的成员变量,成员方法,构造函数,实例化类,并且利用invoke 调用方法 实操如下:
- //返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
- getDeclaredField(String name)
- //返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。
- getDeclaredFields()
-
-
- //返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
- getField(String name)
- //返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
- getFields()
例如:
- Class> cls = Class.forName("com.person");
- System.out.println(cls.getName());
-
- Field[] field = cls.getFields();
- for (Field field1 : field) {
- System.out.println(" 属性: "+field1.getName()); //只能获取public 的属性
- }
-
- System.out.println("========");
-
- Field[] declaredFields = cls.getDeclaredFields(); //可以获取类全部属性
- for (Field all : declaredFields) {
- System.out.println("属性" + all.getName());
- }
大致就是 getDeclaredFields() 和getDeclaredField() 可以获取所有的成员变量,下面两个得到的只有 public变量,第一 第三可以传入一个名字 (字符串 ) 来 得到指定的成员变量,而第二第四只能得到一个数组,没法得到指定的 (但是可以遍历 数组,取出所有的遍历)
Class类 获取 Method 方法;
- //返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
- getDeclaredMethod(String name, 类>... parameterTypes);
- //返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。
- getDeclaredMethods(); //数组
- //返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
- getMethod(String name, 类>... parameterTypes);
- //返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
- getMethods();
用法 跟Field 类似。
但是要注意后面的 String name, 类>... parameterTypes
如:
- Class clazz = Class.forName("java.lang.Runtime");
- clazz.getMethod("exec", String.class);
Runtime 类中的 exec 实现了方法的重载, 因此需要使用forMethod就要带上第二个参数,才能准确的找到指定方法的对象。
- //返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
- getDeclaredConstructor(类>... parameterTypes)
- //返回 构造器对象的数组, 构造器对象反映由此 类对象表示的类声明的所有构造函数。
- getDeclaredConstructors()
- //返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数。
- getConstructor(类>... parameterTypes)
- //返回一个包含 构造器对象的数组, 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
- getConstructors()
用法几乎差不多,可以使用 getConstructors 再进行遍历取出
得到地址和类名后,使用newInstance 实例化这个类:
- Class> cls = Class.forName("com.person");
- Object o = cls.newInstance();
- System.out.println(o);
但是这样实例化了这个类呢,会自动调用无参构造器,就需要用到之前得到的Constructor 了。
- Constructor> cons = cls.getDeclaredConstructor(String.class, double.class);
- Object snowy = cons.newInstance("snowy", 1000000000);
- System.out.println(snowy);
反射中的调用,是用方法来调用对象,如:
方法.invoke(对象)
例子, 比如要调用 person 类中的 hi() 方法,首先要得到 hi() 方法的 Method对象,其次 还需要得到 person类的对象 作为invoke的参数
- Class> cls = Class.forName("com.person");//加载类
- Constructor> cons = cls.getDeclaredConstructor(String.class, double.class); //获取构造器
- Object snowy = cons.newInstance("snowy", 123);//实例化对象
- Method m2 = cls.getMethod("m2");//得到Method 对象
- Object name = m2.invoke(snowy); // 调用方法
- //System.out.println(name);
如果调用的是普通方法,第一个参数就是类对象;
如果调用的这个方法是静态方法,第一个参数是类,或者可以护忽略,设置为null
如果得到的Field 、Method、Constructor 是私有属性的话,受到权限影响,不能直接调用或者读取,需要设置 setAccessible(true) ;
setAccessible
方法是AccessibleObejct
类的一个方法,它是Field
、Method
和Constructor
类的公共超类。这个特性是为调式、持久存储和类似机制提供的。
例如,Runtime类的构造函数是私有的,可以这样获取对象
- Class> cls = Class.forName("java.lang.Runtime");
- Constructor> m = cls.getDeclaredConstructor();
- m.setAccessible(true);
- cls.getMethod("exec", String.class).invoke(m.newInstance(),"calc.exe");
JAVA安全基础(一)--类加载器(ClassLoader) - 先知社区 (aliyun.com)
JAVA安全基础(二)-- 反射机制 - 先知社区 (aliyun.com)
《Java安全漫谈》
Java反射学习_bfengj的博客-CSDN博客_java反射学习