Java基础篇反射机制总结


说明

     康师傅说:"获取Class类的实例、获取运行时类对象、调用运行时类的指定结构这三个是比较重要的,需要掌握,其他的理解为主“。

1. 反射基础知识预备

1.1 反射机制概述

概念描述

     ① Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
    ②加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

Java反射机制提供的功能

①在运行时判断任意一个对象所属的
②在运行时构造任意一个类的对象
③在运行时判断任意一个类所具有的成员变量方法
④在运行时获取泛型信息
⑤在运行时调用任意一个对象的成员变量方法
⑥在运行时处理注解
⑦生成动态代理

反射相关的主要API

①java.lang.Class:代表一个
②java.lang.reflect.Method:代表类的方法
③java.lang.reflect.Field:代表类的成员变量
④java.lang.reflect.Constructor:代表类的构造器

1.2 Class类的理解

Class类的概述

    Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()
    以上的方法返回值的类型是一个Class类,此类是Java反射源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
    通过Class类对象的调用可以可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些口。

获取Class类的实例

四种实现方法(第四类不做说明,基本不用):

①调用运行时类的属性.class
②通过运行时类的对象,调用getClass()
③调用Class的静态方法:forName(String classPath)classPath为全路径

实现代码展示

	//获取运行时类
    @Test
    public void test3() throws ClassNotFoundException {
        //方式一:调用运行时类的属性.class
        Class<Person> clazz1 = Person.class;
        System.out.println(clazz1);

        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class<? extends Person> clazz2 = p1.getClass();
        System.out.println(clazz2);

        //方式三:调用Class的静态方法:forName(String classPath)classPath为全路径
        Class<?> clazz3 = Class.forName("day18.com.it.java0.Person");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);

        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class<?> clazz4 = classLoader.loadClass("day18.com.it.java0.Person");
        System.out.println(clazz4);

        System.out.println(clazz1 == clazz4);
    }

实现结果

在这里插入图片描述

1.3 类的加载与ClassLoader的理解

类的加载过程

     当程序主动使用某个类时,如果该类还未被加载内存中,则系统会通过如下三个步骤来对该类进行初始化

在这里插入图片描述
ClassLoader的理解

类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。

三种加载器
    ①引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取扩展
    ②类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
    ③系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载
利用Load实现配置文件的获取

public class LoadTest {
    /*
    测试:使用Properties来读取配置文件
     */
    @Test
    public void test1() throws Exception {
        Properties pros = new Properties();
        //此时的默认文件在当前的module下
        //读取配置文件的方式一:
        FileInputStream fis = new FileInputStream("jdbc.properties");
        pros.load(fis);

        //此时的默认文件在当前module的src下
        //读取配置文件的方式二:
//        ClassLoader clazz = LoadTest.class.getClassLoader();
//        InputStream is = clazz.getResourceAsStream("jdbc1.properties");
//        pros.load(is);
        String name = pros.getProperty("name");
        String password = pros.getProperty("password");
        System.out.println("name = " + name + ", password = " + password);
    }
}

在这里插入图片描述

2. 反射机制的具体实现

2.1 获取运行时类对象

①根据全类名获取对应的Class对象
②调用指定参数结构的构造器,生成Constructor的实例
③通过Constructor的实例创建对应类的对象,并初始化类属性
通过newInstance()获取运行时类的对象

@Test
    public void test1() throws InstantiationException, IllegalAccessException {

        Class<Person> clazz = Person.class;
        /*
        new Instance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器

        要想此方法正常的创建运行时类的对象,要求:
        1.运行时类必须提供空参构造器
        2.空参构造器的访问权限得够。通常,设置为public.

        在javabean中提供一个public的空参构造器。原因:
        1.便于通过反射,创建运行时类的对象
        2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
         */
        Person person = clazz.newInstance();
        System.out.println(person);
    }

在这里插入图片描述

动态获取运行时类的对象

	@Test
    public void test2(){
        int num = new Random().nextInt(3);
        String classPath = "";
        switch (num){
            case 0:
                classPath = "java.util.Date";
                break;
            case 1:
                classPath = "java.sql.Date";
                break;
            case 2:
                classPath = "day18.com.it.java0.Person";
                break;
        }
        try {
            Object obj = getInstance(classPath);
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public Object getInstance(String classPath) throws Exception {
        Class clazz = Class.forName(classPath);
        return clazz.newInstance();
    }

在这里插入图片描述

2.2 获取运行时类的完整结构

     通过反射获取运行时类的完整结构:Field(属性)、Method(方法)、Constructor(构造器)、Superclass(父类)、Interface(接口)、Annotation(注解)

理解即可,不用掌握

对于这些直接看代码理解更好一点
获取属性

@Test
    public void test1(){

        Class<Person> clazz = Person.class;
        //获取属性结构
        //getFields():获取当前运行类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for (Field f : fields){
            System.out.println(f);
        }

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

        //getDeclaredFields()获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields){
            System.out.println(f);
        }
    }

