freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Java代码审计-反射与类加载机制全解
2023-01-11 19:35:24
所属地 陕西省

反射的思维导图

图片.png

反射

一个需求引出反射,请看下面一个问题。

1、根据配置文件re.properties指定信息,创建对象并调用方法。

classfullpath = com.hspdeu.Cat
method=hi

使用现有的技术,你能做到吗?

2、这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的ocp原则(开闭原则)简单的说,不修改源码,来扩展功能。

3、快速入门com.hspedu.reflection.question

ReflectionQuestion.java

代码展示:

re.properties

classfullpath = com.hspdeu.Cat
method=hi

Cat.java

package com.hspedu;

@SuppressWarnings({"all"})
public class Cat {
    private String name = "招财喵";
    public void hi(){   //常用方法
        System.out.println("hi" + name);
    }
}

ReflectionQuestion.java

package com.hspedu.reflection.question;

import com.hspedu.Cat;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

@SuppressWarnings({"all"})
public class ReflectionQuestion {
    /**
     \* 反射问题的引入
     */
    public static void main(String[] args) throws IOException {
        //根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
        //1、我们使用传统的方法,这里是以new的方式,调用方法
//        Cat cat = new Cat();
//        cat.hi();

        //我们尝试做一做->明白反射
        //1、我们使用properties类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src:\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String method = properties.get("method").toString();
        System.out.println("classfullpath=" + classfullpath + "method=" + method);
        //这里问题来了,这里要new一个对象。
        //而我们这里接收的是字符串,不能new一个字符串。
    }
}

反射机制快速入门

关键代码:

//3、使用反射机制来解决
// //(1)、加载类,返回Class类型的对象cls
Class cls = Class.forName(classfullpath);
// //(2)、通过cls得到你加载的类,com.hspedu.Cat的对象实例
Object o = cls.newInstance();
// //这里查看的是运行类型
System.out.println("o的运行类型=" + o.getClass());
// //(3)、通过cls得到你加载的类com.hspedu.Cat的methodName"hi"
// //的方法对象,即:在反射中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
// //(4)、通过method1 调用方法:即通过方法对象来实现调用的方法
System.out.println("=========================");
// //传统方法 对象.方法(),反射机制 方法.invoke(对象)
method1.invoke(o);

反射的原理图

1、反射机制运行程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法,反射在设计模式和框架底层都会用到。

2、加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。

Java反射机制原理示意图

图片.png

反射机制向相关类

  • Java反射机制可以完成

1、在运行时判断任意一个对象所属的类。

2、在运行时构造任意一个类的对象。

3、在运行时得到任意一个类所具有的成员变量的方法。

4、在运行时调用任意一个对象的成员变量和方法。

5、生成动态代理。

  • 反射相关的主要的类

1、java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象。

2、java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法。

3、java.lang.reflect.Field:代表类的成员变量。Field对象表示某个类的成员变量。

4、java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器。

图片.png

这里在调用方法的时候,出现了错误,是因为,我们的name属性是私有的。

package com.hspedu.reflection.question;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

@SuppressWarnings({"all"})
public class Reflection01 {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        System.out.println("classfullpath=" + classfullpath);
        System.out.println("methodName=" + methodName);
        //这里问题来了,这里要new一个对象。
        //而我们这里接收的是字符串,不能new一个字符串。

//        //3、使用反射机制来解决
//        //(1)、加载类,返回Class类型的对象cls
        Class cls = Class.forName(classfullpath);
//        //(2)、通过cls得到你加载的类,com.hspedu.Cat的对象实例
        Object o = cls.newInstance();
//        //这里查看的是运行类型
        System.out.println("o的运行类型=" + o.getClass());
//        //(3)、通过cls得到你加载的类com.hspedu.Cat的methodName"hi"
//        //的方法对象,即:在反射中,可以把方法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
//        //(4)、通过method1 调用方法:即通过方法对象来实现调用的方法
        System.out.println("=========================");
//        //传统方法 对象.方法(),反射机制 方法.invoke(对象)
        method1.invoke(o);
        //java.lang.reflect.Field:代表的成员变量,Field对象表示某个类
        //的成员方法。
        Field ageField = cls.getField("age");
        System.out.println(ageField.get(o));

        //java.lang.reflect.Constructor:代表构造方法,Constructor对象
        //表示构造器
        Constructor constructor = cls.getConstructor(String.class);
        System.out.println(constructor);
    }
}

