欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java-反射、BeanUtils、注解 -学习笔记

程序员文章站 2024-02-16 09:35:22
...

一、反射

  1. 什么是反射
  • 反射:反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的方法,属性,构造方法等成员。
  • 反射:调用构造方法、普通方法、属性的另外一种方式。
    • 最终效果和普通调用方式是一样的
  • 特点:
    • 比普通方式麻烦
    • 比普通方式强大

java-反射、BeanUtils、注解 -学习笔记

  1. 反射的使用

反射操作的统一步骤:

  1. 获取Class对象
  2. 要操作谁,就找到谁:
    1. 要获取Constructor对象: getConstructor() getDeclaredConstructor()
    2. 要获取Method对象:getMethod(), getDeclaredMethod()
    3. 要获取Field对象:getField(), getDeclaredField()
  3. 如果操作的是private,需要提前设置允许暴力反射:setAccessible(true)
  4. 反射操作:
    1. 操作构造方法:newInstance()
    2. 操作普通方法:invoke()
    3. 操作属性字段:get(), set()

2.1 得到Class

  • 得到Class对象的三种方式
    • Class clazz = 类名.class
    • Class clazz = 对象.getClass() 用于不知道别人的类,方法中的参数调用
    • Class clazz = Class.forName(“全限定类名”)
  • 常用方法:
    • clazz.getName():获取全限定类名
    • clazz.getSimpleName():获取简单类名(不包含包名)
    • clazz.newInstance():生成类的实例对象

java-反射、BeanUtils、注解 -学习笔记
//1.得到类的Class对象:类名.class
Class clazz = Person.class;

 //1.得到类的Class对象:对象.getClass()
    Person person = new Person();
    Class clazz = person.getClass();
    
    //1.得到类的Class对象:Class.forName()
    Class clazz = Class.forName("com.itheima._01reflect.Person");
    
    //获取全限定类名
    String name = clazz.getName();
    System.out.println("全限定类名是:" +name);
    //获取简单类名
    String simpleName = clazz.getSimpleName();
    System.out.println("简单类名是:" + simpleName);
    
    //生成类的实例对象
    Object object = clazz.newInstance();
    System.out.println(object);

2.2 得到并操作Constructor(反射) class软件包 java.lang

生成一个类的实例对象,不是通过普通的new方式,而是通过反射

  • 获取Constructor
    • clazz.getConstructor(Class… argTypes):获取的是public类型的构造方法
    • clazz.getDeclaredConstructor(Class… argTypes):获取的是任意类型的构造方法
    • clazz.getConstructors():获取所有public类型的构造方法Constructor数组
    • clazz.getDeclaredConstructors():获取所有任意类型的构造方法Constructor数组
  • 反射调用Constructor
    • constructor.newInstance(Object… argValues):生成类的实例对象
      • 参数argValues:构造方法的实参值
    • constructor.setAccessible(true):设置Constructor允许暴力反射
      • 如果是要操作private类型的Constructor,就需要提前设置允许暴力反射
 /**
         * 反射调用Person的无参构造(public),得到一个Person对象
         */
    @Test
    public void test1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取类的Class对象
        Class<Person> clazz = Person.class;
    
        //获取到构造方法:无参构造,public
        Constructor<Person> constructor = clazz.getConstructor();
    
        //生成类实例对象
        Person person = constructor.newInstance();
    
        System.out.println(person);
    }
    
    /**
         * 反射调用Person的有参构造(private),得到一个Person对象
         */
    @Test
    public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象
        Class<Person> clazz = Person.class;
        //获取到构造方法:有参构造,private
        Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class, Integer.class);
        //暴力反射
        constructor.setAccessible(true);
        //生成类实例对象
        Person person = constructor.newInstance("老王", 45);
    
        System.out.println(person);
    }

2.3 得到并操作Method(反射)

  • 获取到Method
    • clazz.getMethod(String methodName, Class… argTypes):获取public类型的方法
    • clazz.getDeclaredMethod(String methodName, Class… argTypes):获取任意类型的方法
    • clazz.getMethods():获取所有public类型的方法Method数组(自己的和父类的)
    • clazz.getDeclaredMethods():获取所有任意类型的方法Method数组(自己的)
  • 反射调用Method
    • method.invoke(Object target, Object… argValues):反射调用方法
      • 参数:
        • target:调用哪个对象的方法
        • argValues:方法的实参
      • 返回值:方法的返回值
    • method.setAccessible(true):设置方法允许暴力反射
