Java反射

参考博客:https://blog.csdn.net/xiaohanluo/article/details/52034127

因博客主未标明不可转载,若内容涉及侵权请及时告知,我会尽快修改和删除相关内容

反射

第一节 简介

1.1 定义

  能够在运行时分析类称为反射,如泛型类在编译后会比擦除,但通过反射可以获取泛型类的信息,反射会将.class类的信息映射为Class对象,通过Class对象的方法可以获得类的各种信息。

1.2 作用

  1. 在运行时分析类
  2. 在运行时查看对象
  3. 实现通用的数组操作代码
  4. 利用Method对象

1.3 使用场景

  编写应用程序很少会使用反射,因为性能不好,而且破坏了类的封装,大部分场景并不会用到反射。反射常用于编写工具和框架,最近会在写一些导出各种格式工具时用到。

  1. 开发通用框架,如Spring等框架需要通过xml配置文件来配置JavaBean等,为了保证通用性,需要在运行时根据配置文件来决定加载的对象或类。
  2. 动态代理,需要由反射技术来实现。
  3. 注解,其本身只能进行标记,需要通过反射技术来调用注解解释器去执行行为,没有反射注解只能类似于注释。
  4. 可扩展,通过使用完全限定名称的可扩展性对象调用外部用户定义类

1.4 类的加载过程

  类在编译阶段由.java文件编译为二进制的.class文件;

  在加载和执行阶段,JVM中的类加载器读取生成的字节码文件,取出二进制数据加载到内存中,解析.class文件存储的信息。类加载器会根据类的全限定名来获取此类的二进制字节流;将字节流所代表的静态存储结构转化为方法区的运行时数据结构;在内存中生成代表这个类的 java.lang.Class 对象。

  加载结束后,JVM 开始进行连接阶段(包含验证、准备、初始化)。经过这一系列操作,类的变量会被初始化。

可以尝试把Test.java编译,观察生成的字节码文件

1
2
>javac -encoding utf-8 -d . Test.java
>javap -v Test.class

  详细原理我会在之后的类的加载机制中整理。


第二节 API

2.1 工具类和接口

java.lang.reflect包中提供了接口Type

  • Class<T> 实现了Type接口
  • TypeVariableWildcardTypeParameterizedTypeGenericArrayType 等接口继承了Type,其中 TypeVariable 描述类型变量,如 T extends Comparable<? super T>
  • WildcardType 描述通配符,如 ? super T
  • ParameterizedType 描述泛型类或接口类型,如 Comparable<? super T>
  • GenericArrayType 描述泛型数组,如 T[]
说明
Class 在反射过程中表示内存中的一个Java类,Class可以表示类,接口,原始数据类型,数组等
Object Java中所有类的超类
Constructor 封装了类的构造函数的属性信息,包括访问权限和动态调用信息
Field 提供了类或接口的成员变量属性信息,包括访问权限和动态修改
Method 提供了类或接口的方法属性信息,包括访问权限和动态调用信息
Member 反映关于单个成员(字段或方法)或构造函数的标识信息
Array 该类提供动态地生成和访问 JAVA 数组的方法
Proxy 提供动态地生成代理类和类实例的静态方法

2.2 Class类

  在程序运行时,Java会为所有对象维护一个叫做运行时的类型标识。根据此信息可以跟踪到每个对象所属的类,JVM会使用这些信息执行相应的方法。保存这些信息的类叫Class,Object类中的getClass()方法会返回一个Class类型实例。Class对象记录对应特定类的属性,如getName()方法获取类的名字(forName()方法则根据类名获取Class对象)

  对于Test.java这个类,其有唯一与其对应的一个Class对象,可以通过以下方式获取Class对象。

1
2
3
4
Class<?> cl1 = Class.forName(name);//Class类的forName()静态方法动态的获得Class对象
Class<?> cl2 = Test.class;//可以通过如Test.class来获取固定的Class对象。
Test test = new Test();
Class<?> cl2 = test.getClass();//可以通过Object 的 getClass 方法

第三节 实例

3.1 剖析类

