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实现本地命令执行的流程如下:

  1. 反射获取Runtime类对象(Class.forName("java.lang.Runtime"))。
  2. 使用Runtime类的Class对象获取Runtime类的无参数构造方法(getDeclaredConstructor()),因为Runtime的构造方法是private的我们无法直接调用,所以我们需要通过反射去修改方法的访问权限(constructor.setAccessible(true))。
  3. 获取Runtime类的exec(String)方法(runtimeClass1.getMethod("exec", String.class);)。
  4. 调用exec(String)方法(runtimeMethod.invoke(runtimeInstance, cmd))。
posted @ 2022-01-06 10:13  lalalaxiaoyuren  阅读(56)  评论(0编辑  收藏  举报