Person person1 = new Person();
//获取方法Method对象
Method method = clazz.getMethod("eat", String.class);
//反射调用方法
Object result = method.invoke(person1, "榴莲");
 /**
         * 反射调用Person的eat方法:public,有参String
         * Person person1 = new Person();
         * Person person2 = new Person();
         *
         * person1.eat("..");
         */
    @Test
    public void test1() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person1 = new Person();
        Person person2 = new Person();
    
    
        //获取Class对象                   ctrl+alt+v
        Class<Person> clazz = Person.class;
    
        //获取方法Method对象
        Method method = clazz.getMethod("eat", String.class);
    
        //反射调用方法
        Object result = method.invoke(person1, "榴莲");
    
        System.out.println("方法执行的返回值:" + result);
    }
    
    /**
         * 反射调用Person的sleep方法,private,有参String
         */
    @Test
    public void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person1 = new Person();
        Person person2 = new Person();
    
        //获取Class对象
        Class<Person> clazz = Person.class;
    
        //获取到方法Method对象
        Method method = clazz.getDeclaredMethod("sleep", String.class);
    
        //设置允许暴力反射
        method.setAccessible(true);
    
        //反射调用method方法
        Object result = method.invoke(person2, "老王");
    
        System.out.println("方法执行的返回值:" + result);
    }

2.4 得到并操作Field(反射)

  • 获取到Field对象
    • clazz.getDeclaredField(String fieldName):获取指定名称的属性(任意类型)
    • clazz.getField(String fieldName):获取指定名称的属性(public类型)
    • clazz.getDeclaredFields():获取所有任意类型的属性,得到Field数组
    • clazz.getFields():获取所有public类型的属性,得到Field数组
  • 反射操作Field对象
    • field.set(Object target, Object value):设置属性值
      • target:设置哪个对象的属性值
      • value:设置的值
    • field.get(Object target):获取属性值
    • field.setAccessible(true):设置允许暴力反射
    • field.getType():获取属性类型
 /*
    * 反射获取所有的属性值
    */
    @Test
    public void test1() throws IllegalAccessException {
        Person person1 = new Person();
        person1.setName("坤坤");
        person1.setAge(18);
    
        Person person2 = new Person();
        person2.setName("凡凡");
        person2.setAge(24);
    
        //获取Class对象
        Class<Person> clazz = Person.class;
        //获取所有的属性Field
        Field[] fields = clazz.getDeclaredFields();
        //循环遍历
        for (Field field : fields) {
            field.setAccessible(true);
            //获取每个属性的值
            Object value = field.get(person1);
            //输出属性名称和属性值                    // name  坤坤    
            String fieldName = field.getName();     // age    18
            System.out.println("属性名:" + fieldName + ", 属性值:" + value);
        }
    }

    
    /**
         * 反射设置Person的所有属性值
         * 给name属性设置值:晗晗
         * 给age属性设置值:26
         */
    @Test
    public void test2() throws NoSuchFieldException, IllegalAccessException {
        Person person = new Person();
    
        //获取Class对象                         注意getClass
        Class<? extends Person> clazz = person.getClass();
        //给name设置属性值:晗晗
        Field nameField = clazz.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(person, "晗晗");
        //给age设置属性值:26
        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(person, 26);
    
        System.out.println(person);  //Person[name='晗晗' , age='26']
    }

1.7.2 编写一个工厂方法可以根据配置文件产任意类型的对象。

例如有配置文件stu.properties,存储在项目的src文件夹下,内容如下:

根据配置文件信息创建一个学生对象

 class=com.itheima._02test1.Student      //stu.properties文件内容
    name=jack
    gender=\u7537
    age=21
    /**
     * 根据配置文件里的信息,生成指定的对象
     */
    public class DemoFactory {
        public static void main(String[] args) throws Exception {
            //1.读取配置文件  流读取文件
            Properties properties = new Properties();
            InputStream inputStream = DemoFactory.class.getClassLoader().getResourceAsStream("stu.properties");
            properties.load(inputStream);
    
            //2.从配置文件里得到类名,生成类的实例对象
            String className = properties.getProperty("class");
            Class<?> clazz = Class.forName(className);
            Object object = clazz.newInstance();
    
            //3.反射设置属性值:可以获取clazz里所有的field,也可以获取配置文件里所有的key
            Set<String> names = properties.stringPropertyNames();
            for (String fieldName : names) {
                if ("class".equals(fieldName)) {  //不获取class属性名
                    continue;
                }
    
                String fieldValue = properties.getProperty(fieldName); //三个属性值
    
                //获取Field对象
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                //设置field的属性值
                Class<?> fieldType = field.getType();  //属性类型 string  string  integer
                if (fieldType == Integer.class) {
                    field.set(object, Integer.parseInt(fieldValue));
                }else{
                    field.set(object, fieldValue);
                }
            }
            //4.把设置好的实例对象,输出显示
            System.out.println(object);
        }
    }
//使用beanUtils省略上面代码 第3步
BeanUtils.setProperty(object, fieldName, fieldValue);
 BeanUtils.setProperty(student, "age", "20");

