跳到主要内容

java反射

什么是反射?  

Java 反射(Reflection)就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。(摘自网络)

反射能做什么?

由于我们能够知道这个类的所有属性和方法,并且可以调用其方法与属性,那么我们就可以在外部,不通过修改类的形式来给类增加额外自定义功能。

在一些框架开发中,可以更灵活的编写代码,在运行时装配无需针对原类进行大幅度改动,降低代码耦合度。

在安卓逆向中,反射的主要作用就是寻找到某个类,去注入我们的代码,以便查看调用前后的参数与结果,也称之为 hook。

反射常用 API

获取 Class 对象

在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象,获取 Class 类对象以下方法:

1、使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

Class cls = Class.forName("java.lang.String");

2、使用 .class 方法。

这种方法只适合在编译前就知道操作的 Class。

Class cls = String.class;

3、使用类对象的 getClass() 方法。

String str = new String("Hello");
Class cls = str.getClass();

4、ClassLoader.loadClass()

前提:已经获取到 ClassLoader 的情况下(Person 是定义好的类,其中String.class.getClassLoader()获取到得为 null)

ClassLoader clsl = Person.class.getClassLoader();
Class<?> cls = clsl.loadClass("Person");

基本数据类型的类对象获取 int.class Integer.TYPE 得到int

包装类的 Class 类对象获取 Integer.class 得到 class java.lang.Integer

哪些有 Class 对象

并非所有 java 对象都有 Class 对象,获取方式如上

  • 外部类
  • 内部类

内部类的获取方式通过$连接外部类与内部类,多个内部类也可通过$1,$2 依次获取

Class cls1 = Class.forName("OutClass$InnerClass");
Class cls2 = Class.forName("OutClass$1");
  • 接口 =>interface DemoI
  • 数组 => class [Ljava.lang.String;
  • 枚举 enum
  • Thread.State
  • 注解 anntation
  • 基本数据类型
  • 包装类
  • void

创建类对象(与获取构造函数)

1、通过 Class 对象的 newInstance 方法(无法传参)

Person p = Person.class.newInstance();
// 相当于 Person p = new Person();

2、通过 Constructor 对象的 newInstance() 方法 (可传参数)

可以传参数,但需要知道传入参数类型,以确定哪个构造函数。

Constructor<Person> constructor = Person.class.getConstructor(String.class);
Person p = constructor.newInstance("wenhao");

如果构造函数是私有方法,则通过getDeclaredConstructor获取 Constructor

同时设置是否访问 constructor.setAccessible(true) 才可访问

Constructor<Person> constructor = Person.class.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Person p = constructor.newInstance("wenhao");

getParamerTypes 获取参数类型数组(Class [])

要获取私有属性,私有方法或私有构造器,则必须使用有 declared 关键字的方法。

获取类属性

  • getField 只可获取公有属性
Field nameField = Person.class.getField("name");
String name =(String) nameField.get(p);

设置属性值

Field nameField = Person.class.getField("name");
nameField.set(p,"wenhao12");

设置静态属性值 set 第一个参数给 null 即可

Field nameField = Person.class.getField("name");
nameField.set(null,"wenhao12");
  • getDeclaredField 只可获取所有属性
  • getFields 获取所有共有属性
  • getDeclaredFields 获取所有属性
  Field[] fields = Person.class.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}

获取类方法

  • getMethod 获取

参数一为方法名,其余参数为参数类型

调用通过method.invoke调用,参数一为对象,其余参数为实参

Method method = Person.class.getMethod("say", String.class);
method.invoke(p, "hello")

如果是静态方法,invoke 第一个参数可传入 null

  • getDeclaredMethod 可获取私有方法 (也需要 setAccessible)
Method method = Person.class.getDeclaredMethod("say", String.class);
method.setAccessible(true);
method.invoke(p, "hello");
  • getMethods 获取所有公有方法
  • getDeclaredMethods获取所有方法
Method[] methods = Person.class.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}

获取父类

  • getSuperclass

接口无父类

获取内部类

  • getClasses
Class<?>[] classes = Person.class.getClasses();
System.out.println(classes[0]);
  • getDeclaredClasses 获取所有内部类(包括私有)

获取接口

前提:实现(implements)一个接口

Class<?>[] interfaces = Person.class.getInterfaces();
System.out.println(interfaces.length);

其他方法

官方文档 Class (Java Platform SE 8 ) (oracle.com)

大致常用的方法如上,其余的 Class 类的方法还有

  • getName 获取全类名
  • getSimpleName 获取简单类名
  • getModifiers 获取标识符
  • getAnnotations 获取注解
  • getPackage 获取包名

具体代码就不演示了。