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

一、反射

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

java-反射、BeanUtils、注解 -学习笔记_第1张图片

  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、注解 -学习笔记_第2张图片
//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 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、注解 -学习笔记_第3张图片

你可能感兴趣的:(javaweb)