引言
- 世间万事万物都各有其 “基”,树之基在于根,楼之基在于架,为人之要在于德,为政之要在于民,其实在某种程度上来讲,“基”和“要”可以称为是同根并同枝,他们表示的是相同的意思,那我们不仅要思考,java的“基”和“要”又是什么呢?其实我们可以毫不犹豫的回答这就是“反射”,下文将逐步揭开反射之秘。
java程序的运行过程
- 在来学习反射之前,首先要学习java程序运行的过程。当程序要使用某个类时,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤。
类加载
类加载是指将class文件读入内存,并为之创建一个 java.lang.Class 对象任何类被使用时,系统都会为之建立一个 java.lang.Class 对象。
类加载过程一般要经历三个过程。
1、通过一个类的全限定名来获取其定义的二进制字节流(获取流)
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(方法区)。
3、在 Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数据的访问入口(生成访问入口)。
链接
链接分为三个阶段
1、验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
2、准备阶段:负责为类的类变量分配内存,并设置默认初始化值。
3、解析阶段:将类的二进制数据中的符号引用替换为直接引用。
验证:
验证是链接第一步,这一阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成 4 个阶段的检验动作
文件格式验证:验证字节流是否符合 Class 文件格式的规范;例如:是否以 0xCAFEBABE 开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
元数据验证:对字节码描述的信息进行语义分析(注意:对比 javac 编译阶段的语义分析),以保证其描述的信息符合 Java 语言规范的要求;例如:这个类除了java.lang.Object 之外是否有父类。
字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
符号引用验证:确保解析动作能正确执行。
验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果不需要验证,那么可以考虑采用-Xverifynone 参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
初始化
为类的静态变量赋予正确的初始值,JVM 负责对类进行初始化,主要对类变量进行初始化。在 Java 中对类变量进行初始值设定有两种方式:
1,声明类变量是指定初始值。
2,使用静态代码块为类变量指定初始值。
类的初始化步骤或 JVM 初始化的步骤如下:
1)如果这个类还没有被加载和链接,那先进行加载和链接 ;
2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就直接初始化父类(不适用于接口);
3 ) 假如类中存在初始化语句(如 static 变量和 static 块),那就依次执行这些初始化语句。
java类加载器
java类加载器的主要作用是负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。
JVM的类加载器
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
缓存机制:**保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,**存储到缓存区。
Java中的内置类加载器
Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父加载类。
Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的JavaSE平台API,其实现类和JDK特定的运行时类
System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap。
反射
反射是指在运行时去获取一个类的变量和方法信息(成员方法和构造方法)。然后通过获取到的信息来创建对象,是调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展,下面是一个简单的反射示意图。
其实讲的接地气一点反射就是将一个类的各个部分封装成其他对象
反射简例示意图
无论是Student类或者是Teacher类,在运行时通过反射机制获得来获取这两个类的信息,来创建新的规则对象。
案例一
1、通过properties文件加载来配置反射文件
实体类
public class Student {
private String username;
private String age;
private String gender;
private String address;
private String phone;
public Student() {
}
public Student(String username, String age,
String gender, String address, String phone) {
this.username = username;
this.age = age;
this.gender = gender;
this.address = address;
this.phone = phone;
}
public String getUsername() {
return username;
}
public Student setUsername(String username) {
this.username = username;
return this;
}
public String getAge() {
return age;
}
public Student setAge(String age) {
this.age = age;
return this;
}
public String getGender() {
return gender;
}
public Student setGender(String gender) {
this.gender = gender;
return this;
}
public String getAddress() {
return address;
}
public Student setAddress(String address) {
this.address = address;
return this;
}
public String getPhone() {
return phone;
}
public Student setPhone(String phone) {
this.phone = phone;
return this;
}
//成员方法:一个私有,四个公共
private void function() {
System.out.println("function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
public void study() {
System.out.println("好好学习天天向上");
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", age='" + age + '\'' +
", gender='" + gender + '\'' +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
public class Teacher {
public void teach() {
System.out.println("为国育人");
}
}
测试类
public class Demo02Test {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, IOException, InstantiationException, NoSuchMethodException, ClassNotFoundException {
ReflectTest();
}
public static void ReflectTest() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
System.out.println("------------------------------------");
//BufferedReader bufferedReader = new BufferedReader(new FileReader("reflect\\myproperties.properties"));
FileReader fileReader = new FileReader("G:\\javaEE\\javaTEST\\newfeature\\src\\reflect\\myproperties.properties");
Properties properties = new Properties();
properties.load(fileReader);
fileReader.close();
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
Class<?> aClass = Class.forName(className);
Constructor<?> constructor = aClass.getConstructor();
Object o = constructor.newInstance();
Method method = aClass.getMethod(methodName);
method.invoke(o);
System.out.println("---------------------------------");
}
}
properties文件
className=reflect.domain.Teacher
methodName=teach
测试结果
当修改配置文件后,测试结果如下:
className=reflect.domain.Student
methodName=study
案例二
将student实例对象的发射到user实例对象。
实体类
public class User {
private String username;
private String age;
private String gender;
private String address;
private String phone;
public User() {
}
public User(String username, String age,
String gender, String address, String phone) {
this.username = username;
this.age = age;
this.gender = gender;
this.address = address;
this.phone = phone;
}
public String getUsername() {
return username;
}
private User setUsername(String username) {
this.username = username;
return this;
}
public String getAge() {
return age;
}
private User setAge(String age) {
this.age = age;
return this;
}
public String getGender() {
return gender;
}
private User setGender(String gender) {
this.gender = gender;
return this;
}
public String getAddress() {
return address;
}
private User setAddress(String address) {
this.address = address;
return this;
}
public String getPhone() {
return phone;
}
private User setPhone(String phone) {
this.phone = phone;
return this;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age='" + age + '\'' +
", gender='" + gender + '\'' +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
反射工具类
public class MyReflectUtils {
public static <T> T MyReflectMethod(HashMap<String ,String> hashMap, Class<T> cls){
T t = null;
try {
t = cls.newInstance();
Field[] declaredFields = cls.getDeclaredFields();
for (Field field:
declaredFields) {
field.setAccessible(true);
System.out.println(field.toString());
}
Method[] methods = cls.getDeclaredMethods();
for (Method method :
methods) {
method.setAccessible(true);
String methodName = method.getName();
if(methodName.startsWith("set")){
String subName = methodName.substring(3);
String firstchar = subName.substring(0, 1);
firstchar=firstchar.toLowerCase();
String actMethodName= firstchar+subName.substring(1);
String value = hashMap.get(actMethodName);
try {
method.invoke(t,value);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return t;
}
/*public static User MyReflectMethod1(HashMap<String,String> hashMap){
User user = new User();
//获取姓名时一定要精确匹配,不然就运行不出结果
String username =hashMap.get("username ");
String age =hashMap.get("age ");
String gender =hashMap.get("gender ");
String address =hashMap.get("address ");
String phone =hashMap.get("phone ");
user.setAddress(address).setAge(age).
setUsername(username).setGender(gender).setPhone(phone);
return user;
}
public static Student MyReflectMethod2(HashMap<String,String> hashMap){
Student student = new Student();
String username =hashMap.get("username");
String age =hashMap.get("age");
String gender =hashMap.get("gender");
String address =hashMap.get("address");
String phone =hashMap.get("phone");
student.setAddress(address).setAge(age).
setUsername(username).setGender(gender).setPhone(phone);
return student;
}*/
}
测试类
public class Reflect01Demo {
public static void main(String[] args) {
Map map = new HashMap<String,String>();
map.put("username","甄志丙");
map.put("age","18");
map.put("gender","男");
map.put("address","终南山");
map.put("phone","110");
Student student1 = MyReflectUtils.MyReflectMethod((HashMap<String, String>) map, Student.class);
System.out.println(student1);
User user1 = MyReflectUtils.MyReflectMethod((HashMap<String, String>) map, User.class);
System.out.println(user1);
}
}
测试结果
反射的优点
通过上面案例来思考反射为什么说反射是框架的灵魂?
1) 反射使得程序在运行过程中操作目标对象提供了可行的方法,大大提高了程序运行的灵活性。
2)基于反射机制使程序的解耦增强,耦合性降低,程序可扩展性增强。
至此,反射案例复习完毕。