
本文共 3712 字,大约阅读时间需要 12 分钟。
反射的思想及作用
反射是一种强大的技术,它能够让程序在运行时对类的信息进行动态获取和操作。在编写程序时,我们常常需要面对数据结构的频繁更改的情况,比如从HashMap
切换到LinkedHashMap
。每次发生这种更改都需要重新修改源码,编译并重启程序,这种效率非常低。然而,通过使用反射,我们可以在不修改源码的情况下灵活地使用不同的数据结构。
在学习反射之前,先了解正射的概念。new
关键字实例化对象的方式就是正射的典型体现。下面是一个实例化HashMap
的示例:
Mapmap = new HashMap<>();map.put(1, 1);
如果需要切换到LinkedHashMap
,代码就需要改为:
Mapmap = new LinkedHashMap<>();map.put(1, 1);
这种需求变更需要频繁修改源码,显然不够高效。通过引入开关机制,我们可以在程序运行时动态决定使用哪一种数据结构:
public MapgetMap(String param) { 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;}
传入不同的参数可以灵活选择数据结构,但如果需要支持更多类的使用,反射就派上用场了。通过反射,我们可以在运行时动态获取类信息并创建实例:
public MapgetMap(String className) { Class clazz = Class.forName(className); Constructor constructor = clazz.getConstructor(); return (Map ) constructor.newInstance();}
无论是HashMap
还是TreeMap
,只要它们实现了Map
接口,都可以通过全类名传入方法中使用,无需修改源码。
反射的基本使用
反射的主要组成部分包括Class
、Field
、Constructor
和Method
四个类。它们分别用于获取类信息、属性、构造器和方法。
获取Class对象
获取Class
对象的方法有三种:
Class clazz = SmallPineapple.class;
- 通过实例获取对应的类对象:
- 使用全限定名获取类对象:
- 使用
newInstance()
方法: - 使用构造器和
newInstance()
方法: - 获取类中所有公共变量:
- 根据变量名获取特定变量:
- 获取类中所有公共方法:
- 根据方法名和参数类型获取特定方法:
- 获取类中所有构造器:
- 根据参数类型获取特定构造器:
- 获取类上的所有注解:
- 获取特定注解:
-
Spring IOC容器:Spring通过读取配置文件解析出所有
<bean>
标签,并使用反射实例化对象,存储在IOC容器中。 -
反射+工厂模式:通过反射和工厂模式结合,通过传入不同的类名实例化新的子类,无需修改工厂类代码。
-
JDBC连接数据库:通过反射加载数据库驱动类,灵活配置数据源。
-
优势:
- 增加程序灵活性,能够在运行时动态选择类实例化。
- 适用于频繁改变需求的场景,避免修改源码。
-
缺点:
- 破坏类封装性,可以强制访问私有属性和方法。
- 性能开销较大,反射操作不受优化,可能影响程序性能。
SmallPineapple instance = new SmallPineapple();Class clazz = instance.getClass();
Class clazz = Class.forName("com.example.SmallPineapple");
构造类实例
通过反射,可以选择两种方式构造类的实例:
Class clazz = Class.forName("com.example.SmallPineapple");Object instance = clazz.newInstance();
Class clazz = Class.forName("com.example.SmallPineapple");Constructor constructor = clazz.getConstructor(String.class, Integer.class);constructor.setAccessible(true);Object instance = constructor.newInstance("小菠萝", 21);
获取类信息
通过反射,可以获取类中定义的所有信息,包括变量、方法和构造器。
获取变量(Field)
Class clazz = SmallPineapple.class;Field [] fields = clazz.getFields();
Field weightField = clazz.getDeclaredField("weight");
获取方法(Method)
Class clazz = SmallPineapple.class;Method [] methods = clazz.getMethods();
Method getInfoMethod = clazz.getMethod("getInfo");
获取构造器(Constructor)
Class clazz = SmallPineapple.class;Constructor [] constructors = clazz.getConstructors();
Constructor defaultConstructor = clazz.getConstructor();
获取注解
反射还支持获取注解信息,注解信息可以定义在变量、方法和构造器上。
Annotation [] annotations = clazz.getAnnotations();
Annotation retentionAnnotation = clazz.getAnnotation(Retention.class);
调用方法
可以通过反射调用方法,方法的invoke()
方法可以传递参数:
Class clazz = Class.forName("com.example.SmallPineapple");Method getInfoMethod = clazz.getMethod("getInfo");Object instance = (SmallPineapple) clazz.newInstance();Object result = getInfoMethod.invoke(instance);
反射的应用场景
JDBC 加载驱动类
在程序中,JVM不会自动加载外部库类,所以我们需要通过Class.forName()
动态加载驱动类:
public class DBConnectionUtil { private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver"; public static Connection getConnection() { Class.forName(DRIVER_CLASS_NAME); return DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8", "root", "root"); }}
通过修改配置文件中的驱动类名,程序可以灵活切换数据库驱动。
反射的优缺点
反射技术在开发中非常实用,特别是在需要灵活配置和面对频繁需求变更的情况下。通过掌握反射的基本使用方法,可以在项目开发中提高效率,减少代码修改的频率。
发表评论
最新留言
关于作者
