栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java-反射

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java-反射

注:看了几篇反射的内容,记录一下,方便后续学习。
原作者连接:https://juejin.cn/post/6864324335654404104

场景(纯属虚构):
1.项目中定义了一个HashMap进行数据存储。
2.后面需求变动,使用linkedHashMap更合适,就用linkedHashMap替换HashMap。重新编译上线。
3.万恶的需求又变了,感觉还是HashMap来存储更好,再次修改,编译,部署。
对于这种需求频繁变更但变更不大的场景,频繁的更改源码肯定是一种不允许的 操作。

我们先定义一个“开关”,判断什么时候用哪一种数据结构。根据动态的入参决定使用哪一个数据结构。

    public Map getMap(String param) {
    Map map = null;
    if (param.equals("HashMap")) {
        map = new HashMap<>();
    } else if (param.equals("linkedHashMap")) {
        map = new linkedHashMap<>();
    } else if (param.equals("WeakHashMap")) {
        map = new WeakHashMap<>();
    }
    return map;
}

但是如果某一天还想用TreeMap,还要修改源码。这个时候,反射就排上了用场。

总结:
在代码运行之前,我们不确定将来要使用哪一种数据结构,只有程序在运行时才决定使用哪一种数据。反射,就可以在程序运行过程中动态的获取类信息和调用类方法。

使用反射后,就不需要通过new来增加新的数据结构了,只需要传入className就可以动态获取对应的对象。使用反射的好处:
1.不需要在编译器就把对象的类型确定下来。
2.如果发生需求变更,可以通过变更开关来实例化不同的数据结构。
3.如果扩展类有非常多,就会创建出非常多的分支。使用反射,就可以在运行时才确定使用哪一个数据类,在切换类时,无需要重新修改源码,编译程序。

 public Map getMap(String className) {
    Class clazz = Class.forName(className);
    Consructor con = clazz.getConstructor();
    return (Map) con.newInstance();
}
反射的基本使用

java反射有主要组成部分如下:

  1. Class:任何运行在内存中的所有类都是该Class类的实例对象,每个Class类对象内部都包含类的所有信息。通过反射干任何事,先找到Class准没错。
  2. Field:描述一个类的属性,内部包含了改属性的所有信息,例如数据类型,属性名,访问修饰符…。
  3. Constructor:描述一个类的构造方法,内部包含了构造方法的所有信息。例如参数类型,参数名字,访问修饰符…。
  4. Method:描述一个类的所有方法,包含了该方法的所有信息,云Constructour类似,不同之处是Method拥有返回值类型信息,因为构造方法是没有返回值的。

以下是原作者的图:

定义一个类,进行反射的基本操作

1. 获取类的Class对象

2. 构造类的实例化对象

3. 获取类中的变量Field
Field[] getFields():获取类中所有被public修饰的所有变量

Field getField(String name):根据变量名获取类中的一个变量,该变量必须被public修饰

Field[] getDeclaredFields():获取类中所有的变量,但无法获取继承下来的变量

Field getDeclaredField(String name):根据姓名获取类中的某个变量,无法获取继承下来的变量

4. 获取类中的方法
Method[] getMethods():获取类中被public修饰的所有方法

Method getMethod(String name, Class… paramTypes):根据名字和参数类型获取对应方法,该方法必须被public修饰

Method[] getDeclaredMethods():获取所有方法,但无法获取继承下来的方法

Method getDeclaredMethod(String name, Class… paramTypes):根据名字和参数类型获取对应方法,无法获取继承下来的方法

通过反射获取到某个 Method 类对象后,可以通过调用invoke方法执行。
invoke(Oject obj, Object… args):参数``1指定调用该方法的对象,参数2`是方法的参数列表值。
如果调用的方法是静态方法,参数1只需要传入null,因为静态方法不与某个对象有关,只与某个类有关。

5. 反射的应用场景
1.spring的ioc容器
定义bean标签

 

    
        
        
    

定义ClassPahXmlApplicationContext刷新应用上下文。

public class Main {
    public static void main(String[] args) {
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        ReflectDemo reflectDemo= (ReflectDemo ) ac.getBean("reflectDemo");
        smallPineapple.getInfo(); // [小熊的年龄是:18]
    }
}

ioc容器本质上就是一个工厂,通过该工厂传入bean标签中定义的id获取对应的实例。
bean的创建过程请自行查询资料。

2.反射 + 抽象工厂模式
传统的工厂模式,如果需要生产新的子类,需要修改工厂类,在工厂类中增加新的分支;

public class MapFactory {
    public Map produceMap(String name) {
        if ("HashMap".equals(name)) {
            return new HashMap<>();
        } else if ("TreeMap".equals(name)) {
            return new TreeMap<>();
        } // ···
    }
}

通过反射,工厂类就不用修改任何东西。当子类确定下来时,工厂就可以生成该字类了。
反射+抽象工厂的核心思想时:
在运行时通过参数传入不同子类的全限定名获取到不同的 Class 对象,调用 newInstance() 方法返回不同的子类。细心的读者会发现提到了子类这个概念,所以反射 + 抽象工厂模式,一般会用于有继承或者接口实现关系。

public class MapFactory {
    
    public Map produceMap(String className) {
        Class clazz = Class.forName(className);
        Map map = clazz.newInstance();
        return map;
    }
}

className 可以指定为 java.util.HashMap,或者 java.util.TreeMap 等等,根据业务场景来定。

3.jdbc加载数据库驱动类
在导入第三方库时,JVM不会主动去加载外部导入的类,而是等到真正使用时,才去加载需要的类,正是如此,我们可以在获取数据库连接时传入驱动类的全限定名,交给 JVM 加载该类。

public class DBConnectionUtil {
    
    private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
    
    public static Connection getConnection() {
        Connection conn = null;
        // 加载驱动类
        Class.forName(DRIVER_CLASS_NAME);
        // 获取数据库连接对象
        conn = DriverManager.getConnection("jdbc:mysql://···", "root", "root");
        return conn;
    }
}

在我们开发 SpringBoot 项目时,会经常遇到这个类,但是可能习惯成自然了,就没多大在乎,我在这里给你们看看常见的application.yml中的数据库配置,我想你应该会恍然大悟吧。

这里的 driver-class-name,和我们一开始加载的类是不是觉得很相似,这是因为MySQL版本不同引起的驱动类不同,这体现使用反射的好处:不需要修改源码,仅加载配置文件就可以完成驱动类的替换。

反射的优势与缺陷

优点:
增加程序的灵活性
缺点:
1.破坏类的封装性,可以强制访问private修饰的信息。
2.性能损耗,反射相比直接实例化对象,带哦用方法,访问变量,中间需要非常多的检查步骤与分析步骤,jvm无法对他们优化。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/328339.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号