反射调用优化

反射的优点和缺点:

1、优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去了底层支撑。

2、缺点:使用了反射基本是解释执行,对执行速度有影响。

反射调用优化-关闭访问检查

1、Method和Field,Constructor对象都有setAccessible()方法。

2、setAccessible作用是启动和禁用访问安全检查的开关

3、参数值为true表示,反射的对象在使用时取消访问检查,提高反射的效率,参数值为false则表示反射的对象执行访问检查。

package com.hspedu.reflection.question;

import com.hspedu.Cat;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;

@SuppressWarnings({"all"})
public class Reflection02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        /**
         \* 传统方法和发射方法执行时间对比
         */
        m1();
        m2();
        m3();
    }
    //传统方法来调用hi
    public static void m1(){
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 9000000 ; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("m1() =" + (end - start));
    }
    //用反射机制调用方法h1
    public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class cls = Class.forName("com.hspedu.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");
        long statr = System.currentTimeMillis();
        for (int i = 0; i < 9000000; i++) {
            hi.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("m2() =" + (end-statr));
    }

    public static void m3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class cls = Class.forName("com.hspedu.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");
        hi.setAccessible(true);
        long statr = System.currentTimeMillis();
        for (int i = 0; i < 9000000; i++) {
            hi.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("m3() =" + (end-statr));
    }
}

运行截图:

图片.png

Class类

基本介绍:

1、Class也是类,因此也继承了Object类[类图]

2、Class类对象不是new出来的,而是系统创建的[演示]

3、对于某个类的Class类对象,在内存中只有一份,因为类只加载一次[演示]

4、每个类的实例都会记得自己是由那个Class实例所生成

5、通过Class可以完整的得到一个类的完整结构,通过一系列API

6、Class对象是存放在堆中的

7、类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等等) https://www.zhihu.com/question/38496907

我们通过类图,来进行查看Class。

图片.png

查看父类。

图片.png

Class类的常用方法。

方法名功能说明
static Class forName(String name)返回指定类名 name 的Class对象
Object newInstance()调用缺省构造函数,返回该Class对象的一个实例
getName()返回此Class对象所表示的实体(类,接口,数组类,基本类型等)名称
Class getSuperClass()返回当前Class对象的父类的Class对象
Class[] getIntterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Class getSuperclass()返回表示此Class所表示的实体的超类的Class
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
Field[] getDeclardeFields()返回Field对象的一个数组
Method getMethod(String name,class...paramTypes)返回一个Method对象,此对象的形参类型为paramType

Class类的常用方法

String str = "com.hspedu.reflection.Person";

//获取到Class类对象,表示不确定的java类型

Class<?> clazz = Class.forName(str);

System.out.println(clazz);//显示该clazz对象是哪个类的Class对象

System.out.println(clazz.getClass());//运行类型

System.out.println(clazz.getPackage.getName());

System.out.println(clazz.getName());

Object obj = clazz.newInstance();//通过反射创建对象

Field field = clazz.getField("name");//通过反射获取属性

field.set(obj,"hspedu");//通过字段对象赋值

Object obj2 = field.get(obj);//获取值

System.out,println(obj2);//输出

代码实例:

Car.java

package com.hspedu;

public class Car {
    public String brand = "宝马";
    public int price = 500000;
    public String color = "黄色";

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

Class02.java

package com.hspedu.reflection.class_;

import com.hspedu.Car;

import java.lang.reflect.Field;

@SuppressWarnings({"all"})
public class Class02 {
    public static <Filed> void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        String str = "com.hspedu.Car";
        //获取的Class类对象,表示不确定的java类型
        Class<?> cls = Class.forName(str);
        //显示该clazz对象是哪个类的Class对象
        System.out.println(cls);
        //运行类型
        System.out.println(cls.getClass());
        //得到包名
        System.out.println(cls.getPackage().getName());
        //得到全类名
        System.out.println(cls.getName());
        //创建反射对象
        Car car = (Car) cls.newInstance();
        System.out.println(car);
        //通过反射获取属性
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));
        brand.set(car,"奔驰");
        System.out.println("==============修改后============");
        System.out.println(brand.get(car));

        //8 希望大家可以得到所有的属性(字段)
        //这里依次输出各个字段属性
        Field[] field = cls.getFields();
        for(Field f : field){
            System.out.println(f.getName());
        }
    }
}

获取Class对象的六种方式

