【反射1】——Java基础(韩顺平讲解)

发布时间:2023年12月25日

一个需求,引出反射 | 完整的反射使用流程:

在不修改源码的情况下,来控制程序,也符合设计模式中的opc原则(开闭原则:不修改源码,扩容功能)

1、创建配置文件:re.properties

classfullpath=com.reflection.Cat
method=hi

2、创建Cat类

package com.reflection;

public class Cat {

    public String name = "招财猫";

    public void hi(){
        System.out.println("hi!"+ name);
    }

    public void cry(){
        System.out.println(name + "喵喵叫!");
    }
}

3、创建调用类:quection,并回顾传统调用的方法,以及反射机制解决的完整流程

package com.reflection;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class quection {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //问题:根据配置文件 re.properties 中的指定信息,创建Cat类 并 调用hi方法;

        //传统方法
        Cat cat = new Cat();
        cat.hi(); // 如果想调用cry方法,只能修改代码;
        System.out.println("===========================================");

        //反射
        //1. 使用Properties类 获取配置文件中的内容
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\main\\resources\\re.properties"));
        String classfullpath = properties.getProperty("classfullpath").toString();
        String method = properties.getProperty("method").toString();
        System.out.println("classfullpath:" + classfullpath);
        System.out.println("method:" + method);

        //2. 使用反射机制解决
        Class aClass = Class.forName(classfullpath); //加载类,返回Class类型的对象aClass
        Object o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例
        System.out.println("o的运行类型:" + o.getClass());
        //通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象
        //即:在反射中,可把方法视为对象(万物接对象)
        Method method1 = aClass.getMethod(method);

        System.out.println("===========================================");
        //通过 方法对象 来实现 调用方法; 也就是 通过 method1对象 来调用 Cat类中的方法;
        method1.invoke(o);//传统方法:对象.方法();  反射:方法.invoke(对象);
    }
}

在 传统方法调用 和 反射机制 中,若改为调用Cat类中的cry方法:
传统方法需要修改代码,而反射机制只需要修改配置文件即可,将re.properties配置文件中的,method=hi 改为 method=cry

反射机制:

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

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

Java反射机制原理示意图:
在这里插入图片描述

Java反射机制可以完成:

1、在运行时判断任意一个对象所属的类
2、在运行时构造任意一个类的对象
3、在运行时得到任意一个类所具有的成员变量和方法
4、在运行时调用任意一个对象的成员变量和方法
5、生成动态代理

反射相关的主要类:这些类在java.lang.reflection包中

1、java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
2、java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
3、java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
4、java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器

反射相关的主要类的代码应用:

package com.reflection;

public class Cat {

    private String name = "招财猫";
    public int age = 0;

    public Cat() {}

    public Cat(String name) {
        this.name = name;
    }

    public void hi(){
        System.out.println("hi!"+ name);
    }

    public void cry(){
        System.out.println(name + "喵喵叫!");
    }
}
package com.reflection;

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

public class reflection01 {

    public static void main(String[] args) throws Exception {

        //反射
        //1. 使用Properties类 获取配置文件中的内容
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\main\\resources\\re.properties"));
        String classfullpath = properties.getProperty("classfullpath").toString();
        String method = properties.getProperty("method").toString();

        //2. 使用反射机制解决
        Class aClass = Class.forName(classfullpath); //加载类,返回Class类型的对象aClass
        Object o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例
        System.out.println("o的运行类型:" + o.getClass());
        //通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象
        //即:在反射中,可把方法视为对象(万物接对象)
        Method method1 = aClass.getMethod(method);

        System.out.println("===========================================");
        //通过 方法对象 来实现 调用方法; 也就是 通过 method1对象 来调用 Cat类中的方法;
        method1.invoke(o);//传统方法:对象.方法();  反射:方法.invoke(对象);

        //java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
        //得到name字段;
        //getField不能得到私有的属性
        Field name = aClass.getField("age");
        System.out.println(name.get(o));//传统写法:对象.成员变量   反射:成员变量对象.get(对象)

        //java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
        Constructor constructor = aClass.getConstructor();//()中可以指定构造器参数类型,返回无参构造器
        System.out.println(constructor);//Cat()

        Constructor constructor1 = aClass.getConstructor(String.class);//这里传入的String.class 就是String类的Class对象
        System.out.println(constructor1);//Cat(String name)
    }
}

反射的优点和缺点:

优点: 可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
缺点: 使用反射基本是解释执行,对执行速度有影响。

package com.reflection;

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

public class reflection02 {

    public static void main(String[] args) throws Exception {
        m1();
        m2();
        m3();
    }

    //传统方法调用hi
    public static void m1() {
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("m1() 耗时:" + (end - start));
    }

    //反射机制调用方法hi
    public static void m2() throws Exception {

        Class aClass = Class.forName("com.reflection.Cat"); //加载类,返回Class类型的对象aClass
        Object o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例
        Method method1 = aClass.getMethod("hi");//通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象

        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            method1.invoke(o);//反射调用方法
        }
        long end = System.currentTimeMillis();
        System.out.println("m2() 耗时:" + (end - start));
    }

    //反射机制优化调用方法hi
    public static void m3() throws Exception {

        Class aClass = Class.forName("com.reflection.Cat"); //加载类,返回Class类型的对象aClass
        Object o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例
        Method method1 = aClass.getMethod("hi");//通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象
        method1.setAccessible(true);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            method1.invoke(o);//反射调用方法
        }
        long end = System.currentTimeMillis();
        System.out.println("m3() 耗时:" + (end - start));
    }
}

=========执行结果============
m1() 耗时:4
m2() 耗时:1187
m3() 耗时:775

反射的简单优化——关闭访问检查:

1、Method 和 Field、Constructor对象都有setAccessible()方法
2、setAccessible作用是启动和禁用访问安全检查的开关
3、参数值为true表示,反射的对象在使用时取消访问检查,提高反射的效率。参数值为false,则表示反射的对象执行访问检查。
在这里插入图片描述

文章来源:https://blog.csdn.net/m0_46459413/article/details/135192296
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。