1. 反射的基本概念
1.1 什么是反射?
简单理解,类只要运行,就一定会有.class文件,只要获取到这个字节码文件,就可以通过反射机制获取到类中的信息(对象、方法Method、字段Field、构造器Constructor······)
反射API(位于java.lang.reflect包)就是通过操作这个Class对象来获取和操作上述信息的。
1.2 类加载与Class对象关系图
#mermaid-svg-DbDIgsGOAKS7FEH0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DbDIgsGOAKS7FEH0 .error-icon{fill:#552222;}#mermaid-svg-DbDIgsGOAKS7FEH0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DbDIgsGOAKS7FEH0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .marker.cross{stroke:#333333;}#mermaid-svg-DbDIgsGOAKS7FEH0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DbDIgsGOAKS7FEH0 p{margin:0;}#mermaid-svg-DbDIgsGOAKS7FEH0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .cluster-label text{fill:#333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .cluster-label span{color:#333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .cluster-label span p{background-color:transparent;}#mermaid-svg-DbDIgsGOAKS7FEH0 .label text,#mermaid-svg-DbDIgsGOAKS7FEH0 span{fill:#333;color:#333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .node rect,#mermaid-svg-DbDIgsGOAKS7FEH0 .node circle,#mermaid-svg-DbDIgsGOAKS7FEH0 .node ellipse,#mermaid-svg-DbDIgsGOAKS7FEH0 .node polygon,#mermaid-svg-DbDIgsGOAKS7FEH0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DbDIgsGOAKS7FEH0 .rough-node .label text,#mermaid-svg-DbDIgsGOAKS7FEH0 .node .label text,#mermaid-svg-DbDIgsGOAKS7FEH0 .image-shape .label,#mermaid-svg-DbDIgsGOAKS7FEH0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-DbDIgsGOAKS7FEH0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DbDIgsGOAKS7FEH0 .rough-node .label,#mermaid-svg-DbDIgsGOAKS7FEH0 .node .label,#mermaid-svg-DbDIgsGOAKS7FEH0 .image-shape .label,#mermaid-svg-DbDIgsGOAKS7FEH0 .icon-shape .label{text-align:center;}#mermaid-svg-DbDIgsGOAKS7FEH0 .node.clickable{cursor:pointer;}#mermaid-svg-DbDIgsGOAKS7FEH0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .arrowheadPath{fill:#333333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DbDIgsGOAKS7FEH0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DbDIgsGOAKS7FEH0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DbDIgsGOAKS7FEH0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DbDIgsGOAKS7FEH0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DbDIgsGOAKS7FEH0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DbDIgsGOAKS7FEH0 .cluster text{fill:#333;}#mermaid-svg-DbDIgsGOAKS7FEH0 .cluster span{color:#333;}#mermaid-svg-DbDIgsGOAKS7FEH0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DbDIgsGOAKS7FEH0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DbDIgsGOAKS7FEH0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-DbDIgsGOAKS7FEH0 .icon-shape,#mermaid-svg-DbDIgsGOAKS7FEH0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DbDIgsGOAKS7FEH0 .icon-shape p,#mermaid-svg-DbDIgsGOAKS7FEH0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DbDIgsGOAKS7FEH0 .icon-shape .label rect,#mermaid-svg-DbDIgsGOAKS7FEH0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DbDIgsGOAKS7FEH0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DbDIgsGOAKS7FEH0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DbDIgsGOAKS7FEH0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 编译
类加载器加载
提供访问入口
动态操作
源代码 .java 文件
字节码 .class 文件
JVM 方法区
为每个类创建唯一的 Class 对象
反射 API
Class, Method, Field, Constructor
运行时对象实例
1.3 反射能做什么?
- 获取Class对象
- 操作构造器 获取构造器对象
- 通过构造器对象 构造对象
- 获取成员方法
- 调用成员方法
······
2. 反射核心API详解
反射的核心API主要包含以下几个类:
2.1 获取Class类对象
获取Class对象的三种常用方式:(这里提前创建了一个学生类Student,通过三种方式,分别调用下Class类对象)
java
//Student实体类
package cn.test.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private String name;
private int age;
private String sex;
public static void show(){
System.out.println("static方法");
}
public static int show(int a){
System.out.println("static方法-有返回值");
return ++a;
}
}
java
//获取Class对象的三种方式
public void getClassObject() throws ClassNotFoundException {
//三种方式获取到的Class对象是同一个对象
//Class.forName
Class cl1 = Class.forName("cn.test.domain.Student");
//.getClass()
Student s = new Student();
Class cl2 = s.getClass();
//.class
Class cl3 = Student.class;
}
2.2 获取Constructor构造器对象
用于获取构造方法创建对象实例。
java
public void getConstructor() throws Exception {
Class cl = Student.class;
//获取全部构造器
Constructor[] cons = cl.getConstructors();
//System.out.println(Arrays.toString(cons));
//获取全部构造器并忽略访问权限
Constructor[] cons1 = cl.getDeclaredConstructors();
System.out.println(Arrays.toString(cons1));
//获取传参为String类型的构造器
Constructor con1 = cl.getDeclaredConstructor(String.class);
//获取无参构造器
Constructor con3 = cl.getConstructor();
//传参Class... 可变参数 入参个数:0-无穷多个
Constructor con2 = cl.getConstructor(String.class, int.class,String.class);
}
2.3 通过构造器构造对象
java
//通过构造器 构造对象
public void createConstructor() throws Exception{
//通过泛型,不需要强转
Class<Student> cl = Student.class;
Constructor<Student> con2 = cl.getConstructor(String.class, int.class,String.class);
//newInstance()构造对象方法
Student o = con2.newInstance("aa", 12, "s");
//System.out.println(o.toString());
}
//通过无参构造器 构造对象
@Test
public void createConstructorNoArgs() throws Exception{
Class cl = Student.class;
Constructor con2 = cl.getConstructor();
//没有定义泛型,进行强转
Student o = (Student) con2.newInstance();
//通过默认的无参构造器直接构造对象
Student o1 = (Student)cl.newInstance();
System.out.println(o.toString());
}
2.4 获取Method方法
用于获取和调用类的方法。
java
@Test
public void getMethod() throws Exception{
Class cl = Class.forName("cn.test.domain.Student");
//获取所有public修饰的方法,包括父类的
Method[] methods = cl.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("----------------------");
//获取所有方法,包括private修饰的,不包括父类的
Method[] methods1 = cl.getDeclaredMethods();
System.out.println(Arrays.toString(methods1));
//获取指定方法 public修饰
Method m1 = cl.getMethod("getName");
Method m2 = cl.getMethod("setName",String.class);
//获取指定方法 父类继承的
Method m3 = cl.getMethod("wait");
//获取指定方法 private修饰
Method getSex = cl.getDeclaredMethod("getSex");
Method setSex = cl.getDeclaredMethod("setSex",String.class);
}
2.5 调用成员方法
java
@Test
public void methodUse() throws Exception{
Class cl = Class.forName("cn.test.domain.Student");
Method show = cl.getMethod("show");
/*
*调用方法的方法:
*Object invoke (Object obj,Object... args);
*开头的Object 应用为调用原始方式之后的返回值
*参数中的Object obj 表示方法应用在哪个对象上
*如果是 static 修饰的方法 这个参数 可以 传递
null 值 也可以传递对象
*第二个参数:Object... args : 调用方法的时候传递的实际
参数
*/
Object invoke = show.invoke(null);
//无返回值 显示为null
System.out.println(invoke);
System.out.println("-----------");
Method show1 = cl.getMethod("show", int.class);
Object invoke1 = show1.invoke(null, 1);
System.out.println(invoke1);
}
//调用private修饰的方法,需要将字段权限开启,才可以进行赋值操作
@Test
public void getField() throws Exception{
Class cl = Class.forName("cn.test.domain.Student");
Field name = cl.getDeclaredField("name");
//name是private修饰的,将字段的权限调整为true
name.setAccessible(true);
Student s = new Student();
//相当于对象.字段 = 值;
name.set(s,"zz");
System.out.println(s);
}
字段一般不使用反射操作,如果操作成员字段一般使用内省机制,核心类为Introspector类
3.Java中的内省机制
3.1 内省机制概念
内省机制是Java语言对Bean类属性、事件的一种缺省处理方法,可以理解为是对操作属性的详细描述,核心类是Introspector类
3.2 内省机制常用API
3.2.1 范围查询Bean
3.2.2 获取属性详细描述(通过BeanInfo)
3.2.3 获取属性描述器
3.2.4 通过对象描述器获取(属性名、属性类型、get方法、set方法)
java
public void testIntro() throws Exception {
//查询Student类
BeanInfo beanInfo = Introspector.getBeanInfo(Student.class, Object.class);
//获取属性描述器
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Student stu = Student.class.newInstance();
for (PropertyDescriptor pd : pds) {
//通过对象描述器获取(属性名、属性类型、get方法、set方法)
String name = pd.getName();
System.out.println("属性名::"+name);
System.out.println("属性数据类型::"+pd.getPropertyType());
System.out.println("属性get方法::"+pd.getReadMethod());
System.out.println("属性set方法::"+pd.getWriteMethod());
//如果属性为name,进行赋值
if (name.equals("name")) {
stu.setName("pl");
}
}
System.out.println(stu);
}
3.3 JavaBean与Map相互转换
两种类型可以相互转换,都是K-V形式存储
3.3.1 JavaBean转为Map
java
public void javaBeanToMap() throws Exception {
//先实例化对象,并进行赋值,可以使用全参构造器直接传参,或是无参构造器后赋值
Student stu = new Student("aa",20,"1");
// stu.setName("aa");
// stu.setAge(20);
// stu.setSex("1");
//用来存放转换后的数据
Map<String,Object> map = new HashMap<>();
//获取到Student类
BeanInfo beanInfo = Introspector.getBeanInfo(Student.class, Object.class);
//获取类属性的详细描述
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
//通过描述器对象获取
for (PropertyDescriptor pd : pds) {
//没获取到一个属性名和值,就向map存储一次
String name = pd.getName();
Object value = pd.getReadMethod().invoke(stu);
map.put(name,value);
}
System.out.println(map);
}
3.3.2 Map转为JavaBean
java
public void map2Bean() throws Exception {
Map<String,Object> map = new HashMap<>(){
{
put("name","aa");
put("age",20);
put("sex","1");
}
};
Student stu = new Student();
BeanInfo beanInfo = Introspector.getBeanInfo(Student.class, Object.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
//使用map中key的值获取到对应的value,并用set方法写入
pd.getWriteMethod().invoke(stu,map.get(pd.getName()));
}
System.out.println(stu.toString());
}