详解 反射机制

反射机制,可能有的同学在学习本篇博文的内容之前,就久仰过大名。
因为,只要学习框架的知识,就必然会看到反射机制的应用。
那么,为什么反射机制这么受欢迎呢?
因为它功能十分强大。
至于为什么本人在次对反射机制赞不绝口,请看博文内容:



首先,本人先要来讲解下有关反射机制的一个很重要的知识点 —— 类的加载

类的加载

请观看本人博文 —— 《详解 类的加载》

反射机制

概述

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


获取 class文件对象:

获取 class文件对象三种方式

  • Object类getClass()方法
  • 任何一个对象静态属性class
  • Class类静态方法forName()
    public static Class forName(String className):
    className: 这个表示的是一个类对应的全类名(就是需要加上包名)

那么,本人来展示下这三种方式:
首先,本人给出一个含有两个成员、三种构造方法、三个方法的信息存储类:

package edu.youzg.about_reflact.core;

public class FanInfo {
    public String name;
    int age;

    public FanInfo() {
        System.out.println("执行类空参构造方法");
    }

    public FanInfo(String name) {
        this.name = name;
        System.out.println("执行类单参构造方法 === " + name);
    }

    private FanInfo(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("执行类双参私有参构造方法 === " + name + " === " + age);
    }

    public void test(){
        System.out.println("这是一个空参的test方法");
    }

    public void test1 (String name){
        System.out.println("这是一个空参的test方法" + name);
    }

    public void test2(String name,int age){
        System.out.println("两个参数的方法"+name+"==="+age);
    }


    private int test(String name, int age, double num) {
        System.out.println("私有的方法" + name + "===" + age + "===" + num);
        return 666;
    }
}

那么,现在,本人来展示下如何获取 class文件对象:

package edu.youzg.about_reflact.core;

public class Test {

    public static void main(String[] args) throws ClassNotFoundException {
        //如何获取一个类的字节码文件对象。
        //方式1 getClasss()
        Object obj = new Object();
        Class aClass = obj.getClass();
        Class aClass1 = obj.getClass();

        Object obj2 = new Object();
        Class aClass2 = obj2.getClass();

        System.out.println(aClass==aClass1);

        System.out.println(aClass1==aClass2);
        System.out.println("=================================");

        //方式2,每一个类,都有一个静态的 class 属性可以获取该类的字节码文件对象
        Class objectClass = Object.class;
        System.out.println(aClass2==objectClass);
        Class stringClass = String.class;

        System.out.println("=================================");

        //方式3:使用Class中的方法 传入一个类的全限定名来获取
        //全限定名:包名+类名 能够确保一个类的唯一性。org.westos.demo.MyTest  org.westos.demo2.MyTest
        //Class 类 描述字节码文件类型的。
        //static Class<?> forName (String className)
        //返回与带有给定字符串名的类或接口相关联的 Class 对象。

        Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
        Class fanClass2 = Class.forName("edu.youzg.about_reflact.core.FanInfo");
        System.out.println(fanClass == fanClass2);
    }

}

现在,本人来展示下运行结果
在这里插入图片描述


获取 构造方法:

获取所有构造方法

  • public Constructor<?>[] getConstructors():
    获取所有的构造方法,不包含私有的
  • public Constructor<?>[] getDeclaredConstructors():
    获取所有的构造方法 ,包括私有的

获取单个构造方法

  • public Constructor< T > getConstructor(Class<?>... parameterTypes):
    获取单个的构造方法, 不包含私有的
  • public Constructor< T > getDeclaredConstructor(Class<?>... parameterTypes):
    获取单个的构造方法,包含私有的

那么,本人来展示下如何 获取构造方法:

package edu.youzg.about_reflact.core;

import java.lang.reflect.Constructor;

public class Test {

    public static void main(String[] args) throws Exception {
        //1.获取该类的字节码文件对象
        Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
        //2.获取该类的空参构造方法对象
        Constructor constructor = fanClass.getConstructor();

        //使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
        Object obj = constructor.newInstance();
        System.out.println(obj);

        System.out.println("========================================");
        //我们之前也可以借助有参构造来创建对象。
        //我们采用反射的方式,来借助有参构造,创建该类对象。
        //获取一个参数的构造方法对象。
        Constructor constructor1 = fanClass.getConstructor(String.class);
        //使用构造方法对象中的方法来创建该类对象。
        FanInfo obj2 = (FanInfo) constructor1.newInstance("米斯达");
        System.out.println(obj2);

        System.out.println("========================================");
        //我们以前不能使用私有构造直接创建对象
        //下来我们采用反射的方式的方式,借助私有构造方法来创建出该类对象
        //获取出私有构造方法对象
        Constructor declaredConstructor = fanClass.getDeclaredConstructor(String.class, int.class);
        //通过私有的构造方法对象,调用他的方法来创建该类对象。
        //取消权限的语法检测
        declaredConstructor.setAccessible(true);
        Object obj7 = declaredConstructor.newInstance("波鲁纳雷夫", 30);
        System.out.println(obj7);
    }

}

