|
| 1 | +--- |
| 2 | +title: Java 反射 |
| 3 | +date: 2018/06/05 |
| 4 | +categories: |
| 5 | +- javase |
| 6 | +tags: |
| 7 | +- java |
| 8 | +- javase |
| 9 | +- advanced |
| 10 | +--- |
| 11 | + |
| 12 | +# Java 反射 |
| 13 | + |
| 14 | +<!-- TOC --> |
| 15 | + |
| 16 | +- [Java 反射](#java-反射) |
| 17 | + - [简介](#简介) |
| 18 | + - [什么是反射](#什么是反射) |
| 19 | + - [反射的主要用途](#反射的主要用途) |
| 20 | + - [反射的缺点](#反射的缺点) |
| 21 | + - [Class](#class) |
| 22 | + - [获得 Class 对象](#获得-class-对象) |
| 23 | + - [判断是否为某个类的实例](#判断是否为某个类的实例) |
| 24 | + - [创建实例](#创建实例) |
| 25 | + - [Field](#field) |
| 26 | + - [Method](#method) |
| 27 | + - [Constructor](#constructor) |
| 28 | + - [Array](#array) |
| 29 | + - [推荐阅读](#推荐阅读) |
| 30 | + - [参考资料](#参考资料) |
| 31 | + |
| 32 | +<!-- /TOC --> |
| 33 | + |
| 34 | +## 简介 |
| 35 | + |
| 36 | +### 什么是反射 |
| 37 | + |
| 38 | +反射(Reflection)是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。 |
| 39 | + |
| 40 | +简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。 |
| 41 | + |
| 42 | +反射通常被用于需要在 JVM 中检查或修改应用程序的运行时行为的程序。 |
| 43 | + |
| 44 | +### 反射的主要用途 |
| 45 | + |
| 46 | +* 开发通用框架 |
| 47 | + |
| 48 | +反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 JavaBean、Filter 等),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。 |
| 49 | + |
| 50 | +* 可扩展性功能 |
| 51 | + |
| 52 | +应用程序可以通过使用完全限定名称创建可扩展性对象实例来使用外部的用户定义类。 |
| 53 | + |
| 54 | +* 类浏览器和可视化开发环境 |
| 55 | + |
| 56 | +类浏览器需要能够枚举类的成员。可视化开发环境可以利用反射中可用的类型信息来帮助开发人员编写正确的代码。 |
| 57 | + |
| 58 | +* 调试器和测试工具 |
| 59 | + |
| 60 | +调试器需要能够检查类上的私有成员。测试工具可以利用反射来系统地调用在类中定义的可发现集 API,以确保测试套件中的高级代码覆盖率。 |
| 61 | + |
| 62 | +### 反射的缺点 |
| 63 | + |
| 64 | +* 性能开销 |
| 65 | + |
| 66 | +由于反射涉及动态解析的类型,因此无法执行某些 Java 虚拟机优化。因此,反射操作的性能要比非反射操作的性能要差,应该在性能敏感的应用程序中频繁调用的代码段中避免。 |
| 67 | + |
| 68 | +* 安全限制 |
| 69 | + |
| 70 | +反射需要在安全管理器下运行时可能不存在的运行时权限。对于必须在受限制的安全上下文中运行的代码(如 Applet 中),这是一个重要的考虑因素。 |
| 71 | + |
| 72 | +* 内部曝光 |
| 73 | + |
| 74 | +由于反射允许代码执行在非反射代码中非法的操作,例如访问私有字段和方法,所以反射的使用可能会导致意想不到的副作用,这可能会导致代码功能失常并可能破坏可移植性。反射代码打破了抽象,因此可能会随着平台的升级而改变行为。 |
| 75 | + |
| 76 | +## Class |
| 77 | + |
| 78 | +对于每种类型的对象,JVM 实例化一个 `java.lang.Class` 的不可变实例,该实例提供了检查对象的运行时属性(包括其成员和类型信息)的方法。类还提供了创建新类和对象的功能。 |
| 79 | + |
| 80 | +最重要的是,**`java.lang.Class` 是所有反射 API 的入口点**。 |
| 81 | + |
| 82 | +java.lang.reflect 包中的类都没有 public 构造方法。 |
| 83 | + |
| 84 | +### 获得 Class 对象 |
| 85 | + |
| 86 | +获得 Class 的三种方法: |
| 87 | + |
| 88 | +(1)**使用 Class 类的 `forName` 静态方法**,示例: |
| 89 | + |
| 90 | +```java |
| 91 | +Class c1 = Class.forName("io.github.dunwu.javase.reflect.ReflectClassTest"); |
| 92 | +Class c2 = Class.forName("[D"); |
| 93 | +Class c3 = Class.forName("[[Ljava.lang.String;"); |
| 94 | +``` |
| 95 | + |
| 96 | +使用类的完全限定名来反射对象的类。常见的应用场景为:在 JDBC 开发中常用此方法加载数据库驱动。 |
| 97 | + |
| 98 | +(2)**直接获取某一个对象的 `class`**,示例: |
| 99 | + |
| 100 | +```java |
| 101 | +Class c1 = boolean.class; |
| 102 | +Class c2 = java.io.PrintStream.class; |
| 103 | +Class c3 = int[][][].class; |
| 104 | +``` |
| 105 | + |
| 106 | +(3)**调用某个对象的 `getClass()` 方法**,示例: |
| 107 | + |
| 108 | +```java |
| 109 | +Class c = "foo".getClass(); |
| 110 | + |
| 111 | +enum E {A, B} |
| 112 | +Class c2 = E.A.getClass(); |
| 113 | + |
| 114 | +byte[] bytes = new byte[1024]; |
| 115 | +Class c3 = bytes.getClass(); |
| 116 | + |
| 117 | +Set<String> set = new HashSet<>(); |
| 118 | +Class c4 = set.getClass(); |
| 119 | +``` |
| 120 | + |
| 121 | +### 判断是否为某个类的实例 |
| 122 | + |
| 123 | +一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的 isInstance()方法来判断是否为某个类的实例,它是一个 Native 方法: |
| 124 | + |
| 125 | +```java |
| 126 | +ArrayList arrayList = new ArrayList(); |
| 127 | +if (arrayList instanceof List) { |
| 128 | + System.out.println("ArrayList is List"); |
| 129 | +} |
| 130 | +if (List.class.isInstance(arrayList)) { |
| 131 | + System.out.println("ArrayList is List"); |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +### 创建实例 |
| 136 | + |
| 137 | +通过反射来生成对象主要有两种方式。 |
| 138 | + |
| 139 | +(1)使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例。 |
| 140 | + |
| 141 | +```java |
| 142 | +Class<?> c1 = String.class; |
| 143 | +Object str1 = c1.newInstance(); |
| 144 | +System.out.println(str1.getClass().getCanonicalName()); |
| 145 | +``` |
| 146 | + |
| 147 | +(2)先通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建实例。这种方法可以用指定的构造器构造类的实例。 |
| 148 | + |
| 149 | +```java |
| 150 | +//获取String所对应的Class对象 |
| 151 | +Class<?> c2 = String.class; |
| 152 | +//获取String类带一个String参数的构造器 |
| 153 | +Constructor constructor = c2.getConstructor(String.class); |
| 154 | +//根据构造器创建实例 |
| 155 | +Object obj = constructor.newInstance("bbb"); |
| 156 | +System.out.println(obj.getClass().getCanonicalName()); |
| 157 | +``` |
| 158 | + |
| 159 | +## Field |
| 160 | + |
| 161 | +获取某个 Class 对象的成员变量的方法有: |
| 162 | + |
| 163 | +* getFiled: 访问公有的成员变量 |
| 164 | +* getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量 |
| 165 | + |
| 166 | +示例如下: |
| 167 | + |
| 168 | +```java |
| 169 | +class FieldSpy<T> { |
| 170 | + public boolean[][] b = {{ false, false }, { true, true } }; |
| 171 | + public String name = "Alice"; |
| 172 | + public List<Integer> list; |
| 173 | + public T val; |
| 174 | +} |
| 175 | + |
| 176 | +Field f1 = FieldSpy.class.getField("b"); |
| 177 | +Field f2 = FieldSpy.class.getField("name"); |
| 178 | +Field f3 = FieldSpy.class.getField("list"); |
| 179 | +Field f4 = FieldSpy.class.getField("val"); |
| 180 | +``` |
| 181 | + |
| 182 | +输出: |
| 183 | + |
| 184 | +``` |
| 185 | +Type: class [[Z |
| 186 | +Type: class java.lang.String |
| 187 | +Type: interface java.util.List |
| 188 | +Type: class java.lang.Object |
| 189 | +``` |
| 190 | + |
| 191 | +## Method |
| 192 | + |
| 193 | +获取某个 Class 对象的方法集合的方法有: |
| 194 | + |
| 195 | +(1)getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 |
| 196 | + |
| 197 | +```java |
| 198 | +Method[] methods1 = Thread.class.getDeclaredMethods(); |
| 199 | +System.out.println("Thread getDeclaredMethods 清单(数量 = " + methods1.length + "):"); |
| 200 | +for (Method m : methods1) { |
| 201 | + System.out.println(m); |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +(2)getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。 |
| 206 | + |
| 207 | +```java |
| 208 | +Method[] methods2 = Thread.class.getMethods(); |
| 209 | +System.out.println("Thread getMethods 清单(数量 = " + methods2.length + "):"); |
| 210 | +for (Method m : methods2) { |
| 211 | + System.out.println(m); |
| 212 | +} |
| 213 | +``` |
| 214 | + |
| 215 | +(3)getMethod() 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应 Class 的对象。 |
| 216 | + |
| 217 | +```java |
| 218 | +Method method = Thread.class.getMethod("join", long.class, int.class); |
| 219 | +System.out.println(method); |
| 220 | +``` |
| 221 | + |
| 222 | +## Constructor |
| 223 | + |
| 224 | +获取某个 Class 对象的构造方法集合的方法有: |
| 225 | + |
| 226 | +(1)getDeclaredConstructor()方法返回类的所有构造方法,包括公共、保护、默认(包)访问和私有方法。 |
| 227 | + |
| 228 | +```java |
| 229 | +Method[] methods1 = Thread.class.getDeclaredMethods(); |
| 230 | +System.out.println("Thread getDeclaredMethods 清单(数量 = " + methods1.length + "):"); |
| 231 | +for (Method m : methods1) { |
| 232 | + System.out.println(m); |
| 233 | +} |
| 234 | +``` |
| 235 | + |
| 236 | +(2)getConstructors() 方法返回某个类的所有公用(public)构造方法。 |
| 237 | + |
| 238 | +```java |
| 239 | +Constructor<?>[] constructors = FileOutputStream.class.getConstructors(); |
| 240 | +System.out.println("FileOutputStream 构造方法清单(数量 = " + constructors.length + "):"); |
| 241 | +for (Constructor c : constructors) { |
| 242 | + System.out.println(c); |
| 243 | +} |
| 244 | +``` |
| 245 | + |
| 246 | +(3)getConstructor() 方法返回一个特定的方法,参数为方法的参数对应 Class 的对象。 |
| 247 | + |
| 248 | +```java |
| 249 | +Constructor constructor = FileOutputStream.class.getConstructor(String.class, boolean.class); |
| 250 | +System.out.println(constructor); |
| 251 | +``` |
| 252 | + |
| 253 | +## Array |
| 254 | + |
| 255 | +## 推荐阅读 |
| 256 | + |
| 257 | +本文示例代码见:[源码](https://github.com/dunwu/javase-notes/tree/master/codes/advanced/src/main/java/io/github/dunwu/javase) |
| 258 | + |
| 259 | +本文同步维护在:[Java 系列教程](https://github.com/dunwu/javase-notes) |
| 260 | + |
| 261 | +## 参考资料 |
| 262 | + |
| 263 | +* [Java 编程思想(Thinking in java)](https://item.jd.com/10058164.html) |
| 264 | +* [深入解析 Java 反射(1) - 基础](https://www.sczyh30.com/posts/Java/java-reflection-1/) |
| 265 | +* https://docs.oracle.com/javase/tutorial/reflect/index.html |
0 commit comments