程序员内功修炼大法【反射、内省】(下)

举报
XiaoLin_Java 发表于 2021/12/08 23:06:27 2021/12/08
【摘要】 五、内省 5.1、JavaBean​ JavaBean 是 Java 中最重要的一个可重用的组件(减少代码重复,可重用,封装业务逻辑,封装数据)。 5.1.1、JavaBean 的规范要求使用 public 修饰。字段私有化。提供 get/set 方法。公共的无参数的构造器(使用反射,使用字节码对象.newInstance去创建对象)。 5.1.2、三大成员事件方法属性 5.1.3、什么是...

五、内省

5.1、JavaBean

​ JavaBean 是 Java 中最重要的一个可重用的组件(减少代码重复,可重用,封装业务逻辑,封装数据)。

5.1.1、JavaBean 的规范要求

  1. 使用 public 修饰。
  2. 字段私有化。
  3. 提供 get/set 方法。
  4. 公共的无参数的构造器(使用反射,使用字节码对象.newInstance去创建对象)。

5.1.2、三大成员

  1. 事件
  2. 方法
  3. 属性

5.1.3、什么是属性

​ JavaBean 可以封装数据,就是将数据保存到一个 bean 对象的属性中的。

​ 属性不是字段,属性是通过get/set方法推导出来的。

​ **规范的get方法/获取方法/读方法:**public修饰、无参数、有返回、get开头。

​ **规范的set方法/设置方法/写方法:**public修饰、有参数、无返回、set开头。

​ 注意:

  1. 只要是标准的get/set方法,就存在属性,不一定非得是通过工具自动生成的规范的写法。

在这里插入图片描述

  1. 字段是 boolean 的,读方法不是 get 开头,而是 is 开头。

在这里插入图片描述

5.2、内省的概述

​ JavaBean是一个非常常用的组件,无外乎就是操作里面的属性。而之前我们要获取JavaBean中的方法,如果使用反射非常麻烦,于是SUN公司专门提供了一套操作 JavaBean 属性的API: 内省Introspector)。

5.3、内省的作用

  1. 获取到属性名和属性类型等相关状态信息。
  2. 获取属性对应的读写方法操作属性的值等操作方式。

5.4、内省常用的API

  1. 通过字节码对象,获取到JavaBean的描述对象,返回 JavaBean的描述对象

    public static BeanInfo getBeanInfo(Class beanClass, Class stopClass);

  2. 通过 JavaBean 描述对象获取属性描述器

    PropertyDescriptor[] getPropertyDescriptors();

  3. 通过属性描述器,获取到属性名、属性类型、读写(getter/setter)方法

    获取属性名:public String getName();

    获取属性类型:public Class<?> getPropertyType();

    获取读方法(getter):public Method getReadMethod();

    获取写方法(setter):public Method getWriteMethod();

​ 通过字节码对象来获取BeanInfo对象的时候,默认会内省当前字节码对象以及其所有的父类的信息。比如:getBeanInfo(A.class),其实它也会内省A的父类,如Object的信息。一般来说,我们不关心父类的属性相关信息,此时可以调用getBeanInfo的重载方法:getBeanInfo(beanClass,stopClass)

在这里插入图片描述

示范:BeanInfo beanInfo = Introspector.getBeanInfo(Person.class,Object.class);

package com.day03.IntrospectorDemo;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

/**
 * @author Xiao_Lin
 * @date 2020/12/29 13:37
 */
public class TestIntrospector {

  public static void main(String[] args) throws Exception {
    //创建对象
    Student student = Student.class.newInstance();
    //把 JavaBean 转成 beanInfo
    BeanInfo beanInfo = Introspector.getBeanInfo(student.getClass(),Object.class);
    //通过通过beanInfo获取所有属性
    PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
    //遍历属性描述器数组,获取到每个属性描述器
    for (PropertyDescriptor pd : propertyDescriptors) {
      //获取属性名
      System.out.println("属性名 = " + pd.getName());
      //获取属性类型
      System.out.println("属性类型 = " + pd.getPropertyType());
      //获取属性的getter/setter方法
      Method getMethod = pd.getReadMethod();
      System.out.println("get方法 = " +getMethod);
      Method setMethod = pd.getWriteMethod();
      System.out.println("set方法 = " +setMethod);
      //调用age属性的set方法
      if ("age".equals(pd.getName())){
        //执行age的set方法,invoke参数含义是给哪个对象赋予哪个值
        setMethod.invoke(student,22);
      }
      //再次执行get方法
      System.out.println(student.getAge());
    }
  }
}

5.5、JavaBean 和 Map 之间的转化

​ map和JavaBean的结构很类似,我们可以将 map 和 JavaBean 相互转换.将key和属性名一 一对应起来

在这里插入图片描述

