基于《Java编程思想》第四版
前言
我们要操作一个类实例对象时,一般都要先知道这个类有哪些方法或者成员变量。反射就是在我们不知道这个类有哪些方法或成员变量时,使用特定方式得到类的这些信息,再根据特定规则去调用对应的方法操作类实例对象。
这中间有两个未知条件
- 如何记录类信息
- 如何确定规则
类信息是通过Class
记录的,规则是由程序员定的。
Class
Class
是一个记录类信息的类,每个类(包括Class
)都会有一个Class
对象。其实现也很好猜测和理解:编译器扫描完代码,就能知道类的具体信息,比如有哪些方法,然后把这些信息保存到Class
对象中。
因为Class
对象并不是程序员自己实例化的,所以必须得有一个确定的名字,约定就叫做class
。我们可以通过访问class
对象得到类的信息。
为了获取class
对象,有三种方法
- 通过
类名.class
直接访问
Class c = Integer.class;
- 通过
Class.forName(类名)
函数获取(可能抛出异常,需要放在try catch
中)
Class c = Class.forName("Integer");
- 通过
对象.class
获取
Integer n = new Integer ();
Class c = n.getClass();
不论通过哪种方式获取的class
对象都是同一个对象,也就是说每个类全局只有一个class
对象。可以查看文档知道Class
有哪些接口,以后多用用就会熟能生巧了。
反射
当我们使用反射时,除了Class
对象记录的信息外,还需要一个规则来约束实现者和使用者。
假定有一个持久化功能的规则如下
- 创建表时,表名和列名均为帕斯卡格式
- 使用者存储信息时,使用类似
表名.列名=值
的方式 - 实现的持久化类需要以表名做为类名,以列名作为成员变量名,以set+成员变量名作为方法名,方法名中的成员变量名为帕斯卡格式
按照这个规则实现一个使用了反射的持久化函数,里面没有具体的类型
// 略去了异常处理的代码
public static void save( String s ) {
String[] tmp = s.split("\\.");
String table = tmp[0];
String column = tmp[1].split("=")[0];
String value = tmp[1].split("=")[1];
Class c = Class.forName(table);
Method m = c.getMethod("set" + column, String.class);
Object o = c.getConstructor().newInstance();
m.invoke(o,value);
}
按规则要求实现两个持久化类
class Book{
private String name;
public Book(){
}
public void setName(String name){
this.name = name;
System.out.println("Book name = " + name);
}
}
class Person{
private String name;
public Person(){
}
public void setName(String name){
this.name = name;
System.out.println("Person name = " + name);
}
}
使用者无需关注具体类型,就可以完成存储功能
public static void main(String[] args) {
save("Person.Name=Jack");
save("Book.Name=Five");
}
后续有新增的持久化类时,只要按照规则实现,也能直接嵌入到这个框架中了。
结语
有很多框架都使用了反射机制,后面还要继续深入去了解反射的使用方式。