理解Java反射机制和内省机制应用与实践

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 反射能做什么?

  1. 获取Class对象
  2. 操作构造器 获取构造器对象
  3. 通过构造器对象 构造对象
  4. 获取成员方法
  5. 调用成员方法
    ······

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());
    }
相关推荐
未若君雅裁1 小时前
synchronized 底层原理:Monitor、对象头、Mark Word 与锁升级
java
尤老师FPGA1 小时前
QT代码自适应窗口
开发语言·qt
biter down1 小时前
5:原生 assert 断言
开发语言
m0_752035631 小时前
markdown语言格式
java
布朗克1681 小时前
12 封装与构造方法
java·开发语言·封装·构造方法
z落落2 小时前
C# 抽象类(abstract)
java·开发语言·c#
折哥的程序人生 · 物流技术专研2 小时前
AI 编程与行业赋能|专栏总目录(持续更新)
开发语言·人工智能·软件工程·ai编程
SilentSamsara2 小时前
爬虫工程化:Playwright + 反反爬 + 数据清洗管道实战
开发语言·爬虫·python·青少年编程·playwright