现在,本人来展示下运行结果
在这里插入图片描述


获取 类的对象:

获取 类的对象

  • public Object newInstance():
    根据调用这个方法的Constructor类对象,获得一个相应的对象

那么,本人来展示下如何 获取类的对象:

package edu.youzg.about_reflact.core;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test {

    public static void main(String[] args) throws Exception {
        //我们采用反射的方式,来创建一个类的对象。
        //1.获取该类的字节码文件对象
        Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
        //2.获取空参的构造方法对象
        Constructor declaredConstructor = fanClass.getDeclaredConstructor();
        //3.调用 Constructor 中的方法来创建FanInfo的对象
        FanInfo fan = (FanInfo) declaredConstructor.newInstance();
        System.out.println(fan);

        System.out.println("===================================");
        //我们现在来通过双参构造方法来创建FanInfo的对象
        Constructor constructor = fanClass.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);
        //设置权限
        fan = (FanInfo) constructor.newInstance("米斯达", 30);
        System.out.println(fan);

        System.out.println("===================================");
        //如果我们借助 空参 来创建对象,还有一个简便方法。

        Class fanClass2 = Class.forName("edu.youzg.about_reflact.core.FanInfo");
        //不用去获取空参构造对象,在 Class 里面有一个方法,就能创建对象。
        FanInfo fan2 = (FanInfo) fanClass2.newInstance();
        System.out.println(fan2);
    }

}

那么,本人现在来展示下运行结果
在这里插入图片描述


获取成员变量:

获取所有成员变量

  • public Field[] getFields():
    获取所有的成员变量包含从父类继承过来的
  • public Field[] getDeclaredFields():
    获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量

获取单个成员变量

  • public Field getField(String name)
  • public Field getDeclaredField(String name)

那么,本人来展示下如何 获取成员变量:

package edu.youzg.about_reflact.core;

import java.lang.reflect.Field;

public class Test {

    public static void main(String[] args) throws Exception {
        //对于成员变量 用 Field 这个类型来描述
        //1.获取该类的字节码文件对象
        Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
        //2.获取该类的成员变量对象
        //2.1 获取该类中所有的字段对象数组
        //getFields() 获取所有的公共字段
        Field[] fields = fanClass.getFields();
        for (Field field : fields) {
            System.out.println(field.toString());
        }

        System.out.println("==================================");
        //获取所有字段对象,包括私有
        Field[] declaredFields = fanClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        System.out.println("===========================================================");
        //获取公共的单个字段对象
        Field name = fanClass.getField("name");//传入字段名称
        System.out.println(name);
        //获取非公共的单个字段对象。
        Field age = fanClass.getDeclaredField("age");
        System.out.println(age);
    }

}

那么,本人现在来展示下运行结果:
在这里插入图片描述


获取成员方法:

获取所有成员方法

  • public Method[] getMethods():
    获取所有的公共的成员方法不包含私有的
    包含从父类继承过来的过来的公共方法
  • public Method[] getDeclaredMethods():
    获取自己的所有成员方法 包含私有的

获取单个成员方法

//参数1: 方法名称 参数2:方法行参的class 对象

  • public Method getMethod(String name,Class<?>... parameterTypes):
    获取单个的方法 不包含私有的
  • public Method getDeclaredMethod(String name,Class<?>... parameterTypes):
    获取单个方法包括私有的

那么,本人现在来展示下 获取成员方法:

package edu.youzg.about_reflact.core;

import java.lang.reflect.Method;

public class Test {

    public static void main(String[] args) throws Exception {
        //我们采用反射的方式来调用方法执行。

        //1.获取该类的字节码文件对象
        Class<?> fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
        //2.获取空参的方法对象
        Method testMethod = fanClass.getMethod("test");
        //3.调用Method类中的 invoke() 方法,让test 调用执行
        Object obj = fanClass.newInstance();
        testMethod.invoke(obj); //参数1,类的对象,参数2,给方法的形参传递的实际的值

        System.out.println("================================");
        //调用两个参数的方法执行
        Method test1 = fanClass.getMethod("test1", String.class);
        //参数1,类的对象,参数2,给方法的形参传递的实际的值
        test1.invoke(obj, "米斯达");

        System.out.println("==================================");
        Method test2 = fanClass.getMethod("test2", String.class, int.class);
        test2.invoke(obj, "米斯达", 30);

        System.out.println("==================================");
        //以前的方式,你在外界new对象调用 私有方法是调用不到的。
        //我们通过反射来调用私有方法执行。
        Method test3 = fanClass.getDeclaredMethod("test3", String.class, int.class, double.class);
        //调用私有方法,取消私有的权限校验
        test3.setAccessible(true);
        Integer result = (Integer) test3.invoke(obj, "波鲁纳雷夫", 30, 66.6);
        System.out.println(result);
    }

}