1、前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法,forName()获取,可能抛出ClassNotFoundException,实例:

Class cls1 = Class.forName("java.lang.Cat");

应用场景:多用于配置文件,读取类全路径,加载类。

2、前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高实例:Class cls2 = Cat.class;

应用场景:多用于参数传递,比如通过反射得到对应构造器对象。

3、前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:

Class clazz = 对象.getClass();//运行对象

应用场景:通过创建好的对象,获取Class对象

4、其他方式

ClassLoader c1 = 对象.getClass().getclassLoader();

Class clazz4 = c1.loadClass("类的全类名");

5、基本数据(int,char,boolean,float,double,byte,long,short),按如下方式得到Class类的对

6、基本数据类型对应的包装类,可以通过.TYPE得到Class类对象。

package com.hspedu.reflection.class_;

import com.hspedu.Car;

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        //1、Class.forName
        //通过读取配置文件来获取
        String classALLPath = "com.hspedu.Car";
        Class<?> cls1 = Class.forName(classALLPath);
        System.out.println(cls1);

        //2、类名.class,应用场景:用于参数传递
        Class cls2 = Car.class;
        System.out.println(cls2);

        //3、对象.getClass(),应用场景,有对象实例
        Car car = new Car();
        Class cls3 = car.getClass();
        System.out.println(cls3);

        //4、通过类加载器[4种]来获取到类的Class对象
        //(1)先得到类加载器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
        //(2)通过类加载器得到Class对象
        Class cls4 = classLoader.loadClass(classALLPath);
        System.out.println(cls4);

        //5、基本数据(int,char,boolean,float,double,byte,long,short)
        //按如下方式得到Class类的对象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
        System.out.println(integerClass);

        //6、基本数据类型对应的包装类,可以通过.TYPE得到Class类对象。
        Class<Integer> type = Integer.TYPE;
        Class<Character> type1 = Character.TYPE;

        System.out.println(integerClass.hashCode());
        System.out.println(type.hashCode());
    }
}

那些类型有Class对象

下列类型都有Class对象

1、外部类,成员内部类,静态内部类,局部内部类,匿名内部类

2、interface:接口

3、数组

4、enum:枚举

5、annotation:注解

6、基本数据类型

7、void

ALLTypeClass.java

package com.hspedu.reflection.class_;

import java.io.Serializable;

public class AllTypeClass {
    public static void main(String[] args) {
        //外部类
        Class<String> cls1 = String.class;
        //接口
        Class<Serializable> cls2 = Serializable.class;
        //数组
        Class<Integer> cls3 = Integer.class;
        //二维数组
        Class<float[][]> cls4 = float[][].class;
        //注解
        Class<Deprecated> cls5 = Deprecated.class;
        //枚举
        Class<Thread.State> cls6 = Thread.State.class;
        //基本数据类型
        Class<Long> cls7 = long.class;
        Class<Void> cls8 = void.class;
        Class<Class> cls9 = Class.class;

        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}

类加载

基本说明

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

1、静态加载:编译时加载相关的类,如果没有则报错,依赖性太强。

代码示例:

import java.util.*;

public class ClassLoad_{

	public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		System.out.println("ÇëÊäÈëKEY");
		String key = scanner.next();
		switch(key){
		case "1":
			Dog dog = new Dog();
			dog.cry();
			break;
		case "2":
			System.out.println("ok");
			break;
		default:
			System.out.println("do nothing...");
		}
	}
}

代码截图:

图片.png

这样就是可能你加载不到Dog这个类,但是它在编译的时候,还是会给你报错,这里就是静态加载。

2、动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。

import java.util.*;
import java.lang.reflect.*;

public class ClassLoad_{

	public static void main(String[] args) throws Exception {
		
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入数字:");
		String key = scanner.next();
		switch(key){
		case "1":
			Dog dog = new Dog();
			dog.cry();
			break;
		case "2":
			//反射 -> 动态加载
			Class cls = Class.forName("Person");//加载Person类,动态的进行加载
			Object o = cls.newInstance();
			Method m = cls.getMethod("hi");
			m.invoke(o);
			System.out.println("ok");
			break;
		default:
			System.out.println("do nothing...");
		}
	}
}

//因为new Dog()是静态加载,因此必须编写Dog
//Person类是动态加载,多以,没有编写Person类也不会报错,只有当
//动态加载的时候,才会报错

class Dog{

	public void cry() {
		System.out.println("小狗汪汪叫!");
	}
}

