Java反射机制
反射的概念
-
Java反射机制是在运行状态时,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。
-
通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
-
要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
获取class对象方式
Java反射操作的是java.lang.Class
对象,所以我们需要先想办法获取到Class对象,通常我们有如下几种方式获取一个类的Class对象:
1.Class.forName("全类名") //如:Class.forName("com.tyut.TestHelloWorld")
2.类名.class //如:com.tyut.TestHelloWorld.class
3.对象.getClass() //TestHelloWorld test = new TestHelloWorld();
// test.getClass();
4.通过类加载器获得Class对象
// classLoader.loadClass("com.tyut.TestHelloWorld");
-
Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
多用于配制文件,将类名定义在配置文件中。读取文件,加载类。
-
类名.class: 通过类名的属性class获取
多用于参数的传递
-
对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。
//获取Runtime类Class对象代码片段: String className = "java.lang.Runtime"; Class runtimeClass1 = Class.forName(className); Class runtimeClass2 = java.lang.Runtime.class; Class runtimeClass3 = ClassLoader.getSystemClassLoader().loadClass(className);
成员变量&构造方法&方法对象的应用
// Field:成员变量 1.设置值 void set(Object obj,Object value) 2.获取值 get(Object obj) 3.忽略访问权限修饰符的安全检查 setAccessible(true) 暴力反射 // Constructor:构造方法 创建对象: T newInstance(Object... initargs) 如果使用空参数构造方法创建对象,操作可简化:Class对象的newInstance方法 // Method:方法对象: 执行方法: Object invoke(Object obj,Object... args) 获取方法名称: String getName:获取方法名
定义一个Person类,利用三种反射方式获取class对象
//定义Person类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/*利用三种反射方式获取class对象
1、Class.forName("全类名")
2、类名.class
3、对象.getClass()
*/
public class reflect {
public static void main(String[] args) throws ClassNotFoundException {
//Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
Class cls1 = Class.forName("Person");
System.out.println(cls1);//class Person
//类名.class: 通过类名的属性class获取
Class cls2 = Person.class;
System.out.println(cls2);//class Person
//对象.getClass():getClass()方法在Object类中定义着。
Person person = new Person();
Class cls3 = person.getClass();
System.out.println(cls3);//class Person
// == 比较3个对象
System.out.println(cls1==cls2);//true
System.out.println(cls2==cls3);//true
}
}
反射对象的使用
获取成员变量们
Field [] getFields() // 获取所有public修饰的成员变量
Field [] getField(String name) // 获取指定名称的public修饰的成员变量
Field [] getDeclaredFields() //获取所有的成员变量,不考虑修饰符
Field [] getDeclaredFields(String name) //获取指定的成员变量,不考虑修饰符
//Demo 获取成员变量们,定义Person类,利用反射获取成员变量们
public class Person {
public int a;
public String b;
protected String c;
private String name;
private int age;
private String d;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a=" + a +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
public void eat() {
System.out.println("eat....");
}
public void eat(String food) {
System.out.println("eat..." + food);
}
}
//利用反射获取成员变量们
import java.lang.reflect.Field;
public class reflect {
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class<Person> personClass = Person.class;
//1.Field[] getFields()获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(field); //public int Person.a
//public java.lang.String Person.b
}
System.out.println("=====================");
//2.Field getField(String name)
Field b = personClass.getField("b");
//获取成员变量a的值
Person p = new Person();
Object value = b.get(p);
System.out.println(value); // null
//设置a的值
b.set(p,"张三");
System.out.println(p);//Person{name='null', age=0, a=0, b='张三', c='null', d='null'}
System.out.println("================");
//Field [] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
/*
private java.lang.String Person.name
private int Person.age
public int Person.a
public java.lang.String Person.b
protected java.lang.String Person.c
private java.lang.String Person.d
*/
//Field getDeclaredField(String name)
Field d = personClass.getDeclaredField("d");
//忽略访问权限修饰符的安全检测
d.setAccessible(true); //暴力反射
Object value2 = d.get(p);
System.out.println(value2); //null
//Field [] getDeclaredFields(String name)
}
}
获取构造方法们
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class reflect2 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//0.获取Person的Class对象
Class<Person> personClass = Person.class;
//Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
//创建对象
Person p = constructor.newInstance("张三", 28);
System.out.println(p);//Person{name='张三', age=28, a=0, b='null', c='null', d='null'}
Constructor<Person> constructor2 = personClass.getConstructor();
Person p2 = constructor2.newInstance();
System.out.println(p2);//Person{name='null', age=0, a=0, b='null', c='null', d='null'}
Person p3 = personClass.newInstance();
System.out.println(p3);
}
}
获取成员方法们
Method[] getMethods()
Method getMethod(String name,类<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name,类<?>... parameterTypes)
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflect3 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//0.获取Person的Class对象
Class<Person> personClass = Person.class;
//获取指定名称的方法
Method eat_method = personClass.getMethod("eat");
//执行方法
Person p = new Person();
eat_method.invoke(p);//eat....
Method eat_method2 = personClass.getMethod("eat",String.class);
eat_method2.invoke(p,"饭");//eat...饭
Method[] methods = personClass.getMethods();
for (Method method : methods) {
//System.out.println(method);
String name = method.getName();
System.out.println(name);
}
//获取类名
String className = personClass.getName();
System.out.println(className);
}
}
获取类名
String getName()
反射java.lang.Runtime
java.lang.Runtime
因为有一个exec
方法可以执行本地命令,所以在很多的payload
中我们都能看到反射调用Runtime
类来执行本地系统命令,通过学习如何反射Runtime
类也能让我们理解反射的一些基础用法。
不使用反射执行本地命令代码片段:
// 输出命令执行结果
System.out.println(IOUtils.toString(Runtime.getRuntime().exec("whoami").getInputStream(), "UTF-8"));
反射Runtime执行本地命令代码片段:
//System.out.println(IOUtils.toString(Runtime.getRuntime().exec("whoami").getInputStream(), "UTF-8"));
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class test {
public static void main(String[] args) throws Exception {
// 获取Runtime类对象
Class runtimeClass1 = Class.forName("java.lang.Runtime");
// 获取构造方法
Constructor constructor = runtimeClass1.getDeclaredConstructor();
constructor.setAccessible(true);
// 创建Runtime类示例,等价于 Runtime rt = new Runtime();
Object runtimeInstance = constructor.newInstance();
// 获取Runtime的exec(String cmd)方法
Method runtimeMethod = runtimeClass1.getMethod("exec", String.class);
// 调用exec方法,等价于 rt.exec(cmd);
Process process = (Process) runtimeMethod.invoke(runtimeInstance, "ifconfig");
// 获取命令执行结果
InputStream in = process.getInputStream();
// 输出命令执行结果
System.out.println(IOUtils.toString(in, "UTF-8"));
}
}
反射调用Runtime
实现本地命令执行的流程如下:
- 反射获取
Runtime
类对象(Class.forName("java.lang.Runtime")
)。 - 使用
Runtime
类的Class对象获取Runtime
类的无参数构造方法(getDeclaredConstructor()
),因为Runtime
的构造方法是private
的我们无法直接调用,所以我们需要通过反射去修改方法的访问权限(constructor.setAccessible(true)
)。 - 获取
Runtime
类的exec(String)
方法(runtimeClass1.getMethod("exec", String.class);
)。 - 调用
exec(String)
方法(runtimeMethod.invoke(runtimeInstance, cmd)
)。