那么,本人现在来展示下运行结果
在这里插入图片描述


现在,本人要讲解一个非常常用的反射机制的应用场景 —— 通过反射运行配置文件内容

反射运行配置文件内容:

在本人《详解 Properties类》博文中,曾讲过:Properties类可以将配置文件中的键值对读取出来。

那么,在这里,本人将通过反射机制,将从配置文件中读取出来的值,赋给类中相应成员:

本人直接来上代码:
首先,本人来给出一个多功能手机类:

package edu.youzg.about_reflact.core;

public class Phone {

    public Phone() {
    }
    
    public void game() {
        System.out.println("正在运行游戏");
    }
    
    public void music() {
        System.out.println("正在播放音乐");
    }

    public void call() {
        System.out.println("正在进行通话");
    }
    
}

现在,本人来展示下配置文件的信息:

className=edu.youzg.about_reflact.core.Phone
methodName=music

那么,现在,本人来展示下通过反射机制扫描配置文件,来调用目标方法:

package edu.youzg.about_reflact.core;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {

    public static void main(String[] args) throws Exception {
        //加载配置文件
        //基于目标类开发
        Properties properties = new Properties();
        properties.load(new FileReader("./config.properties"));
        //1.获取该类的字节码文件对象
        Class clazz = Class.forName(properties.getProperty("className"));
        //2.通过反射来创建目标类对象
        Object obj = clazz.getDeclaredConstructor().newInstance();
        //3.调用目标类中的方法执行
        Method methodName = clazz.getMethod(properties.getProperty("methodName"));
        methodName.invoke(obj);
    }

}

那么,本人来展示下运行结果
在这里插入图片描述


反射忽视泛型检查:

在本人博文《详解 泛型 与 自动拆装箱》中讲到过:

泛型仅在编译期有效,而反射机制是针对运行期的。
所以,我们若是通过反射机制去调用泛型的话,就会读取不到相应的类型,只会读取到Object类型,这就是我们所谓的 泛型擦除机制

那么,现在,本人就来展示下通过反射机制去调用一个集合时,可以存入不恰当的数据:

package edu.youzg.about_reflact.core;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Test {

    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(666);
        list.add(374);
        list.add(251);
        // list.add("abc");
        //泛型,只在编译期有效,运行期就擦除了。
        //ArrayList.class
        Class aClass = list.getClass();
        //获取add()方法对象
        Method add = aClass.getMethod("add", Object.class);
        add.invoke(list,"abc");

        System.out.println(list);
    }

}

那么,本人来展示下运行结果
在这里插入图片描述
可以看到,Integer类型的ArrayList,存入了字符串数据!


接下来,本人来通过反射机制给出一个工具类,以便我们能够直接对一个对象的某成员赋值:

反射机制设置某个对象的某个属性为指定的值:

首先,是这个工具类

package edu.youzg.about_reflact.core;

import java.lang.reflect.Field;

public class AssignUtil {

    /* 此方法可将obj对象中名为propertyName的属性的值设置为value*/
    public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, IllegalAccessException {
        Class aClass = obj.getClass();
        Field declaredField = aClass.getDeclaredField(propertyName);
        declaredField.setAccessible(true); //取消private权限的检测
        declaredField.set(obj,value);
    }
}

现在,本人来给出一个测试类:

package edu.youzg.about_reflact.core;

public class Test {

    public static void main(String[] args) throws Exception {
        //通过反射写一个通用的设置某个对象的某个属性为指定的值
        FanInfo FanInfo = new FanInfo();
        AssignUtil.setProperty(FanInfo,"name","波鲁纳雷夫");
        AssignUtil.setProperty(FanInfo,"age",30);
        System.out.println(FanInfo.name);
        System.out.println(FanInfo.age);
    }

}

那么,现在,本人来展示下运行结果
在这里插入图片描述


那么,现在,本人再来讲解一个有关反射机制的很重要的知识点 —— 动态代理

动态代理

请观看本人博文—— 《详解 动态代理》


posted @ 2020-03-05 11:11  在下右转,有何贵干  阅读(239)  评论(0编辑  收藏  举报