5.5.1、JavaBean转map

 // Javabean 转 map
  public static Map<String, Object> BeanToMap(Object obj) throws Exception{
    Map<String, Object> map = new HashMap<>();
    //通过内省获得所有属性
    BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass(), Object.class);
    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor pd : pds) {
      //获取属性名作为key
      String key = pd.getName();
      //获取属性的getter方法并且调用
      Object value = pd.getReadMethod().invoke(obj);
      map.put(key, value);
    }
    return map;
  }

public class BeanMap {

  public static void main(String[] args) throws Exception{
    Map<String, Object> map = BeanToMap(new Student("张三", 20));
    map.forEach((k,v)-> System.out.println(k+"->"+v));
  }
}

在这里插入图片描述

5.5.2、map转JavaBean

//map转JaveBean,这里使用泛型
  public static <T> T  MapToBean(Map<String, Object> map ,Class<T> clz) throws Exception{
    //创建JavaBean对象
    T t = clz.newInstance();
    //遍历属性,获取属性名作为mao的key 去获取value值,再设置给setter方法
    //获取所有属性
    BeanInfo beanInfo = Introspector.getBeanInfo(clz, Object.class);
    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor pd : pds) {
      String key = pd.getName();
      Object value = map.get(key);
      pd.getWriteMethod().invoke(t,value);
    }
    return t;
  }

在这里插入图片描述

六、注解

6.1、注解介绍

​ 我们可以使用注解来修饰类中的成员信息,注解其实就是Annotation。

在这里插入图片描述

6.2、定义格式

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface 注解名 {
}

​ 定义格式:@interface 注解名

​ 使用格式:@注解名(属性名=属性值, 属性名=属性值)

​ 注解贴在程序元素上,想要拥有某一些功能,必须有三个角色去参与

  1. 注解本身
  2. 被贴的程序元素
  3. 第三方程序,使用反射给注解赋予功能(在注解的背后,一定有一段代码给注解赋予功能)。

6.3、内置注解

  • @Override 限定覆写父类方法
  • @Deprecated 标记已过时,不推荐使用.在JDK5之前,使用文档注释来标记过时
  • @SuppressWarings 抑制编译器发出的警告
  • @Functionallnterface 标记该接口是一个函数接口(JDK1.8开始出现的)

6.4、元注解

注解:用来贴在类/方法/变量等之上的一个标记,第三方程序可以通过这个标记赋予一定功能。

元注解:在定义注解的时候用来贴在注解上的注解,用来限定注解的用法。他囊括了三个注解

6.4.1、@Target

​ 表示注解可以贴在哪些位置(类,方法上,构造器上等等).位置的常量封装在ElementType枚举类
中。

  • ElementType.ANNOTATION_TYPE只能修饰类。
  • ElementType.CONSTRUCTOR只能修饰构造方法。
  • ElementType.FIELD只能修饰字段(属性),包括枚举常量。
  • ElementType.LOCAL_VARIABLE只能修饰局部变量。
  • ElementType.METHOD只能修饰方法。
  • ElementType.PACKAGE只能修饰包。
  • ElementType.PARAMETER只能修饰参数。
  • ElementType.TYPE只能修饰类,接口,枚举。

6.4.2、@Retention

​ 表示注解可以保存在哪一个时期,表示时期的值,封装在RetentionPolicy枚举类中。

在这里插入图片描述

6.4.3、@Documented

​ 使用@Documented标注的标签会保存到API文档中。

6.4.4、@Inherited

@Inherited标注的标签可以被子类所继承。

标记已过时,不推荐使用.在JDK5之前,使用文档注释来标记过时

  • @SuppressWarings 抑制编译器发出的警告
  • @Functionallnterface 标记该接口是一个函数接口(JDK1.8开始出现的)

6.4、元注解

注解:用来贴在类/方法/变量等之上的一个标记,第三方程序可以通过这个标记赋予一定功能。

元注解:在定义注解的时候用来贴在注解上的注解,用来限定注解的用法。他囊括了三个注解

6.4.1、@Target

​ 表示注解可以贴在哪些位置(类,方法上,构造器上等等).位置的常量封装在ElementType枚举类
中。

  • ElementType.ANNOTATION_TYPE只能修饰类。
  • ElementType.CONSTRUCTOR只能修饰构造方法。
  • ElementType.FIELD只能修饰字段(属性),包括枚举常量。
  • ElementType.LOCAL_VARIABLE只能修饰局部变量。
  • ElementType.METHOD只能修饰方法。
  • ElementType.PACKAGE只能修饰包。
  • ElementType.PARAMETER只能修饰参数。
  • ElementType.TYPE只能修饰类,接口,枚举。

6.4.2、@Retention

​ 表示注解可以保存在哪一个时期,表示时期的值,封装在RetentionPolicy枚举类中。

在这里插入图片描述

6.4.3、@Documented

​ 使用@Documented标注的标签会保存到API文档中。

6.4.4、@Inherited

@Inherited标注的标签可以被子类所继承。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。