Java学习----反射机制

Java学习——反射机制

一、前言

​ 最近又是 OAID 获取又是加固,都避不开 Java 反射 这个技术,于是来系统学一下。

二、开始

Java 反射是什么?

  • Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息

  • Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁

  • 反射是实现动态加载的技术之一。

反射的优缺点

  • 优点:在运行时获得类的各种内容,进行反编译,对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
  • 缺点:反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

反射的作用:反射机制在实现壳程序动态加载被保护程序时非常关键,它可以突破默认的权限访问控制,访问安卓系统默认情况下禁止用户代码访问的以及不公开的部分。

反射的本质

  • 正常类的加载过程

​ 执行Student student=new Student(),向JVM请求创建student实例;
​ JVM寻找Student.class文件并加载到内存中
​ JVM创建Student对应的Class对象(一个类只对应一个Class对象)
​ JVM创建student实例

对于同一个类而言,无论有多少个实例,都只对应一个Class对象。

Java 反射的本质: 获取Class对象后,反向访问实例对象。

image-20250819164901967

反射的入口——Class 类

反射相关文件

Java.lang.Class;
Java.lang.reflect.Constructor;
Java.lang.reflect.Field;
Java.lang.reflect.Method;
Java.lang.reflect.Modifier;

JDK 中,主要由以下类来实现 Java 反射机制,这些类都位于 java.lang.reflect 包中:

  • Class类: 代表一个类

  • Constructor 类: 代表类的构造方法

  • Field 类: 代表类的成员变量(属性)

  • Method类: 代表类的成员方法

反射的入口——Class 类:

  1. Class 类的对象称为类对象
  2. Class 类是 Java 反射机制的起源和入口
    • 用于获取与类相关的各种信息
    • 提供了获取类信息的相关方法
    • Class 类继承自 Object 类
  3. Class类是所有类的共同的图纸
    • 每个类有自己的对象,好比图纸和实物的关系
    • 每个类也可看做是一个对象,有共同的图纸 Class,存放类的结构信息,比如类的名字、属性、方法、构造方法、父类和接口,能够通过相应方法取出相应信息

实例类,用于后续操作

// 示例类
class Person implements Runnable{
    public String name;
    private int age;
 