class Person{
	public void hi(){
		System.out.println("okk");
	}
}

代码截图:

图片.png

类加载时机

1、当创建对象时(new)

2、当子类被加载时

3、调用类中的静态成员时

4、通过反射

类的加载流程图

图片.png

类加载时,各个阶段的任务

图片.png

类加载的五个阶段

加载阶段

JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至网络)转化伪二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象。

图片.png

连接阶段-验证

1、目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。

2、包括:文件格式验证(是否以魔数 oxcafebabe开头),元数据验证,字节码验证和符号引用验证[举例说明]

3、可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

连接阶段-准备

1、JVM会在该阶段对静态变量,分配内存并初始化(对应数据类型的默认初始化,如0,0L,null,false等)。这些变量所使用的内存都将在方法区中进行分配

连接阶段-解析

1、虚拟机将常量池内的符号引用替换为直接引用的过程。

Initialization(初始化)

1、到了初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行()方法的过程。

2、()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。[举例说明]

3、虚拟机会保证一个类()方法在多线程环境中被正确的加锁,同步,如果多个线程同时区初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。

通过反射获取类的结构信息

第一组:java.lang.Class类

com.hspedu.reflection

ReflectionUtils.java

1、getName:获取全类名

2、getSimpleName:获取简单类名

3、getFields:获取所有public修饰的属性,包含本类以及父类的

4、getDeclaredFields:获取本类中所有属性

5、getMethods:获取所有public修饰的方法,包含本类以及父类的

6、getDeclaredMethods:获取本类中所有的方法

7、getConstructors:获取所有public修饰的构造器,包含本类

8、getDeclaredConstructors:获取本类中所有的构造器

9、getPackage:以Package形式返回包信息

10、getSuperClass:以Class形式返回父类信息

11、getInterfaces:以Class[]形式返回父类信息

12、getAnnotations:以Annotation[]形式返回注解信息

演示代码如下:

package com.hspedu.reflection.question;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@SuppressWarnings({"all"})
public class ReflectionUtils {
    public static void main(String[] args) {
        /**
         \* 演示如何通过反射获取类的结构信息
         */
    }
    //第一组
    @Test
    public void api_01() throws ClassNotFoundException {
        //得到Class对象
        Class<?> personCls = Class.forName("com.hspedu.reflection.question.Person");
        System.out.println("------------------------------------------");
//        1、getName:获取全类名
        System.out.println(personCls.getName());
        //com.hspedu.reflection.question.Person
        System.out.println("------------------------------------------");
//        2、getSimpleName:获取简单类名
        System.out.println(personCls.getSimpleName());
        System.out.println("------------------------------------------");
//        3、getFields:获取所有public修饰的属性,包含本类以及父类的
        Field[] fields = personCls.getFields();
        for (Field field : fields) {
            System.out.println("本类以及父类的属性=" + field);
        }
//        4、getDeclaredFields:获取本类中所有属性
        System.out.println("------------------------------------------");
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有属性=" + declaredField);
        }
//        5、getMethods:获取所有public修饰的方法,包含本类以及父类的
        System.out.println("------------------------------------------");
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("所有public修饰的方法,包括父类=" + method.getName());
        }
//        6、getDeclaredMethods:获取本类中所有的方法
        System.out.println("------------------------------------------");
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Field declaredField : declaredFields) {
            System.out.println("本类所有的方法=" + declaredField.getName());
        }
//        7、getConstructors:获取所有public修饰的构造器,包含本类
        System.out.println("------------------------------------------");
        Constructor<?>[] constructors = personCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor.getName());
        }
//        8、getDeclaredConstructors:获取本类中所有的构造器
        System.out.println("------------------------------------------");
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor.getName());
        }
//        9、getPackage:以Package形式返回包信息
        System.out.println("------------------------------------------");
        System.out.println(personCls.getPackage());
//        10、getSuperClass:以Class形式返回父类信息
        System.out.println("------------------------------------------");
        Class<?> superclass = personCls.getSuperclass();
        System.out.println(superclass.getName());
//        11、getInterfaces:以Class[]形式返回父类信息
        System.out.println("------------------------------------------");
        Class<?>[] interfaces = personCls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface.getName());
        }
//        12、getAnnotations:以Annotation[]形式返回注解信息
        System.out.println("------------------------------------------");
        Annotation[] annotations = personCls.getAnnotations();
       for (Annotation annotation : annotations) {
             System.out.println(annotation);
        }
    }
}