以下代码接用<Java核心技术>相关章节思路,通过反射剖析输入类的各种信息,只涉及解析类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public class test {
public static void main(String[] args){
String name;
if(args.length > 0) name = args[0];
else {
try(Scanner in = new Scanner(System.in)){
System.out.println("请输入class name: ");
name = in.next();
}
}

try{
Class<?> cl = Class.forName(name);//Class类的forName()静态方法动态的获得Class对象。

System.out.println("创建实例: ");
String str1 = (String) cl.getDeclaredConstructor().newInstance();//通过调用类声明的构造器来创建实例
System.out.println("str1: " + str1);

Constructor constructor = cl.getConstructor(String.class);//获取String类的构造器
//根据构造器创建实例
String str2 = (String) constructor.newInstance("bbb");//调用带字符串参数的构造器
System.out.println("str2: " + str2);

printClass(cl);
System.out.println("方法: ");
for(Method method : cl.getDeclaredMethods())//获得类的所有方法
printMethod(method);
}catch (ClassNotFoundException | NoSuchMethodException |
InstantiationException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException ex){
ex.printStackTrace();
}
}

//打印类信息
public static void printClass(Class<?> cl){
System.out.println("Class<?>: " + cl);

System.out.println("所有已声明的类成员: ");
for(Field field : cl.getDeclaredFields())//获得类的所有已声明的类成员
System.out.println("field[" + field.getName() + "] Type[" + field.getType() + "]");

System.out.println("所有公有的(public)类成员: ");
for(Field field : cl.getFields())//获得类的所有公有的(public)类成员
System.out.println("field[" + field.getName() + "] Type[" + field.getType() + "]");

System.out.println("类结构: ");
printTypes(cl.getTypeParameters(),"<",",",">",true);//若为泛型类型则获取泛型类型变量,否则返回长度0数组
Type sc = cl.getGenericSuperclass();//获得被声明为这一类型的超类的泛型类型,若是Object,或不是类返回null
if(sc != null){
System.out.print(" extends ");
printType(sc,false);
}
printTypes(cl.getGenericInterfaces(),"<",",",">",false);//获得被声明为这一类型的接口的泛型类型,若没有实现接口,返回长度0数组
System.out.println();
}

//打印方法信息
public static void printMethod(Method method){
String name = method.getName();
System.out.print(Modifier.toString(method.getModifiers()));
System.out.print(" ");
printTypes(method.getTypeParameters(),"<",",",">",true);//若方法为泛型方法,则获得泛型变量,否则返回长度0数组

printType(method.getGenericReturnType(),false);//获得此方法被声明的泛型返回类型
System.out.print(" ");
System.out.print(name);
System.out.print("(");
printTypes(method.getGenericParameterTypes(),"<",",",">",true);//获得此方法被声明的泛型参数类型,若无参数,则返回长度0数组
System.out.print(")");
System.out.println("");
}

//打印泛型类型信息
public static void printTypes(Type[] types,String pre,String sep,String suf,boolean isDefinition){
if(pre.equals(" extends ") && Arrays.equals(types,new Type[]{Object.class})) return;
if(types.length > 0) System.out.print(pre);
for(int i = 0;i < types.length;i++){
if(i > 0) System.out.print(sep);
printType(types[i],isDefinition);
}
if(types.length > 0) System.out.print(suf);
}

//打印类型信息
public static void printType(Type type,boolean ifDefinition){
if(type instanceof Class){
Class<?> t = (Class<?>) type;
System.out.print(t.getName());//获得类型变量名
}else if(type instanceof TypeVariable){
TypeVariable<?> t = (TypeVariable<?>) type;
System.out.print(t.getName());
if(ifDefinition)
printTypes(t.getBounds()," extends "," & ","",false);//获得类型变量的子类限定
}else if(type instanceof WildcardType){
WildcardType t = (WildcardType) type;
System.out.print("?");
printTypes(t.getUpperBounds()," extends "," & ","",false);//获得这个类型变量的子类extends限定,否则0数组
printTypes(t.getLowerBounds()," super "," & ","",false);//获得这个类型变量的超类super限定,否则0数组
}else if(type instanceof ParameterizedType){
ParameterizedType t = (ParameterizedType) type;
Type owner = t.getOwnerType();//若是内部类型,返回其外部类型,若是顶级类型,返回null
if(owner != null){
printType(owner,false);
System.out.print(".");
}
printType(t.getRawType(),false);//获得这个参数化类型的原始类型
printTypes(t.getActualTypeArguments(),"<",",",">",false);//获得这个参数化类型声明时所使用的类型参数
}else if(type instanceof GenericArrayType){
GenericArrayType t = (GenericArrayType) type;
System.out.print("");
printType(t.getGenericComponentType(),ifDefinition);//获得声明该数组类型的泛型组件类型
System.out.print("[]");
}
}
}

结果输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
请输入class name: 

