Java的反射机制


简介

什么是反射?

在运行状态中

  • 对于任意一个类,都能够获取到这个类的所有属性和方法
  • 对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性)

这种动态获取信息以及动态调用对象的方法的功能就称为java语言的反射机制

也就是说,虽然我们获取不到该类的源代码,但是通过该类的.class文件能反射(Reflect)出这些信息

简单来说

反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息

原理

略…

获取.class字节码文件对象的四种方式

1
2
3
4
5
6
7
8
9
10
//方法一
Class clazz1 = Class.forName("my.Student");//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。包名为 my,类名为 Student
//方法二
Class clazz2 = Student.class; //当类被加载成.class文件时,此时Student.java类变成了Student.class,该类处于字节码阶段
//方法三
Student s=new Student(); //实例化Student对象
Class clazz3 = s.getClass(); //通过该类的实例获取该类的字节码文件对象,该类处于创建对象阶段
// 方法四 :通过类的加载器
ClassLoader classLoader = Student.class.getClassLoader();
Class clazz4 = classLoader.loadClass("Student");

反射中常用的方法

  • 获取指定构造器的方法getConstructor(int.class.String.class)
  • 获取全部构造方法getContstructor()
  • 获取指定成员变量getDeclaredField("id")
  • 获取全部成员变量getDeclaredFields()
  • 实例化newInstance()
  • 获取方法getMethod("test",String.class)
    • test为方法名字,String.class为参数类型,无参数可空
  • 执行获得的方法invoke(class,"test")
    • class为一个实例,test是向我们要执行的方法中传入的参数

实例

Runtime类执行任意代码

1
2
3
public static void main(String[] args) throws IOException {
Runtime.getRuntime().exec("calc.exe");
}

可以成功弹出计算器

下面跟进这个Runtime类,看一下到底调用了个啥

1
2
3
4
5
6
7
8
9
private static Runtime currentRuntime = new Runtime();
/*...*/
public static Runtime getRuntime() {
return currentRuntime;
}
...
public Process exec(String command) throws IOException {
return exec(command, null, null);
}

可以看到getRuntime方法将会返回一个Runtime类得实例,在通过这个实例去调用exec方法,进而执行命令

反射执行

1
2
3
4
public static void main(String args[]) throws Exception{
Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null); //得到Runtime.getRuntime()函数
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime,"calc.exe"); //执行函数
}

解析代码

  • Class.forName("java.lang.Runtime")获取Runtime类得字节码对象
  • 调用getMethod方法指定获取getRuntime方法
    • 此处new Class[]{}可省略
  • 通过invoke执行获取到Runtime类得实例
  • 在获取exec方法,calc.exe就是exec方法执行的代码

其实也可以写成一句话

1
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null),"calc.exe");  //执行函数

1608802408155

end

下一篇分析java的经典利用链