引入
一般情况下,我们都是通过 new 关键字来实例化对象,这是一种正射
实例化一个 HashMap 集合:
1
2Map<Integer, Integer> map = new HashMap<>();
当需要修改集合类型为 LinkedHashMap 时,需要修改代码:
1
2Map<Integer, Integer> map = new LinkedHashMap<>();
每一次我们改变需求的时候,都需要修改代码,然后对代码进行编译、打包、再到 JVM 上重启项目
缺点:效率低下
优化一:动态传参,根据不同情况选择不同的数据结构
1
2
3
4
5
6
7
8
9
10
11
12public Map<Integer, Integer> getMap(String param) { Map<Integer, Integer> 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; }
弊端:需要尽量考虑多的情况,当漏了某种情况时,还是需要修改代码
优化二:反射,在程序运行过程中动态获取类信息,调用类的方法,构造类实例
1
2
3
4
5
6public Map<Integer, Integer> getMap(String className) { Class c = Class.forName(className); Consructor con = c.getConstructor(); return (Map<Integer, Integer>) con.newInstance(); }
当需要使用的时候,传入对应结构的 全类名路径 到方法中,返回一个对应实例
总结
- new:在编译器确定对象类型
- 反射:在运行期确定具体的数据类型
反射机制
- 简述反射机制
反射机制是指在运行过程中,对于任意一个类,能获取并调用该类的任意属性和方法,获取类的构造器来构建对象。这种 动态获取程序信息 以及 动态调用对象的功能 称为反射机制(Reflection),反射是 Java 被视为 动态语言 的关键
- 反射机制的作用
在程序运行时,构造任意一个类的对象,判定任意一个对象所属的类,判断任意一个类所具有的成员变量和方法,调用任意一个对象的方法,生成动态代理
- 反射的优缺点
优点:动态加载类,动态创建对象和编译,当需求变更时,可以灵活地实例化不同对象
缺点:
- 性能瓶颈。反射是一种解释操作,我们可以告诉 JVM,我们希望做什么并且它满足我们的要求,这类操作总是慢于直接执行相同的操作
- 破坏类的封装性:通过反射我们可以强制访问 private 修饰的信息
主要用法
- 获取类的 Class
- 获取类的实例化对象
- 获取类的所有信息,包括:变量、方法、构造器、注解、泛型信息
- 动态代理
- 获取类的私有信息(存在访问权限的成员,如 protected、private)并修改其作用域
Class
概述
每一个 Java 类都有一个 Class 模板(字节码文件对象),当一个 java 类经 javac 编译后,产生一个 .class 字节码文件,其中包含了类的所有信息,如 属性、构造方法、方法 等
当 .class 被装载进 JVM 执行时,在内存中生成一个 Class 对象,它包含了该类内部的所有信息。通过该对象,我们可以在程序运行时获取类的信息
特点:
-
Class 是一个类,Class 对象只能由系统建立
-
每个类的 Class 模板是唯一的
-
每个类的实例都会记得自己是由哪个 Class 模板所生成
Class 是反射的根源,只有先获取 Class 对象,才能对类的信息进行动态获取、加载、运行
获取 Class
- 类名.class:安全、最高性能
- 实例.getClass()
- Class.forName(className):灵活
1
2
3
4Class c = Student.class; Class c = new Student().getClass(); Class c = Class.forName("com.wes.pojo.Student");
基本类型的 Class
基本类型的 Class 通过其包装类来获取
1
2Class c = Integer.TYPE;
获取父类 Class
1
2Class parent = c.getSuperclass();
其他类型的 Class
1
2
3
4
5
6
7
8
9
10
11
12public static void main(String[] args) { Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = Override.class; Class c6 = ElementType.class; Class c7 = Integer.class; Class c8 = void.class; Class c9 = Class.class; }
常用方法
方法 | 说明 |
---|---|
getName() | Class 对象所代表的实体的名称 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
获取类名
1
2
3
4
5
6public static void main(String[] args) throws Exception { Class c = Class.forName("com.yue.pojo.Student"); String typeName = c.getName();//获取类的全限定名 String name = c.getSimpleName(); // 获取类名 }
创建对象
获取 Class 后,通过反射创建对象:
- 使用 Class 对象的 newInstance 方法(调用类的无参构造器)
- 获取构造器,调用构造器的 newInstance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public static void main(String[] args) { Class<Student> c = Student.class; Student student = null; try { student = c.newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } // 获取无参构造器 Constructor<Student> c1 = c.getConstructor(); // 调用无参构造 student = c1.newInstance(); // 获取有参构造器 Constructor<Student> c2 = c.getConstructor(String.class, int.class); // 调用有参构造,传入对应参数类型进行赋值 student = c2.newInstance("zhangsan", 20); }
获取构造器
方法 | 说明 |
---|---|
Constuctor[] getConstructors() | 获取 public 构造器 |
Constructor getConstructor(Class…<?> paramTypes) | 根据参数获取 public 构造器 |
Constructor[] getDeclaredConstructors() | 获取所有构造器 |
Constructor getDeclaredConstructor(class…<?> paramTypes) | 根据参数获取所有构造器 |
1
2
3
4
5
6
7
8
9
10
11
12public static void main(String[] args) throws Exception { Class<Student> c = Student.class; // public Constructor<?>[] publicC = c.getConstructors(); // 无参构造器 Constructor<Student> c1 = c.getConstructor(); // 所有构造器 Constructor<?>[] allC = c.getDeclaredConstructors(); // 指定构造器 Constructor<Student> c2 = c.getDeclaredConstructor(String.class, int.class); }
通过 getDeclaredConstructor 可以获取到私有的构造器,但却不能直接进行创建对象
- 使用 setAccessible 取消访问检查
1
2
3
4
5
6
7public static void main(String[] args) throws Exception { Class c = Class.forName("com.yue.pojo.Student"); Constructor con = c.getDeclaredConstructor(String.class); // 获取一个私有的构造器 c.setAccessible(true); // 取消访问检查 Object o = c.newInstance("蓝天"); // 创建对象 }
获取变量
方法 | 说明 |
---|---|
Field[] getFields() | 获取 public 变量 |
Field getField(String name) | 根据变量名获取 public 变量 |
Field[] getDeclaredFields() | 获取所有变量,无法获取继承的变量 |
Field getDeclaredField(String name) | 根据变量名获取变量,无法获取继承的变量 |
1
2
3
4
5
6
7
8
9
10public static void main(String[] args) throws Exception { Class<Student> c = Student.class; // 获取所有 pubilc 变量 Field[] fs1 = c.getFields(); Field[] fs2 = c.getDeclaredFields(); // 根据变量名 Field f1 = c.getField("address"); Field f2 = c.getDeclaredField("address"); }
获取变量,创建对象后对变量进行动态赋值
1
2
3
4
5
6
7
8
9public static void main(String[] args) throws Exception { Class<Student> c = Student.class; Constructor<Student> con = c.getConstructor(); Student student = con.newInstance(); Field field = c.getField("address"); field.setAccessible(true); field.set(student, "北京"); }
获取方法
方法 | 说明 |
---|---|
Method[] getMethods() | 获取 public 方法 |
Method getMethod(String name, Class…<?> paramTypes) | 根据名字、参数获取 public 方法 |
Method[] getDeclaredMethods() | 获取所有方法,无法获取继承的方法 |
Method getDeclaredMethod(String name, Class…<?> paramTypes) | 根据名字、参数获取方法,无法获取继承的方法 |
1
2
3
4
5
6
7
8
9
10public static void main(String[] args) throws Exception { Class<Student> c = Student.class; // 获取所有方法 Method[] ms1 = c.getMethods(); Method[] ms2 = c.getDeclaredMethods(); // 获取单个方法 Method m1 = c.getMethod("method"); Method m2 = c.getDeclaredMethod("method"); }
利用 Method 中的 invoke 函数调用方法
1
2
3
4Object invoke(Object obj, Object... args); // obj:调用对象 // args:参数
1
2
3
4
5
6public static void main(String[] args) throws Exception { Class<Student> c = Student.class; Student student = c.getConstructor().newInstance(); // 创建对象 c.getMethod("method").invoke(student); // 调用方法 }
如果调用的是静态方法,invoke 函数的第一个参数只需要传入 null,因为静态方法不与某个对象有关,只与某个类有关
获取注解
在反射中,Field、Constructor 和 Method 类对象都可以获取标注在它们之上的注解
方法 | 说明 |
---|---|
Annotation[] getAnnotations() | 获取对象上所有注解 |
Annotation getAnnotation(Class annotaionClass) | 传入注解类型,获取对象上某个注解 |
Annotation[] getDeclaredAnnotations() | 获取对象上所有注解(显示标记的),无法获取继承的注解 |
Annotation getDeclaredAnnotation(Class annotationClass) | 传入注解类型,获取对象上某个注解(显示),无法获取继承的注解 |
只有注解的 @Retension 标注为 RUNTIME 时,才能够通过反射获取到该注解
定义一个抽象类、一个抽象的继承
1
2
3
4
5
6
7
8
9
10public abstract class Pineapple { void getInfo(); } public class SmallPineapple extends Pineapple { @Transient @Override public void getInfo() { } }
1
2
3
4
5
6
7
8public static void main(String[] args) throws Exception { Class<Student> c = Student.class; // 获取方法 getInfo Method method = c.getMethod("getInfo"); // 获取方法上的注解,这里获取到 @Transient Annotation[] annotations = method.getAnnotations(); }
应用场景
- 举例说明反射的应用场景
- 在 JDBC 中,通过反射动态加载了数据库驱动程序
- Web 服务器利用反射调用 Servlet 的服务方法
- Spring 利用反射注入属性并调用方法
抽象工厂
传统的工厂模式,如果需要生产新的子类,需要修改工厂类,在工厂类中增加新的分支
1
2
3
4
5
6
7
8
9
10public class MapFactory { public Map<Object, object> produceMap(String name) { if ("HashMap".equals(name)) { return new HashMap<>(); } else if ("TreeMap".equals(name)) { return new TreeMap<>(); } // ··· } }
结合反射,工厂类不需要进行条件的判定去特定返回特定对象。在运行时,通过参数传入不同子类的全限定名获取到不同的 Class,调用 newInstance 方法返回不同的子类
例如,在运行时才确定使用哪一种 Map 结构,我们可以利用反射传入某个具体 Map 的全限定名,实例化一个特定的子类
1
2
3
4
5
6
7
8public class MapFactory { public Map<Object, Object> produceMap(String className) { Class c = Class.forName(className); Map<Object, Object> map = c.newInstance(); return map; } }
className 可以指定为 java.util.HashMap,或者 java.util.TreeMap 等等,根据业务场景来定
JDBC
在导入第三方库时,JVM 不会主动去加载外部导入的类,而是等到真正使用时,才去加载需要的类
正是如此,我们可以在获取数据库连接时传入驱动类的全限定名,交给 JVM 加载该类
1
2
3
4
5
6
7
8
9
10
11
12
13public 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; } }
读取配置文件
通过反射,使用配置文件的内容来运行指定的类中的指定的方法
以后想要访问什么类的什么方法,只需要修改一下配置文件即可,灵活性很高
现在有一个配置文件 class.txt
1
2
3className=com.yue.pojo.Student methodName=study
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public static void main(String[] args) throws Exception { // 首先要加载数据 Properties properties = new Properties(); FileReader fileReader = new FileReader("L:\反射\class.txt"); properties.load(fileReader); fileReader.close(); String className = properties.getProperty("className"); String methodName = properties.getProperty("methodName"); // 通过反射来使用 Class<?> c = Class.forName(className); Constructor<?> constructor = c.getConstructor(); Object o = constructor.newInstance(); Method method = c.getMethod(methodName); method.invoke(o); }
添加不同数据
向 ArrayList<Integer> 中添加一个字符串数据:通过反射获取到 List 的 add 方法,调用 invoke 添加数据
1
2
3
4
5
6
7
8
9
10
11public static void main(String[] args) throws Exception{ ArrayList<Integer> list = new ArrayList<>(); list.add(100); // 通过反射拿到 add 方法,然后添加数据 Class<? extends ArrayList> c = list.getClass(); Method add = c.getMethod("add", Object.class); add.invoke(list, "hello"); add.invoke(list, "world"); System.out.println(list); }
如何提升反射的性能
Spring 的反射如何实现
最后
以上就是俏皮饼干最近收集整理的关于反射初体验引入反射机制Class应用场景如何提升反射的性能Spring 的反射如何实现的全部内容,更多相关反射初体验引入反射机制Class应用场景如何提升反射的性能Spring内容请搜索靠谱客的其他文章。
发表评论 取消回复