Java-反射
1. 概念
- 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。反射是java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接
- 在程序运行期间,系统始终为所有对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时的类型信息选择相应的方法执行。保存这些信息的类成为 Class。
- java代码运行的阶段
- source源代码阶段: .java -> .class字节码文件
- Class类对象阶段: .class字节码文件被类加载器加载进内存,并将其封装成class对象(用于在内存中描述字节码文件),class对象将原字节码文件中的成员变量抽取等装成数组Field[],将原字节码文件的构造函数封装成数组Construction[],将成员变量封装成Method[]。当然Class类不止这三个,还封装了很多,常用的是以上这三个。
- RunTime运行阶段,创建对象的过程new
- 加载的时候: Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
- 如图是类的正常加载过程:反射的原理在于class对象。
2. 反射作用
- 反编译 .class -> .java
- 通过反射机制访问Java对象的属性,方法,构造方法等
- 实现通用的数组操作代码
- 利用Method对象,这个对象很像C++的函数指针
3. 反射机制中的类
java.lang.Class;
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;
4. 具体实现
- 反射机制获取类有三种方法,我们来获取Employee类型
//第一种方式: 源代码阶段
Classc1 = Class.forName("Employee");
//第二种方式: class类对象阶段
//java中每个类型都有class 属性.
Classc2 = Employee.class;
//第三种方式:RunTime运行阶段
//java语言中任何一个java对象都有getClass 方法
Employeee = new Employee();
Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)
- 创建对象:获取类以后我们来创建它的对象,利用newInstance
Class c =Class.forName("Employee");
//创建此Class 对象所表示的类的一个新实例
Objecto = c.newInstance(); //调用了Employee的无参数构造方法.
-
获取属性: 分 所有属性和特定属性
获取所有属性
//获取整个类
Class c = Class.forName("java.lang.Integer");
//获取所有的属性?
Field[] fs = c.getDeclaredFields();
//定义可变长的字符串,用来存储属性
StringBuffer sb = new StringBuffer();
//通过追加的方法,将每个属性拼接到此字符串中
//最外边的public定义
sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");
//里边的每一个属性
for(Field field:fs){
sb.append("\t");//空格
sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等
sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字
sb.append(field.getName()+";\n");//属性的名字+回车
}
sb.append("}");
System.out.println(sb);
获取特定方法
public static void main(String[] args) throws Exception{
//以前的方式:
/*
User u = new User();
u.age = 12; //set
System.out.println(u.age); //get
*/
//获取类
Class c = Class.forName("User");
//获取id属性
Field idF = c.getDeclaredField("id");
//实例化这个类赋给o
Object o = c.newInstance();
//打破封装
idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。
//给o对象的id属性赋值"110"
idF.set(o, "110"); //set
//get
System.out.println(idF.get(o));
}
5. 好处
-
对于封装好的框架来说,直接拿来用就可以,不用修改框架内的代码。但如果我们使用的是传统的new形式来实例化,那么当类名更改的时候,我们就要修改java代码,还有重新测试,编译等。如果仅修改配置文件就简单的多。
-
反射还能解耦,假设我们使用的是new这种形式进行对象的实例化,此时如果在项目的某一个小模块中我们的一个实例类丢失了,那么在编译期间就会报错,以导致整个项目无法启动。而对于反射创建对象Class.forName(“全类名”);这种形式,我们在编译期需要的仅仅只是一个字符串(全类名),在编译期不会报错,这样其他的模块就可以正常的运行,而不会因为一个模块的问题导致整个项目崩溃。这就是Spring框架中IOC控制反转的本质。