Java基础之反射

一、什么是反射

JAVA机制反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象,总的来说:反射就是把java类中的各种成分映射成一个个的Java对象。

二、反射机制的相关工具包

java.lang.reflect.*;

三、反射机制相关的重要的类

含义
java.lang.Class代表整个字节码。代表一个类型,代表整个类。
java.lang.reflect.Method代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor代表字节码中的构造方法字节码。代表类中的构造方法。
java.lang.reflect.Field代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

四、讲解前准备

public class Person {

    /*变量:姓名*/
    public String name;

    /*变量:年龄*/
    private Integer age;

    /*构造方法*/
    public Person(){}
    public Person(String name,Integer age){
        this.name=name;
        this.age=age;
    }
    /*getter setter方法*/
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }

	@Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
    /*通用方法*/
    public String sayHello(){
        return "Hello,my name is "+this.name;
    }
    
    /*私有方法*/
    private String privateMethod(){
        return "this is private method";
    }
}

五、获取Class字节码

(1)为什么要获取Class字节码

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象

(2)如何获取Class字节码

以下三种方式:

  • Class.forName(“完整类名带包名”)
  • 对象.getClass()
  • 任何类型.class

六、通过反射实例化对象

ClassObject.newInstance()

注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。否则会抛出java.lang.InstantiationException异常。
例子:

    @Test
    public void ReflectTest01() throws ClassNotFoundException, InstantiationException, IllegalAccessException {//实例初始化
        //通过反射机制获取class,并用其进行实例化
        Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
        // 通过class获取实例
        Object personObject = personClass.newInstance();
        System.out.println(personObject); // ===>com.xunmeng.reflect.Person@1540e19d
    }

七、Class.forName注意点

如果你只是希望一个类的静态代码块执行,其它代码一律不执行,可以使用这个方法,它执行会导致类加载,类加载时,静态代码块执行。

八、反射对类的属性操作

(1)Class类方法

方法名备注
public T newInstance()创建对象
public String getName()返回完整类名带包名
public String getSimpleName()返回类名
public Field[] getFields()返回类中public修饰的属性
public Field[] getDeclaredFields()返回类中所有的属性
public Field getDeclaredField(String name)根据属性名name获取指定的属性
public native int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Method[] getDeclaredMethods()返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors()返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)根据方法形参获取指定的构造方法
public native Class<? super T> getSuperclass()返回调用类的父类
public Class<?>[] getInterfaces()返回调用类实现的接口集合

例子:

    @Test
    public void ReflectTest02() throws ClassNotFoundException, InstantiationException, IllegalAccessException {//Class类方法
        //通过反射机制获取class,并用其进行实例化
        Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");

        // 通过class获取实例
        Object personObject = personClass.newInstance();
        System.out.println(personObject); // ===>com.xunmeng.reflect.Person@1540e19d

        //获取完整类名带包名
        String personAllClassName = personClass.getName();
        System.out.println(personAllClassName); //===>com.xunmeng.reflect.Person
        //获取类名
        String simpleName = personClass.getSimpleName();
        System.out.println(simpleName);// ===>Person

        //获取中public修饰的属性
        Field[] fields = personClass.getFields();
        Arrays.stream(fields).forEach(field -> {
            System.out.println(field.getName());
        });// ===>[name]
    }

(2)Field类方法【属性操作方法】

方法名备注
public String getName()返回属性名
public int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getType()以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value)设置属性值
public Object get(Object obj)读取属性值

例子:

    @Test
    public void ReflectTest03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {//属性操作方法
        //通过反射机制获取class,并用其进行实例化
        Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
        //获取所有的属性
        Field[] declaredFields = personClass.getDeclaredFields();
        Arrays.stream(declaredFields).forEach(field -> {
            System.out.println(field.getName());
        });// ===>[name,age]

        Arrays.stream(declaredFields).forEach(field -> {
            System.out.println(field.getType());
        });// ===>["class java.lang.String","class java.lang.Integer"]

        //属性赋值
        // 1.先创建一个实例对象
        // 通过class获取实例
        Object personObject = personClass.newInstance();
        //2.设置相关属性
        Field nameField = personClass.getDeclaredField("name");
        nameField.set(personObject,"xunmeng");
        //3.获取相关属性值
        Object name = nameField.get(personObject);
        System.out.println(name);// ===>xunmeng

    }

(3)解决set()访问私有属性问题

  • 正常情况下不可以用set()为私有属性赋值,需要打破封装,才可以。
  • Fidle方法:
方法备注
public void setAccessible(boolean flag)默认false,设置为true为打破封装
  • 例子
    @Test
    public void ReflectTest03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {//属性操作方法
        //通过反射机制获取class,并用其进行实例化
        Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");

		//......

        //属性赋值
        // 1.先创建一个实例对象
        // 通过class获取实例
        Object personObject = personClass.newInstance();
		
		//......

        //私有属性操作
        Field ageField = personClass.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(personObject,21);
        Object age = ageField.get(personObject);
        System.out.println(age); // ===>21

    }

九、反射对方法的操作

API:

方法名备注
public String getName()返回方法名
public int getModifiers()获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getReturnType()以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class<?>[] getParameterTypes()返回方法的参数列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args)调用方法

例子:

    @Test
    public void ReflectTest04() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {//反射Method
        //通过反射机制获取class,并用其进行实例化
        Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");
        // 通过class获取实例
        Object personObject = personClass.newInstance();

        // 获取所有的Method(包括私有的!)
        Method[] declaredMethods = personClass.getDeclaredMethods();
        Arrays.stream(declaredMethods).forEach(method -> {
            System.out.println(method.getName());
        }); // ===>[getName,setName,setAge,sayHello,getAge]

        //修饰符列表
        Arrays.stream(declaredMethods).forEach(method -> {
            System.out.println(Modifier.toString(method.getModifiers()));
        }); // ===>[public,public,public,public,public,private]

        //获取参数列表
        Method setNameMethod = personClass.getMethod("setName", String.class);
        Class<?>[] parameterTypes = setNameMethod.getParameterTypes();
        Arrays.stream(parameterTypes).forEach(type->{
            System.out.println(type.getName());
        });// ===>java.lang.String

        //调用方法 getDeclaredMethod可获取私有和公有方法
        Method privateMethod = personClass.getDeclaredMethod("privateMethod", null);
        // *打破封装
        privateMethod.setAccessible(true);
        Object invoke = privateMethod.invoke(personObject, null);
        System.out.println(invoke); // ===>this is private method
    }

十、反射对构造函数的操作

API:

方法名备注
public String getName()返回构造方法名
public int getModifiers()获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?>[] getParameterTypes()返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs)创建对象【参数为创建对象的数据】

例子:

    @Test
    public void ReflectTest05() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //通过反射机制获取class,并用其进行实例化
        Class<?> personClass = Class.forName("com.xunmeng.reflect.Person");

        //获取构造函数
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        Arrays.stream(declaredConstructors).forEach(constructor -> {
            System.out.print("构造函数名:"+constructor.getName()+"===》参数:");
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            Arrays.stream(parameterTypes).forEach(parameterType->{
                System.out.print(parameterType.getName());
            });
            System.out.println();
        });// 构造函数名:com.xunmeng.reflect.Person===》参数:null; 构造函数名:com.xunmeng.reflect.Person===》参数:java.lang.Stringjava.lang.Integer

        //创建对象
        Constructor<?> declaredConstructor = personClass.getDeclaredConstructor(String.class, Integer.class);
        Object xunmeng = declaredConstructor.newInstance("xunmeng", 21);
        System.out.println(xunmeng); // ===>Person{name='xunmeng', age=21}
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值