    //默认构造函数和有参构造函数
    public Person() {}
    private Person(String str){
        System.out.println("Private constructor:"+str);
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    // 不同访问权限的方法
    public void introduce() {
        System.out.println("我是" + name + ",年龄" + age);
    }
 
    private void privateMethod(String name,int age) {
 
        System.out.println("这是Person的私有方法,"+"name="+name+","+"age="+age);
    }
 
    @Override
    public void run() {
        System.out.println("Hello World");
    }
}
反射获取 Class

非动态加载时,可通过 .class属性或实例 .getClass()方法获取 Class 类

动态加载时,可使用 Class.forName() 和 ClassLoader.loadClass() 加载并获取类对象

//1. 获取类对象
// 动态加载
Class<?> clazz= Class.forName("MyUnidbgScripts.Person"); // 通过类的完整名加载
Class<?> clazz2=ClassLoader.getSystemClassLoader().loadClass("MyUnidbgScripts.Person");// 通过classloader加载
// 非动态加载
Class<?> clazz3=Person.class;
Class<?> clazz4=new Person().getClass();
System.out.println("Load Class:");
System.out.println(clazz);
System.out.println(clazz2);
System.out.println(clazz3);
System.out.println(clazz4);
System.out.println();
 
//2. 从类对象获取类的各种信息
System.out.println("Class info:");
System.out.println(clazz.getName());       // 完整类名
System.out.println(clazz.getSimpleName()); // 类名
System.out.println(clazz.getSuperclass()); // 父类类对象
System.out.println(Arrays.toString(clazz.getInterfaces()));    //接口类对象数组
System.out.println();

输出如下:

Load Class:
class MyUnidbgScripts.Person
class MyUnidbgScripts.Person
class MyUnidbgScripts.Person
class MyUnidbgScripts.Person
 
Class info:
MyUnidbgScripts.Person
Person
class java.lang.Object
[interface java.lang.Runnable]
反射获取 Constructor
  • class.getConstructor(Class<?>… ParameterTypes) 获取class类指定参数类型的public构造方法
  • class.getConstructors() 获取class类中的所有public权限的构造方法
  • class.getDeclaredConstructor(Class<?>… ParameterTypes) 获取class类中的任意构造方法
  • class.getDeclaredConstructors() 获取class类中的所有构造方法
//3. 获取构造方法
// 获取无参构造方法(默认构造方法)
System.out.println("Get constructor:");
Constructor<?> constructor=clazz.getConstructor();
System.out.println(constructor);
System.out.println();
 
// 获取public构造方法
System.out.println("Get public constructors:");
Constructor<?>[] constructors=clazz.getConstructors();
System.out.println(Arrays.toString(constructors));
System.out.println();
 
// 获取所有构造方法
System.out.println("Get all constructors:");
constructors=clazz.getDeclaredConstructors();
System.out.println(Arrays.toString(constructors));
System.out.println("Print All Constructors:");
for(Constructor<?> cons:constructors){
    System.out.println("constructor: "+cons);
    System.out.println("\tname: "+cons.getName()+
            "\n\tModifiers: "+Modifier.toString(cons.getModifiers())+
            "\n\tParameterTypes: "+Arrays.toString(cons.getParameterTypes()));
}
System.out.println();

输出如下

Get constructor:
public MyUnidbgScripts.Person()
 
Get public constructors:
[public MyUnidbgScripts.Person(java.lang.String,int), public MyUnidbgScripts.Person()]
 
Get all constructors:
[public MyUnidbgScripts.Person(java.lang.String,int), private MyUnidbgScripts.Person(java.lang.String), public MyUnidbgScripts.Person()]
Print All Constructors:
constructor: public MyUnidbgScripts.Person(java.lang.String,int)
    name: MyUnidbgScripts.Person
    Modifiers: public
    ParameterTypes: [class java.lang.String, int]
constructor: private MyUnidbgScripts.Person(java.lang.String)
    name: MyUnidbgScripts.Person
    Modifiers: private
    ParameterTypes: [class java.lang.String]
constructor: public MyUnidbgScripts.Person()
    name: MyUnidbgScripts.Person
    Modifiers: public
    ParameterTypes: []
反射获取 Field
  • class.getField(FieldName) 获取class类中的带public声明的FieldName变量
  • class.getFields() 获取class类中的带public声明的所有变量
  • class.getDeclaredField(FieldName) 获取class类中的FieldName变量
  • class.getDeclaredFields() 获取class类中的所有变量
//3. 获取属性
// 获取所有public属性
System.out.println("Get public fields:");
Field[] fields=clazz.getFields();
System.out.println(Arrays.toString(fields));
System.out.println();
 
// 获取所有属性
System.out.println("Get all fields:");
fields=clazz.getDeclaredFields();
System.out.println(Arrays.toString(fields));
System.out.println("Print all fields:");
for(Field field:fields){
    System.out.println("field: "+field);
    System.out.println("\ttype: "+field.getType()+
            "\n\tname: "+field.getName());
}
System.out.println();
 
System.out.println("Get specific field:");
// 获取public权限的指定属性
Field field=clazz.getField("name");
System.out.println(field);
// 获取任意权限的指定属性
field=clazz.getDeclaredField("age");
System.out.println(field);

输出如下

Get public fields:
[public java.lang.String MyUnidbgScripts.Person.name]
 
Get all fields:
[public java.lang.String MyUnidbgScripts.Person.name, private int MyUnidbgScripts.Person.age]
Print all fields:
field: public java.lang.String MyUnidbgScripts.Person.name
    type: class java.lang.String
    name: name
field: private int MyUnidbgScripts.Person.age
    type: int
    name: age
 
Get specific field:
public java.lang.String MyUnidbgScripts.Person.name
private int MyUnidbgScripts.Person.age
反射获取Method
  • class.getMethod(MethodName,…ParameterTypes) 获取指定方法名和指定参数的public方法
  • class.getMethods() 获取class类中所有public方法
  • class.getDeclaredMethod(MethodName,…ParameterTypes) 获取class类中指定方法名和指定参数的任意方法
  • class.getDeclaredMethods() 获取class类的所有方法
//4. 获取方法
System.out.println("Get public methods:");
Method[] methods=clazz.getMethods();   // 注意会获取所实现接口的public方法
System.out.println(Arrays.toString(methods));
System.out.println();
 
System.out.println("Get all methods:");
methods=clazz.getDeclaredMethods();    // 获取所有声明的方法
System.out.println(Arrays.toString(methods));
System.out.println();
 
System.out.println("Print all methods:");
for(Method method:methods){
    System.out.println("method: "+method);
    System.out.println("\tname: "+method.getName());
    System.out.println("\treturnType: "+method.getReturnType());
    System.out.println("\tparameterTypes: "+Arrays.toString(method.getParameterTypes()));
}
System.out.println();
 
// 获取public的指定方法
Method method=clazz.getMethod("introduce");
System.out.println(method);
// 获取任意权限的指定方法
method=clazz.getDeclaredMethod("privateMethod",String.class,int.class);
System.out.println(method);
System.out.println();

输出如下

Get public methods:
[public void MyUnidbgScripts.Person.run(), public void MyUnidbgScripts.Person.introduce(), public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException, public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public java.lang.String java.lang.Object.toString(), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
 
Get all methods:
[public void MyUnidbgScripts.Person.run(), public void MyUnidbgScripts.Person.introduce(), private void MyUnidbgScripts.Person.privateMethod(java.lang.String,int)]
 
Print all methods:
method: public void MyUnidbgScripts.Person.run()
    name: run
    returnType: void
    parameterTypes: []
method: public void MyUnidbgScripts.Person.introduce()
    name: introduce
    returnType: void
    parameterTypes: []
method: private void MyUnidbgScripts.Person.privateMethod(java.lang.String,int)
    name: privateMethod
    returnType: void
    parameterTypes: [class java.lang.String, int]
 
public void MyUnidbgScripts.Person.introduce()
private void MyUnidbgScripts.Person.privateMethod(java.lang.String,int)
反射创建对象
  1. 通过Class.newInstance() 调用无参构造方法创建实例 不能传递参数
  2. 通过Constructor.newInstance() 调用指定构造方法创建实例 可传递参数
//5. 反射创建对象
System.out.println("Create instance by reflection:");
//5.1 Class.newInstance() 要求Class对象对应类有无参构造方法,执行无参构造方法创建实例
System.out.println("Create instance by Class.newInstance():");
Object obj=clazz.newInstance();
System.out.println(obj.toString());
System.out.println();
 
//5.2 Constructor.newInstance() 通过Class获取Constructor,再创建对象,可使用指定构造方法
System.out.println("Create instance by Constructor.newInstance():");
Constructor<?> cons=clazz.getConstructor();// 获取无参构造方法
obj=cons.newInstance();
System.out.println(obj.toString());
cons=clazz.getDeclaredConstructors()[0];// 获取有参构造方法
obj=cons.newInstance("张三",18);
System.out.println(obj.toString());
System.out.println();

输出如下

Create instance by reflection:
Create instance by Class.newInstance():
MyUnidbgScripts.Person@30dae81
 
Create instance by Constructor.newInstance():
MyUnidbgScripts.Person@1b2c6ec2
MyUnidbgScripts.Person@4edde6e5
反射操作属性
  1. Class.getField(FieldName) 获取指定名称的public属性

  2. Class.getDeclaredField(FieldName) 获取指定名称的任意属性

  3. Field.get(Object obj) 获取指定实例的值

  4. Field.set(Object obj,Object value) 设置指定实例的值

  5. Field.setAccessible(true) 突破属性权限控制

    //6. 反射操作属性
     System.out.println("Access field by reflection:");
     Field nameField=clazz.getField("name");
     nameField.set(obj,"王五");    // 修改指定对象的指定属性
     Field ageField=clazz.getDeclaredField("age");
     ageField.setAccessible(true);// 突破权限控制
     ageField.set(obj,20);
     System.out.println(nameField.get(obj));// get方法获取字段值
     System.out.println(ageField.get(obj));
    

输出如下

Access field by reflection:
王五
20
反射调用方法
  1. Class.getMethod(String name,Class<?>… parameterTypes) 获取指定名称和参数类型的public方法
  2. Class.getDeclaredMethod(String name,Class<?>… parameterTypes) 获取指定名称和参数类型的方法
  3. Method.setAccessible(true) 突破访问权限控制
  4. Method.invoke(Object obj,Object… args) 调用指定实例的方法,可传递参数
//7. 反射调用方法
 System.out.println("Run method by reflection:");
 Method introduceMethod=clazz.getMethod("introduce");
 introduceMethod.invoke(obj); //person.introduce()
 Method privateMethod=clazz.getDeclaredMethod("privateMethod",String.class,int.class);// person.privateMethod("赵四",19)
 privateMethod.setAccessible(true);
 privateMethod.invoke(obj,"赵四",19);

输出如下

Run method by reflection:
我是王五,年龄20
这是Person的私有方法,name=赵四,age=19
封装反射类

封装Reflection.java反射类便于后续使用,提供以下功能:

  1. 调用静态/实例方法
  2. 访问静态/实例字段
package com.example.androidshell;
 
import android.util.Log;
 
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
public class Reflection {
    private static final String TAG="glass";
    public static Object invokeStaticMethod(String class_name,String method_name,Class<?>[] parameterTypes,Object[] parameterValues){
        try {
            Class<?> clazz = Class.forName(class_name); //反射获取Class类对象
            Method method = clazz.getMethod(method_name,parameterTypes);//反射获取方法
            method.setAccessible(true);//突破权限访问控制
            return method.invoke(null,parameterValues);//反射调用,静态方法无需指定所属实例,直接传参即可
        }catch (Exception e){
            Log.d(TAG, e.toString());
            return null;
        }
    }
 
    public static Object invokeMethod(String class_name,String method_name,Object obj,Class<?>[] parameterTypes,Object[] parameterValues)
    {
        try {
            Class<?> clazz = Class.forName(class_name);
            Method method = clazz.getMethod(method_name,parameterTypes);
            method.setAccessible(true);//突破权限访问控制
            return method.invoke(obj,parameterValues);// 反射调用,动态方法需要指定所属实例
        }catch (Exception e)
        {
            Log.d(TAG, e.toString());
            return null;
        }
    }
 
    public static Object getField(String class_name,Object obj,String field_name)
    {
        try {
            Class<?> clazz = Class.forName(class_name);
            Field field = clazz.getDeclaredField(field_name);
            field.setAccessible(true);
            return field.get(obj);  //获取实例字段,需要指定实例对象
        }catch(Exception e)
        {
            Log.d(TAG, e.toString());
            return null;
        }
    }
 
    public static Object getStaticField(String class_name,String field_name)
    {
        try {
            Class<?> clazz = Class.forName(class_name);
            Field field = clazz.getDeclaredField(field_name);
            field.setAccessible(true);
            return field.get(null);
        }catch (Exception e)
        {
            Log.d(TAG, e.toString());
            return null;
        }
    }
 
    public static void setField(String class_name,String field_name,Object obj,Object value)
    {
        try {
            Class<?> clazz = Class.forName(class_name);
            Field field = clazz.getDeclaredField(field_name);
            field.setAccessible(true);
            field.set(obj,value);
        }catch (Exception e)
        {
            Log.d(TAG, e.toString());
        }
    }
 
    public static void setStaticField(String class_name,String field_name,Object value)
    {
        try {
            Class<?> clazz = Class.forName(class_name);
            Field field = clazz.getDeclaredField(field_name);
            field.setAccessible(true);
            field.set(null,value);
        }catch (Exception e){
            Log.d(TAG, e.toString());
        }
    }
}

三、参考

大佬文章
[原创]Android从整体加固到抽取加固的实现及原理-Android安全-看雪-安全社区|安全招聘|kanxue.com


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1621925986@qq.com

💰

×

Help us with donation