Skip to content

Latest commit

 

History

History
207 lines (144 loc) · 5.91 KB

反射.md

File metadata and controls

207 lines (144 loc) · 5.91 KB

反射

写在前面

当然不可能什么都记录,只记录一些对我有用的

概念

Java反射机制是在运行状态时,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。

Java如何获取一个类

JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息;因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息

1.直接通过一个class的静态变量class获取:

Class cls = String.class;

2.通过该实例变量提供的getClass()方法获取

String s = "Y4";
Class cls = s.getClass();

3.知道一个class的完整类名,可以通过静态方法Class.forName()获取

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

并且说明一点,由于Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例

访问字段

getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。 getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。

package ysoserial.mytest;

import java.lang.reflect.Field;

public class test {
    public static void main(String[] args) throws Exception {
        Class stiClass = StuInfo.class;
        // 获取public字段"score":
        System.out.println(stiClass.getField("age"));
        // 获取继承的public字段"name":
        System.out.println(stiClass.getField("name"));
        // 获取private字段"grade":
        System.out.println(stiClass.getDeclaredField("money"));
        // 获得值,name.get里面参数需要该类对象,而不是.class
        Field name = stiClass.getField("name");
        System.out.println(name.get(stiClass.newInstance()));
        // 设置值
        StuInfo stuInfo = new StuInfo();
        Field money = stiClass.getDeclaredField("money");
        money.setAccessible(true);
        money.set(stuInfo,2333333);
        System.out.println(stuInfo);




    }
}

class StuInfo extends PersonInfo{
    public int age;
    private int money;

    @Override
    public String toString() {
        return "StuInfo{" +
            "name=" + name +
            ", money=" + money +
            '}';
    }
}

class PersonInfo{
    public String name = "Y4tacker";
}

获取方法

Class类提供了以下几个方法来获取Method

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

简单来通过反射来使用substring

String name = "Y4tacker";
Method substring = String.class.getMethod("substring", int.class);
System.out.println(substring.invoke(name,3));

如果调用的方法是静态方法。那么invoke方法传入的第一个参数永远为null

// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "23333");
System.out.println(n);

获取构造方法

通过Class实例获取Constructor的方法如下:

  • getConstructor(Class...):获取某个publicConstructor
  • getDeclaredConstructor(Class...):获取某个Constructor
  • getConstructors():获取所有publicConstructor
  • getDeclaredConstructors():获取所有Constructor

调用非publicConstructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。

反射执行命令

       Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),"calc");

反射修改被final关键字修饰的成员变量

// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");

// 设置modifiers修改权限
modifiers.setAccessible(true);

// 修改成员变量的Field对象的modifiers值
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);

// 修改成员变量值
field.set(类实例对象, 修改后的值);

因此我们这里给一个具体实例

Class aClass = Class.forName("bcell.Test");
Test Test = (Test) aClass.newInstance();
Field field = aClass.getDeclaredField("TEST");
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field,field.getModifiers()&~Modifier.FINAL);//fianl标志位置0
field.set(Test,"wocao");
System.out.println(field.get(Test));

其他

但是虽然这么多,其中较为重要的⽅法:

获取类的⽅法: forName

实例化类对象的⽅法: newInstance

获取函数的⽅法: getMethod

执⾏函数的⽅法: invoke

其中比较好玩的是第一种方式

Class.forName(className)
// 等于
Class.forName(className, true, currentLoader

第⼆个参数表示是否初始化,在 forName 的时候,构造函数并不会执⾏,而是执⾏类初始化。他会执行static{}静态块里面的内容

public class test {
    public static void main(String[] args) throws Exception {
        Class.forName("mytest.Calc");

    }
}

class Calc{
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}