java.lang.String

创建实例:
str1:
str2: bbb
Class<?>: class java.lang.String
所有已声明的类成员:
field[value] Type[class [B]
field[coder] Type[byte]
field[hash] Type[int]
field[serialVersionUID] Type[long]
field[COMPACT_STRINGS] Type[boolean]
field[serialPersistentFields] Type[class [Ljava.io.ObjectStreamField;]
field[CASE_INSENSITIVE_ORDER] Type[interface java.util.Comparator]
field[LATIN1] Type[byte]
field[UTF16] Type[byte]
所有公有的(public)类成员:
field[CASE_INSENSITIVE_ORDER] Type[interface java.util.Comparator]
类结构:
extends java.lang.Object<java.io.Serializable,java.lang.Comparable<java.lang.String>,java.lang.CharSequence>
方法:
[B value()
byte coder()
public boolean equals(<java.lang.Object>)
public int length()
public java.lang.String toString()
public int hashCode()
public void getChars(<int,int,[C,int>)
public int compareTo(<java.lang.String>)
public volatile int compareTo(<java.lang.Object>)
public int indexOf(<java.lang.String,int>)
public int indexOf(<int>)
public int indexOf(<int,int>)
static int indexOf(<[B,byte,int,java.lang.String,int>)
public int indexOf(<java.lang.String>)
static void checkIndex(<int,int>)
public static java.lang.String valueOf(<int>)
public static java.lang.String valueOf(<char>)
public static java.lang.String valueOf(<boolean>)
public static java.lang.String valueOf(<long>)
public static java.lang.String valueOf(<float>)
public static java.lang.String valueOf(<double>)
public static java.lang.String valueOf(<java.lang.Object>)
public static java.lang.String valueOf(<[C>)
public static java.lang.String valueOf(<[C,int,int>)
private static java.lang.Void rangeCheck(<[C,int,int>)
public java.util.stream.IntStream codePoints()
public boolean isEmpty()
public char charAt(<int>)
public int codePointAt(<int>)
public int codePointBefore(<int>)
public int codePointCount(<int,int>)
public int offsetByCodePoints(<int,int>)
public void getBytes(<int,int,[B,int>)
void getBytes(<[B,int,byte>)
public [B getBytes(<java.nio.charset.Charset>)
public [B getBytes()
public [B getBytes(<java.lang.String>)
public boolean contentEquals(<java.lang.StringBuffer>)
public boolean contentEquals(<java.lang.CharSequence>)
private boolean nonSyncContentEquals(<java.lang.AbstractStringBuilder>)
public boolean equalsIgnoreCase(<java.lang.String>)
public int compareToIgnoreCase(<java.lang.String>)
public boolean regionMatches(<boolean,int,java.lang.String,int,int>)
public boolean regionMatches(<int,java.lang.String,int,int>)
public boolean startsWith(<java.lang.String,int>)
public boolean startsWith(<java.lang.String>)
public boolean endsWith(<java.lang.String>)
static int lastIndexOf(<[B,byte,int,java.lang.String,int>)
public int lastIndexOf(<int,int>)
public int lastIndexOf(<java.lang.String>)
public int lastIndexOf(<java.lang.String,int>)
public int lastIndexOf(<int>)
public java.lang.String substring(<int,int>)
public java.lang.String substring(<int>)
public java.lang.CharSequence subSequence(<int,int>)
public java.lang.String concat(<java.lang.String>)
public java.lang.String replace(<java.lang.CharSequence,java.lang.CharSequence>)
public java.lang.String replace(<char,char>)
public boolean matches(<java.lang.String>)
public boolean contains(<java.lang.CharSequence>)
public java.lang.String replaceFirst(<java.lang.String,java.lang.String>)
public java.lang.String replaceAll(<java.lang.String,java.lang.String>)
public [Ljava.lang.String; split(<java.lang.String>)
public [Ljava.lang.String; split(<java.lang.String,int>)
public static transient java.lang.String join(<java.lang.CharSequence,[Ljava.lang.CharSequence;>)
public static java.lang.String join(<java.lang.CharSequence,java.lang.Iterable<? extends java.lang.CharSequence>>)
public java.lang.String toLowerCase()
public java.lang.String toLowerCase(<java.util.Locale>)
public java.lang.String toUpperCase(<java.util.Locale>)
public java.lang.String toUpperCase()
public java.lang.String trim()
public java.util.stream.IntStream chars()
public [C toCharArray()
public static transient java.lang.String format(<java.lang.String,[Ljava.lang.Object;>)
public static transient java.lang.String format(<java.util.Locale,java.lang.String,[Ljava.lang.Object;>)
public static java.lang.String copyValueOf(<[C,int,int>)
public static java.lang.String copyValueOf(<[C>)
public native java.lang.String intern()
private boolean isLatin1()
static void checkOffset(<int,int>)
static void checkBoundsOffCount(<int,int,int>)
static void checkBoundsBeginEnd(<int,int,int>)
static [B access$100(<java.lang.String>)
static boolean access$200(<java.lang.String>)

3.2 创建实例对象

两种方法:用 Class 对象的 newInstance 方法和用 Constructor 对象的 newInstance 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class NewInstanceDemo {
public static void main(String[] args)
throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> c1 = StringBuilder.class;
StringBuilder sb = (StringBuilder) c1.newInstance();
sb.append("aaa");
System.out.println(sb.toString());
//获取String所对应的Class对象
Class<?> c2 = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c2.getConstructor(String.class);
//根据构造器创建实例
String str2 = (String) constructor.newInstance("bbb");
System.out.println(str2);
}
}

3.3 获取属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ReflectFieldDemo {
class FieldSpy<T> {
public Boolean[][] b = {{false, false}, {true, true}};
public String name = "Alice";
public List<Integer> list;
public T val;
}
public static void main(String[] args) throws NoSuchFieldException {
Field f1 = FieldSpy.class.getField("b");
System.out.format("Type: %s%n", f1.getType());
Field f2 = FieldSpy.class.getField("name");
System.out.format("Type: %s%n", f2.getType());
Field f3 = FieldSpy.class.getField("list");
System.out.format("Type: %s%n", f3.getType());
Field f4 = FieldSpy.class.getField("val");
System.out.format("Type: %s%n", f4.getType());
}
}
//Output:
//Type: class [[Z
//Type: class java.lang.String
//Type: interface java.util.List
//Type: class java.lang.Object

3.4 方法调用

Class 对象提供以下方法获取对象的方法(Method):

  • getMethod - 返回类或接口的特定方法。其中第一个参数为方法名称,后面的参数为方法参数对应 Class 的对象。
  • getDeclaredMethod - 返回类或接口的特定声明方法。其中第一个参数为方法名称,后面的参数为方法参数对应 Class 的对象。
  • getMethods - 返回类或接口的所有 public 方法,包括其父类的 public 方法。
  • getDeclaredMethods - 返回类或接口声明的所有方法,包括 public、protected、默认(包)访问和 private 方法,但不包括继承的方法。

获取一个 Method 对象后,可以用 invoke 方法来调用这个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test {

public static void main(String args[]) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Printer printer = new Printer("This is a printer");
Class clazz = printer.getClass();
Method m1 = clazz.getDeclaredMethod("print");
// 注意参数类型要正确,包装类型不能代替基本类型
Method m2 = clazz.getDeclaredMethod("setMsg", String.class);
Method m3 = clazz.getDeclaredMethod("getMsg");
m1.invoke(printer);
m2.invoke(printer, "reset msg");
String msg = (String) m3.invoke(printer);
System.out.println(msg);
}
}

class Printer {
private String msg;

......

public void print() {
System.out.println("Printer print");
}
}

结果输出:

1
2
Printer print
reset msg

3.5 静态方法调用

静态函数调用不需要先创建类的实例对象。

1
2
3
Class clazz = Class.forName("xxx");  
Method staticMethod = clazz.getDeclaredMethod("xx");
staticMethod.invoke(clazz);//这里不需要newInstance,可以直接通过字节码对象调用

3.6 私有成员变量赋值

首先使用反射提供的setAccessible(true),使我们可以访问到非public的变量。然后使用反射提供的getDeclaredFields()方法获取某个类的所有声明字段或getDeclaredField()方法获取指定字段。

1
2
3
4
5
6
7
//  如果直接通过反射给类的private成员变量赋值,是不允许的
Class clazz = Class.forName("xxx");
Object object = clazz.newInstance();
Field field = clazz.getDeclaredField("age");
field.setAccessible(true);//设置允许访问
field.set(student, 10);
System.out.println(field.get(student));

参考博客和文章书籍等:

《Java核心技术 卷Ⅰ》

深入理解 Java 反射和动态代理

因博客主等未标明不可引用,若部分内容涉及侵权请及时告知,我会尽快修改和删除相关内容