欢迎光临
我们一直在努力

理解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 对象

反射 APIClass, Method, Field, Constructor

运行时对象实例

1.3 反射能做什么?

  • 获取Class对象
  • 操作构造器 获取构造器对象
  • 通过构造器对象 构造对象
  • 获取成员方法
  • 调用成员方法
    ······
  • 2. 反射核心API详解

    反射的核心API主要包含以下几个类:

    2.1 获取Class类对象

    获取Class对象的三种常用方式:(这里提前创建了一个学生类Student,通过三种方式,分别调用下Class类对象)

    //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;
    }
    }

    //获取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构造器对象

    用于获取构造方法创建对象实例。

    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 通过构造器构造对象

    //通过构造器 构造对象
    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方法

    用于获取和调用类的方法。

    @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 调用成员方法

    @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方法)

    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

    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

    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());
    }

    赞(0)
    未经允许不得转载:171主机测评 » 理解Java反射机制和内省机制应用与实践
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址