二、BeanUtils工具包

1. BeanUtils简介 封装上面工厂方法

  • 是Apache提供的一个开源工具,简化了JavaBean的数据封装。
  • 相关概念:
    • 字段:类的成员变量 private String name;是字段
    • 属性:get/set方法,去掉get/set,首字母小写,是属性
      注意:创建JavaBean的时候,要保证字段名和属性一致。使用idea快捷键生成,而不要自己手写
  • 相关的jar包:
  1. 使用BeanUtils
  • BeanUtils.setProperty(Object bean, String fieldName, Object fieldValue):设置属性值 如果指定的属性不存在,则什么也不发生。
  • BeanUtils.getProperty(Object bean, String fieldName):获取属性值
  • BeanUtils.copyProperties(Object dest, Object orig):将对象orig的属性值赋值给对象dest对象对应的属性 注意:只有属性名名相同且类型一致的才会赋值成功。
  • BeanUtils.populate(Object bean, Map<String,Object> map):把map里的数据封装到JavaBean对象里

案例演示

三、注解

1. 什么是注解

1.1 注解简介

  • 注解:Annotation,是和class、inteface同一等级的代码说明。是一种记号,记号本身没有功能,是其它软件、其它代码增加的功能。
  • 注解是JDK1.5的新特性。
    注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息。 标记(注解)可以加在包,类,字段,方法,方法参数以及局部变量上。

1.2 注解作用和JDK里常见的注解

  • 编译检查:
    • @Override:被@Override标记的方法,idea/eclipse会提供功能:检查语法
    • @Deprecated:被@Deprecated标记的方法,idea/eclipse会提供功能:把使用方法的代码划掉
    • @SupressWarning:压制警告,idea/eclipse不会显示警告信息
  • 辅助生成文档:
    • @version:版本
  • 代替xml配置文件:
    • @WebServlet:Tomcat软件提供的,用于配置Servlet,代替web.xml的 现在不能完全代替

2. 自定义注解

2.1 注解的定义语法

@元注解
public @interface 注解名{
    属性类型 属性名称() default 默认值;
}
 int age() default 18; // 年龄
  • 属性适用的数据类型
    • 八种基本数据类型(int,float,boolean,byte,double,char,long,short)
    • String类型,Class类型,枚举类型,注解类型
    • 以上所有类型的一维数组

2.2 元注解

  • 元注解:用来限制自定义注解的注解
  • 常用元注解有2个:
    • @Target:用来限制自定义注解可以用在什么地方。从ElementType里取值的,常用值有:
      • ElementType.TYPE:可以用在类、接口上 type
      • ElementType.METHOD:可以用在方法上 method
      • ElementType.FIELD:可以用在成员变量上 field
        PARAMETER:用在参数上 parameter
        CONSTRUCTOR:用在构造方法上 constructor
        LOCAL_VARIABLE:用在局部变量上 local_variable
    • @Retention:用来限制自定义注解保留到什么阶段(生命周期)。从RetentionPolicy里取值,常用的:
      • RetentionPolicy.SOURCE:保留到源码阶段 source
      • RetentionPolicy.CLASS:保留到字节码阶段 class
      • RetentionPolicy.RUNTIME:保留到运行阶段 runtime
public class Demo02MyAnnotationTest {
  
      public static void main(String[] args) throws NoSuchMethodException {
          //获取Class对象
          Class<Demo02MyAnnotationTest> clazz = Demo02MyAnnotationTest.class;
          //获取method1的方法对象
       Method method = clazz.getMethod("method1", String.class);
          //判断方法上是否有指定的注解 保留在什么阶段
    boolean b = method.isAnnotationPresent(Demo02MyAnnotation.class);
   System.out.println("method1方法上是否有注解Demo02MyAnnotation:" + b);   //true  或false
      }

2.2 属性

  • 注解的属性,并非所有类型都可以。只支持:
    • 8种基本数据类型
    • String类型
    • Class类型
    • 注解类型
    • 枚举类型
    • 以及以上类型的一维数组形式

3. 注解的使用

  • 使用注解的语法:@注解名(属性名=值, 属性名=值, …)
  @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Demo02MyAnnotation {
        String str() 
        int number()      
    }   写法  @Demo02MyAnnotation(str = "hello, world", number = 10)
  • 注意:如果只有一个属性需要设置值,并且属性名称是value,可以简写成:@注解名(value的值)

4. 注解的解析(了解)

  • 给注解增加功能
  • method.isAnnotationPresent(Class annotationClass):判断方法上是否有指定注解返回Boolean类型

使用反射获取注解的数据 案例

模拟Junit测试的@Test 案例

java-反射、BeanUtils、注解 -学习笔记

相关标签: javaweb 反射