java反射

  • ~9.73K 字
  1. 1. 什么是反射
  2. 2. Java反射组成相关的类
  3. 3. 实例化对象的方法
    1. 3.0.1. 实例化对象的getClass()方法
    2. 3.0.2. 使用类的 .class 方法
    3. 3.0.3. Class.forName(String className):动态加载类
  • 4. 获取成员变量Field
  • 5. 获取成员方法 Method
  • 6. 获取构造函数 Constructor
  • 7. 命令执行
  • 8. Java反射修改字段
    1. 8.1. private
  • 9. final
    1. 9.1. 直接赋值
    2. 9.2. 间接修改
    3. 9.3. static+final

  • 什么是反射

    反射是大多数语言都必不可少的组成部分,对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有),拿到的⽅法可以调⽤,总之通过“反射”,我们可以将 Java 这种静态语⾔附加上动态特性。

    Java反射组成相关的类

    java.lang.Class:类对象;

    java.lang.reflect.Constructor:类的构造器对象;

    java.lang.reflect.Field:类的属性对象;

    java.lang.reflect.Method:类的方法对象;

    实例化对象的方法

    实例化对象的getClass()方法

    如果上下⽂中存在某个类的实例 obj,那么我们可以通过 obj.getClass 来获取它的类。

    使用类的 .class 方法

    如果你已经加载了某个类,只是想获取到它的 java.lang.Class 对象,那么就直接拿它的 class 属性即可。这个⽅法其实不属于反射。

    Class.forName(String className):动态加载类

    如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName 来获取,后续要利用的话是需要实例化的。

    获取成员变量Field

    获取成员变量Field位于 java.lang.reflect.Field 包中

    Field[] getFields() :获取所有 public 修饰的成员变量

    Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

    Field getField(String name) 获取指定名称的 public 修饰的成员变量

    Field getDeclaredField(String name) 获取指定的成员变量

    获取成员方法 Method

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法

    Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法

    //第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型

    Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法

    Method[] getDeclaredMethods() // 获取该类中的所有方法

    获取构造函数 Constructor

    1
    2
    3
    4
    5
    6
    7
    Constructor<?>[] getConstructors() :只返回public构造函数

    Constructor<?>[] getDeclaredConstructors() :返回所有构造函数

    Constructor<> getConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的public构造函数

    Constructor<> getDeclaredConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的构造函数

    命令执行

    获取 class 实例 之后

    • 获取类名:forName()
    • 创建对应类型的实例:newInstance()
    • 获取字段的值:get()、设置字段的值:set()
    • 获取方法:getMethod()、调用方法:invoke()

    基本知识就是这些,如何执行命令呢,举个例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package src;

    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    public class FinalReflectionCalc {
    public static void main(String[] args) throws Exception{
    Class c1 = Class.forName("java.lang.Runtime");//创建一个runtime的方法
    Method c2 = c1.getDeclaredMethod("getRuntime");//获取getRuntime的方法
    c2.setAccessible(true);//使getRuntime可访问,因为getRuntime是私有属性
    Object o1 = c2.invoke(null);//调用静态方法,无需实例
    Method m1 = c1.getDeclaredMethod("exec",String.class);//获取exec方法
    m1.invoke(o1,"calc");//弹计算器
    }
    }

    这是调用静态方法,不需要实例

    当然也可以实现一个新实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package src;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    public class FinalReflectionCalc {
    public static void main(String[] args) throws Exception{
    Class c1 = Class.forName("java.lang.Runtime");//获取Runtime的Class对象
    Constructor c2=c1.getDeclaredConstructor();//获取Runtime类的无参构造函数
    c2.setAccessible(true);//使getRuntime可访问
    Object c3=c2.newInstance();//创建Runtime的新实例
    Method exec = c1.getDeclaredMethod("exec", String.class);
    exec.invoke(c3,"calc");//弹计算器

    }
    }

    Java反序列化基础篇-02-Java反射与URLDNS链分析参考链接,写的很清晰

    Java反射


    Java反射修改字段

    private

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package src;

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    public class PrivateReflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, InvocationTargetException {
    Class c = Class.forName("src.PrivatePerson");
    Object m = c.newInstance();
    Method PrintMethod = c.getDeclaredMethod("printName");
    PrintMethod.invoke(m);
    Field nameField = c.getDeclaredField("name");
    nameField.setAccessible(true);
    nameField.set(m, new StringBuilder("aaaaa"));
    PrintMethod.invoke(m);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package src;

    public class PrivatePerson {
    private StringBuilder name = new StringBuilder("lll");

    public void printName() {
    System.out.println(name);
    }
    }

    static单独出现的话,用getDeclaredField也可以

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package src;

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    public class PrivateReflect {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, InvocationTargetException {
    Class c = Class.forName("src.PrivatePerson");
    Object m =c.newInstance();
    Method f = c.getDeclaredMethod("printName");
    f.invoke(m);
    Field ff=c.getDeclaredField("name");
    ff.setAccessible(true);
    ff.set(m,new StringBuilder("lvzhouhang"));
    f.invoke(m);
    }
    }

    final

    final 字段能否修改,有且取决于字段是直接赋值还是间接赋值(编译时赋值和运行时赋值的区别)。直接赋值是指在创建字段时就对字段进行赋值,并且值为 JAVA 的 8 种基础数据类型或者 String 类型,而且值不能是经过逻辑判断产生的,其他情况均为间接赋值。

    直接赋值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package src;

    public class FinalStraightPerson {

    private final String name = "lvzhouhang";
    public final int age = 21-1;

    public void printInfo() {
    System.out.println(name+" "+age);

    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package src;

    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    public class FinalStraightReflect {
    public static void main(String[] args) throws Exception {
    Class c = Class.forName("src.FinalStraightPerson");
    Object m = c.newInstance();
    Method printMethod = c.getDeclaredMethod("printInfo");
    printMethod.invoke(m);

    Field nameField = c.getDeclaredField("name");
    Field ageField = c.getDeclaredField("age");
    nameField.setAccessible(true);
    ageField.setAccessible(true);
    nameField.set(m,"lvzhouhang");
    ageField.set(m,"20");

    printMethod.invoke(m);
    }
    }

    这样通过反射修改会报错

    1
    2
    3
    4
    5
    6
    Exception in thread "main" java.lang.IllegalArgumentException: Can not set final int field src.FinalStraightPerson.age to java.lang.String
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at sun.reflect.UnsafeQualifiedIntegerFieldAccessorImpl.set(UnsafeQualifiedIntegerFieldAccessorImpl.java:100)
    at java.lang.reflect.Field.set(Field.java:764)
    at src.FinalStraightReflect.main(FinalStraightReflect.java:18)

    为什么呢?

    因为JVM在编译时期, 就把final类型的String进行了优化, 在编译时期就会把String处理成常量,只要是让name的值经过运行才能获得, 那么就不会被处理为常量。

    间接修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package src;

    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    public class InDirectReflect {
    public static void main(String[] args) throws Exception {
    Class c = Class.forName("src.InDirectPerson");
    Object m = c.newInstance();
    Method printMethod = c.getDeclaredMethod("printInfo");
    printMethod.invoke(m);

    Field nameField = c.getDeclaredField("name");
    Field ageField = c.getDeclaredField("age");
    Field sexField = c.getDeclaredField("sex");
    nameField.setAccessible(true);
    ageField.setAccessible(true);
    sexField.setAccessible(true);
    nameField.set(m,"lvzhouhang");
    ageField.set(m,200);
    sexField.set(m,new StringBuilder("female"));
    printMethod.invoke(m);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package src;

    public class InDirectPerson {
    private final StringBuilder sex = new StringBuilder("male");
    // 经过逻辑判断产生的变量赋值
    public final int age = (null!=null?20:20);
    // 通过构造函数进行赋值
    private final String name;
    public InDirectPerson(){
    name = "lvzhouhang";
    }

    public void printInfo() {
    System.out.println(name+" "+age+" "+sex);

    }
    }

    这样就修改成功了

    static+final

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package src;

    public class finalstaticreflection {
    private final static StringBuilder name = new StringBuilder("lvzhouhang");

    public void printName() {
    System.out.println(name);
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package src;

    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    public class finalstaticflectioncalc {
    public static void main(String[] args) throws Exception{
    finalstaticreflection c = new finalstaticreflection();
    Method m = c.getClass().getDeclaredMethod("printName");
    m.invoke(c);
    Field f = c.getClass().getField("name");
    f.setAccessible(true);
    f.set(c,new StringBuilder("lllllvzhouhang"));
    m.invoke(c);
    }

    }

    这样使修改不成功的,但是还是可以修改,通过反射将name字段取出来后将final修饰符去掉就可以修改

    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
    package src;

    import com.sun.org.apache.xpath.internal.operations.Mod;

    import java.io.File;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;

    public class finalstaticflectioncalc {
    public static void main(String[] args) throws Exception{
    finalstaticreflection c = new finalstaticreflection();
    Method m = c.getClass().getDeclaredMethod("printName");
    c.printName();
    Field f = c.getClass().getDeclaredField("name");
    f.setAccessible(true);
    Field ff = f.getClass().getDeclaredField("modifiers");
    ff.setAccessible(true);
    ff.setInt(f,f.getModifiers() & ~Modifier.FINAL);//去掉final修饰符
    f.set(c,new StringBuilder("lllllvzhouhang"));
    ff.setInt(f,f.getModifiers() & ~Modifier.FINAL);//加上final修饰符
    c.printName();
    }

    }

    这样就成功修改了

    赞助喵
    非常感谢您的喜欢!
    赞助喵
    分享这一刻
    让朋友们也来瞅瞅!