在这里插入图片描述

通过反射访问属性的权限修饰符 数据类型 变量名

 //通过反射访问权限修饰符  数据类型  变量名
    @Test
    public void test2(){

        Class<Person> clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields){

            //权限修饰符
            int modifier = f.getModifiers();
            System.out.print(Modifier.toString(modifier) + "\t");

            //数据类型
            Class<?> type = f.getType();
            System.out.print(type + "\t");

            //变量名
            String name = f.getName();
            System.out.println(name);
        }
    }

在这里插入图片描述

获取方法

 @Test
    public void test1() {
        //getMethods()获取当前运行时类及其父类中声明为public权限的方法
        Class<Person> clazz = Person.class;
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            System.out.println(m);
        }

        //getDeclaredMethods()获取当前运行时类中声明的所有方法
        //获取当前运行时类中所声明的所有方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m);
        }
    }

获取方法的权限修饰符 返回值类型 方法名(形参列表)throws 异常,以及注解

@Test
    public void test2() {
        Class<Person> clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
            //获取注解
            Annotation[] annotations = m.getAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);
            }

            //获取权限修饰符
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");

            //返回值类型
            Class<?> returnType = m.getReturnType();
            System.out.print(returnType + "\t");

            //方法名
            System.out.print(m.getName());

            //形参列表
            System.out.print("(");
            Class<?>[] parameterTypes = m.getParameterTypes();//获取当前方法的形参列表结合
            if (!(parameterTypes == null && parameterTypes.length == 0)) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1) {
                        System.out.print(parameterTypes[i].getName() + "args_" + i);
                    }
                    System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
                }
                System.out.print(")");
            }

            //获取异常信息

            Class<?>[] exceptionTypes = m.getExceptionTypes();
            if (exceptionTypes.length > 0) {
                System.out.print("throws");
                for (int i = 0; i < exceptionTypes.length; i++) {
                    if (i == exceptionTypes.length - 1) {
                        System.out.print(exceptionTypes[i].getName());
                    }
                    System.out.print(exceptionTypes[i].getName() + ",");
                }
            }

            System.out.println();
        }
    }

在这里插入图片描述

获取构造器结构

//获取构造器结构
    @Test
    public void test1() throws NoSuchMethodException {
        //getConstructors():获取当前运行时类中声明为public的构造器
        Class<Person> clazz = Person.class;
        Constructor<?>[] constructor = clazz.getConstructors();
        for (Constructor c : constructor){
            System.out.println(c);
        }

        System.out.println();
        //getDeclaredConstructors():获取当前运行时类中声明的所有构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor c : declaredConstructors){
            System.out.println(c);
        }
    }

在这里插入图片描述
对于其他的使用相同的方法即可获取,这里就不展示了

2.3 调用运行时类的指定结构

获取指定的属性、方法、构造器
这里比较重要需要掌握
调用运行时类中指定的属性

@Test
    public void testField1() throws Exception{
        //获取Class类的实例
        Class<Person> clazz = Person.class;
        //2.获取一个运行时类的对象
        Person p1 = clazz.newInstance();
        //3.getDeclaredField():获取指定的属性
        Field name = clazz.getDeclaredField("name");
        //4.将属性设置为可用
        name.setAccessible(true);
        //5.设置属性数值
        name.set(p1,"Jack");
        System.out.println(name.get(p1));
    }

获取指定的方法

@Test
    public void testMethod() throws Exception {
        //创建Class类的实例
        Class<Person> clazz = Person.class;
        //创建运行时类的对象
        Person p1 = clazz.newInstance();
        //获取指定的某个方法
        //getDeclaredMethod():参数1:知名获取的方法名称 参数2:指明获取方法的参数列表
        Method show = clazz.getDeclaredMethod("show", String.class);
        //保证此方法是可访问的
        show.setAccessible(true);
        //调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
        //invoke()的返回值即为对应类中调用方法的返回值
        Object obj = show.invoke(p1, "CHN");
        System.out.println(obj);

        System.out.println("*************如何调用静态方法***************");
        //没有返回值是调用方法返回的是null
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        Object o = showDesc.invoke(p1);
        System.out.println(o);
    }

获取指定的构造器

	@Test
    public void testConstructor() throws Exception{
        //创建Class类的实例
        Class<Person> clazz = Person.class;
        //获取指定的构造器
        Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
        //保证获取的构造器是可用的
        constructor.setAccessible(true);
        //创建一个运行时类的对象
        Person person = constructor.newInstance("Tom");
        System.out.println(person);
    }

2.4 动态代理

代理设计模式的原理

     ①使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
     ②静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
     ③动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
     ④动态代理使用场合:
               (1)调试
               (2)远程方法调用

动态代理步骤

①创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。
②创建被代理的以及接口
③通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
④通过 Subject代理调用RealSubject实现类的方法

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮皮皮皮皮皮皮卡乒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值