0%

java反射

什么是反射

反射是大多数语言都必不可少的组成部分,对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有),拿到的⽅法可以调⽤,总之通过“反射”,我们可以将 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();
}

}

这样就成功修改了