class A{
    public String hobby;
    public void hi(){}
    public A(){}
}
interface IA{}
interface IB{}
@Deprecated
class Person extends A implements IA,IB{
    //属性
    public String name;
    protected int age;
    String job;
    private double sal;
    //构造器
    public Person(){}
    public Person(String name){}
    //方法
    public  void m1(){

    }
    protected  void m2(){

    }
    void m3(){

    }
    private  void m4(){

    }
}

第二组:java.lang.reflect.Field类

1、getModifiers:以int形式返回修饰符

[说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16]

2、getType:以Class形式返回类型。

3、getName:返回属性名。

第三组:java.lang.reflect.Method类

1、getModifiers:以int形式返回修饰符

[说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16]

2、getReturnTyope:以Class形式获取,返回类型。

3、getName:返回方法名。

4、getParameterTypes:以class[]返回参数类型数据。

第四组:java.lang。reflect.Constructor类。

1、getModifiers:以int形式返回修饰符。

2、getName:返回构造器名(全类名)。

3、getParameterTypes:以Class[]返回参数类型数据。

代码如下:

package com.hspedu.reflection.question;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Period;

@SuppressWarnings({"all"})
public class ReflectionUtils {
    public static void main(String[] args) {
        /**
         \* 演示如何通过反射获取类的结构信息
         */
    }
    //第一组
    @Test
    public void api_01() throws ClassNotFoundException {
        //得到Class对象
        Class<?> personCls = Class.forName("com.hspedu.reflection.question.Person");
        System.out.println("------------------------------------------");
//        1、getName:获取全类名
        System.out.println(personCls.getName());
        //com.hspedu.reflection.question.Person
        System.out.println("------------------------------------------");
//        2、getSimpleName:获取简单类名
        System.out.println(personCls.getSimpleName());
        System.out.println("------------------------------------------");
//        3、getFields:获取所有public修饰的属性,包含本类以及父类的
        Field[] fields = personCls.getFields();
        for (Field field : fields) {
            System.out.println("本类以及父类的属性=" + field);
        }
//        4、getDeclaredFields:获取本类中所有属性
        System.out.println("------------------------------------------");
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有属性=" + declaredField);
        }
//        5、getMethods:获取所有public修饰的方法,包含本类以及父类的
        System.out.println("------------------------------------------");
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("所有public修饰的方法,包括父类=" + method.getName());

        }
//        6、getDeclaredMethods:获取本类中所有的方法
        System.out.println("------------------------------------------");
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Field declaredField : declaredFields) {
            System.out.println("本类所有的方法=" + declaredField.getName());
        }
//        7、getConstructors:获取所有public修饰的构造器,包含本类以及父类的
        System.out.println("------------------------------------------");
        Constructor<?>[] constructors = personCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor.getName());
        }
//        8、getDeclaredConstructors:获取本类中所有的构造器
        System.out.println("------------------------------------------");
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor.getName());
        }
//        9、getPackage:以Package形式返回包信息
        System.out.println("------------------------------------------");
        System.out.println(personCls.getPackage());
//        10、getSuperClass:以Class形式返回父类信息
        System.out.println("------------------------------------------");
        Class<?> superclass = personCls.getSuperclass();
        System.out.println(superclass.getName());
//        11、getInterfaces:以Class[]形式返回父类信息
        System.out.println("------------------------------------------");
        Class<?>[] interfaces = personCls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface.getName());
        }
//        12、getAnnotations:以Annotation[]形式返回注解信息
        System.out.println("------------------------------------------");
        Annotation[] annotations = personCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }

//第二组

@Test
    public void api_02() throws ClassNotFoundException {
        //得到Class对象
        Class<?> personCls = Class.forName("com.hspedu.reflection.question.Person");
       //4、getDeclaredFields:获取本类中所有属性
        System.out.println("------------------------------------------");
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有属性=" + declaredField.getName()
            \+ " 该类的修饰符=" +declaredField.getModifiers()
            \+ " 该属性的类型=" + declaredField.getType());
        }


        //        6、getDeclaredMethods:获取本类中所有的方法
        System.out.println("------------------------------------------");
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("获取本类所有的方法" + declaredMethod.getName()
            \+ " 获取该方法的访问修饰符值=" + declaredMethod.getModifiers()
            \+ " 获取该方法的返回类型=" + declaredMethod.getReturnType());

            //输出当前这个方法的形参数组情况
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该方法的形式参数=" + parameterType);
            }
        }

        //        8、getDeclaredConstructors:获取本类中所有的构造器
        System.out.println("------------------------------------------");
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor.getName());
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println(parameterType);
            }
        }


    }
}

