反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制
每一个Java程序执行必须通过编译、加载、链接和初始化四个阶段
- 编译:将.java.文件编译成字节码.class文件
- 加载:查找并加载类的二进制数据
- 链接:
- 验证:确保被加载类的正确性
- 为类的静态变量分配内存,并将其初始化为默认值
- 将类中的符号转换为直接引用
- 初始化:为类的静态变量赋予正确的初始值
什么是Class类
在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息
也就是说,无论你是什么对象,总会有有一个隐藏的Class对象与你相对应,而Class的实例表示正在运行的 Java 应用程序中的类和接口。借此,实现了我们Java的反射机制。
获取Class实例的三种方式:
- 实例化对象调用getClass()方法
- 使用Class类的静态方法forName(),用类的名字获取一个Class实例
- 运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例
用代码来看一看:
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
| public class reflect {
public static void main(String[] args) throws ClassNotFoundException {
Apple apple = new Apple();
Class a1 = apple.getClass();
Class a2 = Class.forName("p1.apple");
Class a3 = Apple.class;
System.out.printf("a1: %s\na2: %s\na3: %s", a1, a2, a3);
}
}
class Apple {
private Integer weight;
private String color; }
|
打印结果:
1 2 3 4
| a1: class p1.apple a2: class p1.apple a3: class p1.apple 进程已结束,退出代码0
|
通过反射创建类对象
通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance()
方法、通过 Constructor 对象的 newInstance()
方法。
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
| public class reflect {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class temp1 = Apple.class; Apple apple1 = (Apple) temp1.newInstance(); Class temp2 = Apple.class; Constructor constructor1 = temp2.getConstructor(); Apple apple2 = (Apple)constructor1.newInstance();
Class temp3 = Apple.class; Constructor constructor2 = temp3.getConstructor(Integer.class,String.class); Apple apple = (Apple)constructor2.newInstance(2, "Red"); System.out.println(apple);
}
}
class Apple {
private Integer weight;
private String color;
public Apple() { System.out.println("我是无参构造!"); }
public Apple(Integer weight,String color) { this.weight = weight; this.color = color; }
@Override public String toString() { return "Apple{" + "weight=" + weight + ", color='" + color + '\'' + '}'; }
}
|
打印结果:
1 2 3 4 5
| 我是无参构造! 我是无参构造! Apple{weight=2, color='Red'}
进程已结束,退出代码0
|
通过反射获取类属性、方法、构造器
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
| public class reflect {
public static void main(String[] args) throws NoSuchFieldException {
Field[] fields1 = Apple.class.getFields(); System.out.println("getFields结果"); Arrays.stream(fields1).forEach(System.out::println);
Field[] fields2 = Apple.class.getDeclaredFields(); System.out.println("getDeclaredFields结果"); Arrays.stream(fields2).forEach(System.out::println);
Field field1 = Apple.class.getField("noThing"); System.out.println("getField结果"); System.out.println(field1);
Field field2 = Apple.class.getDeclaredField("color"); System.out.println("getDeclaredField结果"); System.out.println(field2);
Method[] methods = Apple.class.getDeclaredMethods(); System.out.println("getDeclaredMethods结果"); Arrays.stream(methods).forEach(System.out::println);
Constructor[] constructors = Apple.class.getDeclaredConstructors(); System.out.println("getDeclaredConstructors结果"); Arrays.stream(constructors).forEach(System.out::println);
}
}
class Apple {
private Integer weight;
private String color;
public String noThing;
public Apple() {
}
public Apple(Integer weight, String color) { this.weight = weight; this.color = color; }
@Override public String toString() { return "Apple{" + "weight=" + weight + ", color='" + color + '\'' + '}'; }
}
|
打印结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| getFields结果 public java.lang.String p1.Apple.noThing getDeclaredFields结果 private java.lang.Integer p1.Apple.weight private java.lang.String p1.Apple.color public java.lang.String p1.Apple.noThing getField结果 public java.lang.String p1.Apple.noThing getDeclaredField结果 private java.lang.String p1.Apple.color getDeclaredMethods结果 public java.lang.String p1.Apple.toString() getDeclaredConstructors结果 public p1.Apple() public p1.Apple(java.lang.Integer,java.lang.String)
|
更改访问权限和实例赋值
首先,通过field.setAccessible()
可更改属性的访问权限
我们写一个例子:
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
| public class reflect {
public static void main(String[] args) throws NoSuchFieldException {
Apple apple = new Apple();
Field[] fields = Apple.class.getDeclaredFields(); Arrays.stream(fields).forEach( field -> { field.setAccessible(true); System.out.println(field); try { if (field.getType() == Integer.class) { field.set(apple, 5); } else if (field.getType() == String.class) { field.set(apple, "Red"); } } catch (IllegalAccessException e) { e.printStackTrace(); } }); System.out.println(apple); }
}
class Apple {
private Integer weight;
private String color;
@Override public String toString() { return "Apple{" + "weight=" + weight + ", color='" + color + '\'' + '}'; }
}
|
打印结果:
1 2 3 4 5
| private java.lang.Integer p1.Apple.weight private java.lang.String p1.Apple.color Apple{weight=5, color='Red'}
进程已结束,退出代码0
|
通过源码文档和打印结果,可见setAccessable()
方法并没有改变类字段的访问权限,而是作为一个标志,使得我们反射获取实例过程中可以对其进行操作
运用场景
在我看来,反射机制实际上就是上帝模式,如果说方法的调用是 Java 正确的打开方式,那反射机制就是上帝偷偷开的后门,只要存在对应的class,一切都能够被调用。
众所周知,语言有静态语言和动态语言两大分类,静态语言例如C/C++、Java、C#等,动态语言有Python、PHP、JavaScript等。为了让Java语言也有动态语言的特性,有了反射机制,解耦以及提高代码的灵活性。
反射在开发过中或许并不常见,可我们使用的框架工具底层都有反射的存在。动态代理设计模式、JDBC 的数据库的连接、Spring 框架的使用等都应用到了反射机制。