什么是反射 反射是大多数语言都必不可少的组成部分,对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有),拿到的⽅法可以调⽤,总之通过“反射”,我们可以将 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) Method getDeclaredMethod (String name, 类<?>... parameterTypes) Method[] getMethods() 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" ); Method c2 = c1.getDeclaredMethod("getRuntime" ); c2.setAccessible(true ); Object o1 = c2.invoke(null ); Method m1 = c1.getDeclaredMethod("exec" ,String.class); 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" ); Constructor c2=c1.getDeclaredConstructor(); c2.setAccessible(true ); Object c3=c2.newInstance(); 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); f.set(c,new StringBuilder ("lllllvzhouhang" )); ff.setInt(f,f.getModifiers() & ~Modifier.FINAL); c.printName(); } }
这样就成功修改了