class A{
    public String hobby;
    public void hi(){}
    public A(){}
}
interface IA{}
interface IB{}
@Deprecated
class Person extends A implements IA,IB{
    //属性
    public String name;
    protected int age;
    String job;
    private double sal;
    //构造器
    public Person(){}

    public Person(String name){}
    public Person(String name, int age){}
    //方法
    public  void m1(String name, int age, double sal){

    }
    protected  void m2(){

    }
    void m3(){

    }
    private  void m4(){

    }
}

通过反射创建对象

1、方式一:调用类中的public修饰的无参构造器

2、方式二:调用类中的指定构造器

3、Class类相关方法

  • newlnstance:调用类中的无参构造器,获取对应类的对象。

  • getConstructor(Class...clazz):根据参数列表,获取对应的构造器对象。

  • getDecalaredConstructor(Class...clazz):根据列表参数,获取对应的构造器对象。

4、Constructor类相关方法

  • setAccessible:爆破

  • newInstance(Object...obj):调用构造器

测试案例:

1、通过反射创建类的对象,要求该类中必须有public的无参构造。

2、通过调用某个特定构造器的方式,实现创建某类的对象。

代码示例:

package com.hspedu.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

@SuppressWarnings({"all"})
public class ReflecCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        /**
         \* 演示通过反射机制创建实例
         */
        //1、先获取到User类的Class对象
        Class<?> userClass = Class.forName("com.hspedu.reflection.User");
        //2、通过public的无参构造器创建实例
        Object o = userClass.newInstance();
        System.out.println(o);
        //3、通过public的有参构造器创建实例
        /**
         \* 这里就不能new出来了,而是要先得到它的构造器,然后在创建实例
         */
        //1、先得到对应的实例
        Constructor<?> constructor = userClass.getConstructor(String.class);
        //2、创建实参
        Object xl = constructor.newInstance("小李");
        System.out.println("小李的基本信息=" + xl);
        //4、通过非public的有参构造器创建实例
        //4.1、得到private的构造器对象
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(String.class, int.class);
        //4.2、创建实例
        constructor1.setAccessible(true);//爆破
        Object zsf = constructor1.newInstance("张三丰", 100);
        System.out.println("zsf=" + zsf);
    }
}

class User {
    private int age = 40;
    private String name = "小王";

    public User() { //无参构造器

    }

    public User(String name) { //public的有参构造器
        this.name = name;
    }

    private User(String name, int age) {

    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

通过反射访问类中的成员

访问属性

1、根据属性名获取Field都西昂

Field f = clazz对象.getDeclaredField(属性名);

2、爆破:f.setAccessible(true); //f是Field

3、访问

f.set(o,值);、

syso(f.get(o));

4、如果是静态属性,则set和get中的参数o,可以写成null;

代码示例:

package com.hspedu.reflection;

import java.lang.reflect.Field;

@SuppressWarnings({"all"})
public class ReflecAccessProperty {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        //1、得到Student类对应的Class对象
        Class<?> stuClass = Class.forName("com.hspedu.reflection.Student");
        //2、创建对象
        Object o = stuClass.newInstance();
        System.out.println(o.getClass());
        //3、使用反射得到age 属性对象
        Field age = stuClass.getField("age");
        //4、使用反射操作属性
        age.set(o, 88);
        System.out.println(o);

        //5、使用反射操作name属性
        Field name = stuClass.getDeclaredField("name");
        //对name进行爆破
        name.setAccessible(true);
//        name.set(o, "李四");
        name.set(null,"李四");
        System.out.println(o);

    }
}

class Student {
    public int age;
    private static String name;

    public Student() {

    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                " name=" + name+
                '}';
    }
}

反射爆破操作方法

访问方法:

1、根据方法名和参数列表获取Method方法对象:Method m = clazz.getDeclaredMethod

(方法名,XX.class); //这个可以得到本类的所有方法

2、获取对象:Object o = clazz.newInstance();

3、爆破:m.setAccessible(true);

4、访问:Object returnValue = m.invoke(o,实参列表);

5、注意:如果是静态方法,则invoke的参数o,可以写成null。

# Java代码审计
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录