14Junit、反射、注解

14.1.1 Junit的概述
      一般IDE都内置了junit,若需要自行下载jar包,可以访问官网,官网地址如下:http://www.junit.org

1. 特点
   - 方法命名规则:以test开头,使用驼峰命名法。
   - 方法声明上:必须使用注解:@Test,必须使用public修饰符,没有返回值,方法没有参数。
  
2. 运行测试方法
   - 选中方法名:右键 --> Run 测试方法名,则运行选中的测试方法
     比如测试方法名为testSum,则右键 --> Run testSum
   - 选中类名:右键 --> Run 类名,则运行该类的所有测试方法
     比如类名为TestCalculte,则右键 --> Run TestCalculte
   - 选中模块名或项目名:右键 --> Run 'All Tests',则运行整个模块中所有类的所有测试方法。
3. 查看测试结果
   - 绿色:表示测试通过,
   - 红色:表示失败或出现错误,

14.1.2 常用注解

- @Before:在每个测试方法之前都会运行一次
- @After:在每个测试方法运行以后运行的方法
- @BeforeClass:在所有的测试方法运行之前,只运行一次,而且必须用在静态方法上面。
- @AfterClass:所有的测试方法运行以后,只运行一次,必须用在静态方法上面。

- 业务类

   /**
    业务类
    */
   public class Calculate {
       /*
          求a和b之和
        */
       public int sum(int a,int b){
           return a + b;
      }
 
       /**
        求a和b之差
        */
       public int sub(int a,int b){
           return a - b;
      }
  }

- 测试类

   import org.junit.*;
 
   public class Test2 {
 
       @BeforeClass
       public static void testBeforeClass() {
           System.out.println("类加载时, 只运行一次.");
      }
 
       @Before
       public void testBefore() {
           System.out.println("测试方法运行前被执行 ...");
      }
 
       @Test
       public void testSum() {
           Calculator calculator = new Calculator();
           int sum = calculator.sum(10, 20);
           System.out.println("sum = " + sum);
      }
 
       @Test
       public void testSub() {
           Calculator calculator = new Calculator();
           int sub = calculator.sub(100, 20);
           System.out.println("sub = " + sub);
      }
 
       @After
       public void testAfter() {
           System.out.println("每个测试方法被执行后执行 ...");
      }
 
       @AfterClass
       public static void testAfterClass() {
           System.out.println("类结束前, 只执行一次.");
      }
  }
 
   输出结果 :
   类加载时, 只运行一次.
   测试方法运行前被执行 ...
   sub = 80
   每个测试方法被执行后执行 ...
   测试方法运行前被执行 ...
   sum = 30
   每个测试方法被执行后执行 ...
   类结束前, 只执行一次.

14.2 反射

14.2.1 反射的基本概念

2.1 什么是反射

反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的方法,属性,构造方法等成员。

2.2 使用反射机制解剖类的前提

必须先要获取到该类的字节码文件对象,即Class类型对象。关于Class描述字节码文件如下图所示:

tips:

1)Java中使用Class类表示某个class文件.

2)任何一个class文件都是Class这个类的一个实例对象.

2.3 获取Class对象的三种方式

- 创建测试类:Student

   public class Student {
       // 属性
     
       // 行为
  }



2.2.1 方式1:通过类名.class获取

       @Test
       public void test1() {
           // 方式一 : 通过类名获取 class 对象
           // 格式 : 类名.class 属性
           // 常用场景 : 反射获取方法时, 确定方法的形参列表类型
           Class<Student> cls = Student.class;
           System.out.println("cls = " + cls);
      }
 
   输出结果 :
   cls = class cn.itcast.test2.Student



2.2.2  方式2:通过Object类的成员方法getClass()方法获取

       @Test
       public void test2() {
 
           // 1. 创建一个 Student 类型的对象
           Student stu = new Student();
 
           // 2. 调用方法, 并传入 stu 对象
           showInfo(stu);
      }
 
       public void showInfo(Object obj) {
 
           // 方式二 : 使用对象名调用 getClass() 方法.
           // 格式 : 对象名.getClass() 方法.
           // 使用场景 : 在方法内部, 确定传入形参的真实类型.
 
           Class<?> cls = obj.getClass();
           System.out.println("cls = " + cls);
      }
 
   输出结果 :
   cls = class cn.itcast.test2.Student



2.2.3  方式3:通过Class.forName("全限定类名")方法获取

       @Test
       public void test3() throws ClassNotFoundException {
 
           // 方式三 : 使用 Class 调用静态方法 forName(全限定类名);   `包名+类名`
           // 使用场景 : 加载外部的配置文件时使用
 
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
           System.out.println("cls = " + cls);
      }
 
   输出结果 :
   cls = class cn.itcast.test2.Student

2.3 获取Class对象的信息

知道怎么获取Class对象之后,接下来就介绍几个Class类中常用的方法了。

2.3.1  Class对象相关方法

String getSimpleName(); 获得简单类名,只是类名,没有包   
    String getName(); 获取完整类名,包含包名+类名   
    T newInstance() ;创建此 Class 对象所表示的类的一个新实例。要求:类必须有public的无参数构造方法
public class TestDate {
 
       @Test
       public void testDate1() throws Exception {
 
           // 1. 获取 Date 表示的 Class 对象.
           Class<?> cls = Class.forName("java.util.Date");
 
           // 2. 获取简单类名
           String simpleName = cls.getSimpleName();
           System.out.println("simpleName = " + simpleName);
 
           // 3. 获取完成名称 (包名 + 类名)
           String name = cls.getName();
           System.out.println("name = " + name);
 
           // 3. 创建一个日期对象
           // cls.newInstance(); 已过时.
           Object obj = cls.getDeclaredConstructor().newInstance();
           System.out.println("obj = " + obj);
      }
  }
 
   输出结果 :
   simpleName = Date
   name = java.util.Date
   obj = Sun Jul 15 15:39:01 CST 2018

2.4 获取Class对象的Constructor信息

一开始在阐述反射概念的时候,我们说到利用反射可以在程序运行过程中对类进行解剖并操作里面的成员。而一般常操作的成员有构造方法,成员方法,成员变量等等,那么接下来就来看看怎么利用反射来操作这些成员以及操作这些成员能干什么,先来看看怎么操作构造方法。而要通过反射操作类的构造方法,我们需要先知道一个Constructor类。

2.4.1 Constructor类概述

Constructor是构造方法类,类中的每一个构造方法都是Constructor的对象,通过Constructor对象可以实例化对象。

2.4.2 Class类中与Constructor相关方法

1. Constructor getConstructor(Class... parameterTypes)
        根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
        如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
       
    2. Constructor getDeclaredConstructor(Class... parameterTypes)
          根据参数类型获取构造方法对象,包括private修饰的构造方法。
          如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
      
    3. Constructor[] getConstructors()
           获取所有的public修饰的构造方法
      
    4. Constructor[] getDeclaredConstructors()
           获取所有构造方法,包括privat修饰的

2.4.3 Constructor类中常用方法

1. T newInstance(Object... initargs)
          根据指定参数创建对象。
    2. void setAccessible(true)
          暴力反射,设置为可以直接访问私有类型的构造方法。

2.5 获取Class对象的Method信息

操作完构造方法之后,就来看看反射怎么操作成员方法了。同样的在操作成员方法之前我们需要学习一个类:Method类。

2.5.1 Method类概述

Method是方法类,类中的每一个方法都是Method的对象,通过Method对象可以调用方法。

2.5.2 Class类中与Method相关方法

1. Method getMethod("方法名", 方法的参数类型... 类型) 
        根据方法名和参数类型获得一个方法对象,只能是获取public修饰的
       
    2. Method getDeclaredMethod("方法名", 方法的参数类型... 类型)
          根据方法名和参数类型获得一个方法对象,包括private修饰的
         
    3. Method[] getMethods() (了解)
          获取所有的public修饰的成员方法,包括父类中。
   
    4. Method[] getDeclaredMethods() (了解)
          获取当前类中所有的方法,包含私有的,不包括父类中。

2.5.3 Method类中常用方法

1.  Object invoke(Object obj, Object... args)
          根据参数args调用对象obj的该成员方法   
          如果obj=null,则表示该方法是静态方法
     
    2.  void setAccessible(boolean flag)
          暴力反射,设置为可以直接调用私有修饰的成员方法

2.5.4 示例代码

测试一 :

       @Test
       public void testMethod1() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getMethod 方法
           Method eat = cls.getMethod("eat", String.class);
 
           // 3. 调用 invoke 方法
           Object obj = cls.getDeclaredConstructor().newInstance();
           eat.invoke(obj, "牛肉");
      }
 
   输出结果 :
   正在吃牛肉



测试二 :

       @Test
       public void testDeclaredMethod2() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 declaredMethod 方法
           Method sleep = cls.getDeclaredMethod("fallInLove");
 
           // 3. 暴力反射 (设置可访问权限)
           sleep.setAccessible(true);
 
           // 4. 调用 invoke 执行
           Object obj = cls.getDeclaredConstructor().newInstance();
           sleep.invoke(obj);
      }
 
   输出结果 :
   正在谈恋爱 ...



测试三 :

       @Test
       public void testStaticMethod3() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getMethod 方法
           Method study = cls.getMethod("study");
 
           // 3. 调用 invoke 方法
           study.invoke(null);
      }
 
   输出结果 :
   正在学习中 ...



测试四 :

       @Test
       public void tesMethods4() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getMethods 方法
           Method[] methods = cls.getMethods();
 
           // 3. 遍历 methods 数组
           for (Method method : methods) {
               System.out.println(method);
          }
      }

测试五 :

       @Test
       public void tesDelcaredMethods5() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getDeclaredMethods 方法
           Method[] methods = cls.getDeclaredMethods();
 
           // 3. 遍历 methods 数组
           for (Method method : methods) {
               System.out.println(method);
          }
      }

2.6 获取Class对象的Field信息(了解)

2.6.1 Field类概述

Field是属性类,类中的每一个属性(成员变量)都是Field的对象,通过Field对象可以给对应的成员变量赋值和取值。

2.6.2 Class类中与Field相关方法

1. Field getDeclaredField(String name)
       根据属性名获得属性对象,包括private修饰的
    
    2. Field getField(String name)
       根据属性名获得属性对象,只能获取public修饰的
      
    3. Field[]    getFields()
        获取所有的public修饰的属性对象,返回数组。
       
    4. Field[]    getDeclaredFields()
          获取所有的属性对象,包括private修饰的,返回数组。

2.6.3 Field类中常用方法

void set(Object obj, Object value)
    Object get(Object obj) 
   
    void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。
    Class getType(); 获取属性的类型,返回Class对象。

2.6.4 示例代码

测试一 :

       @Test
       public void testField1() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getField 方法
           Field description = cls.getField("description");
 
           // 3. 设置属性
           Object obj = cls.getDeclaredConstructor().newInstance();
           description.set(obj, "这就是那个神奇的学生.");
 
           // 4. 获取属性
           Object desc = description.get(obj);
           System.out.println("desc = " + desc);
      }
 
   输出结果 :
   desc = 这就是那个神奇的学生.



测试二 :

       @Test
       public void testDeclaredField2() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getDeclaredField 方法
           Field name = cls.getDeclaredField("name");
 
           // 3. 暴力反射
           name.setAccessible(true);
 
           // 4. 设置属性
           Object obj = cls.getDeclaredConstructor().newInstance();
           name.set(obj, "111");
 
           // 5. 查看
           System.out.println(obj);
      }
 
   输出结果 :
   Student{name='111', age=0, gender= }



测试三 :

       @Test
       public void testFields3() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getFields 方法
           Field[] fields = cls.getFields();
 
           // 3. 遍历 fields 数组
           for (Field field : fields) {
               System.out.println(field);
          }
      }
 
   输出结果 :
   public java.lang.String cn.itcast.test2.Student.description



测试四 :

       @Test
       public void testDeclaredFields4() throws Exception {
 
           // 1. 获取 Student 类表示的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.test2.Student");
 
           // 2. 调用 getDeclaredFields 方法
           Field[] fields = cls.getDeclaredFields();
 
           // 3. 遍历 fields 数组
           for (Field field : fields) {
               System.out.println(field);
          }
      }

2.7 反射案例

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

- 例如有配置文件stu.properties,存储在当前项目下,内容如下:

   className=cn.itcast.reflect.Student
   name=rose
   age=18
   gender=女

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



Student 类 :

   public class Student {
       // 属性
       private String name;
       private int age;
       private char gender;
 
       // 公开构造方法 :
       public Student(String name, int age, char gender) {
           this.name = name;
           this.age = age;
           this.gender = gender;
      }
 
       // 公开无参构造方法
       public Student() {
      }
 
       @Override
       public String toString() {
           return "Student{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   ", gender=" + gender +
                   '}';
      }
 
       public String getName() {
           return name;
      }
 
       public void setName(String name) {
           this.name = name;
      }
 
       public int getAge() {
           return age;
      }
 
       public void setAge(int age) {
           this.age = age;
      }
 
       public char getGender() {
           return gender;
      }
 
       public void setGender(char gender) {
           this.gender = gender;
      }
  }

CreateObject 类 :

   import java.io.FileReader;
   import java.io.IOException;
   import java.lang.reflect.Field;
   import java.util.Properties;
   import java.util.Set;
 
   public class CreateObject {
 
       // 属性
       private static Properties prop;
 
       // 静态方法 : 加载配置文件
       static {
           // 初始化 :
           prop = new Properties();
 
           try {
               prop.load(new FileReader("stu.properties"));
          } catch (IOException e) {
 
               // e.printStackTrace();
               throw new RuntimeException("配置文件加载失败!");
          }
      }
 
       // 方法 : 根据配置文件, 创建对象
       public static Object createObject() throws Exception {
 
           // 1. 获取 class 名称
           String className = prop.getProperty("className");
 
           // 2. 获取 class 对象
           Class<?> cls = Class.forName(className);
 
           // 3. 创建一个 cls 表示的对象
           Object obj = cls.getDeclaredConstructor().newInstance();
 
           // 4. 获取属性集对象的所有 `键集`
           Set<String> keys = prop.stringPropertyNames();
 
           // 5. 遍历
           for (String key : keys) {
 
               // 判断 :
               if ("class".equals(key)) continue;
 
               // 6. 根据 key 获取对应的 value
               String value = prop.getProperty(key);
 
               // 7. 获取所有的 fields 数组
               Field field = cls.getDeclaredField(key);
 
               // 8. 设置访问权限
               field.setAccessible(true);
 
               // 9. 获取属性的类型
               Class<?> type = field.getType();
 
               // 10. 判断类型
               if (type == int.class) {
                   int v = Integer.parseInt(value);
                   // 设置属性
                   field.set(obj, v);
              } else if (type == char.class) {
                   char c = value.charAt(0);
                   // 设置属性
                   field.set(obj, c);
              } else {
                   field.set(obj, value);
              }
          }
 
           // 11. 返回对象
           return obj;
      }
  }

测试类 :

   public class Test {
       public static void main(String[] args) throws Exception {
 
           Object obj = CreateObject.createObject();
           System.out.println("obj = " + obj);
      }
  }
 
   输出结果 :
   obj = Student{name='rose', age=18, gender=女}

14.3 注解

3.1 注解的概述

3.1.1 注解的概念

- 注解是JDK1.5的特性。

- 注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息。
- 标记(注解)可以加在包,类,字段,方法,方法参数以及局部变量上。
- 注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。
  注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。

3.1.2 注解的作用

注解的作用就是给程序带入参数。

以下几个常用操作中都使用到了注解:

1. 生成帮助文档:@author和@version
   - @author:用来标识作者姓名。
   - @version:用于标识对象的版本号,适用范围:文件、类、方法。
     - 使用@author和@version注解就是告诉Javadoc工具在生成帮助文档时把作者姓名和版本号也标记在文档中。如下图:
      
2. 编译检查:@Override
   - @Override:用来修饰方法声明。
     - 用来告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败。如下图
    
3. 框架的配置(框架=代码+配置)
   - 具体使用请关注框架课程的内容的学习。

3.1.3 常见注解

1. @author:用来标识作者名。
2. @version:用于标识对象的版本号。
3. @Override :用来修饰方法声明,告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败。
4. @Deprecated: 用来表示不赞成使用.

3.2 自定义注解

3.2.1 定义格式

public @interface 注解名 {
 
  }
 
   如:定义一个名为 Student 的注解
   public @interface Student {
 
  }

- 以上定义出来的注解就是一个最简单的注解了,但这样的注解意义不大,因为注解中没有任何内容,就好像我们定义一个类而这个类中没有任何成员变量和方法一样,这样的类意义也是不大的,所以在定义注解时会在里面添加一些成员来让注解功能更加强大,这些成员就是属性。接下来就看看怎么给注解添加属性。

3.2.2 注解的属性

1. 属性的作用
   - 可以让用户在使用注解时传递参数,让注解的功能更加强大。
2. 属性的格式
   - 格式1:数据类型 属性名();
   - 格式2:数据类型 属性名() default 默认值;
3. 属性定义示例
      // 该注解拥有三个属性 (name, age, gender)
      public @interface Student {
     
          String name();
     
          int age() default 18;
     
          char gender() default '男';
      }
4. 属性适用的数据类型
   - 八种基本数据类型(byte, short, int, long, float, double, char, boolean)
   - String类型,Class类型,枚举类型,注解类型
   - 以上所有类型的一维数组

3.3 使用自定义注解

3.3.1 定义注解

1. 定义一个注解:Book
   - 包含属性:String value()   书名
   - 包含属性:double price()  价格,默认值为 100
   - 包含属性:String[] authors() 多位作者   
   说明 : 当注解中只有一个属性且名称是value,在使用注解时给value属性赋值可以直接给属性值,无论value是单值元素还是数组类型。
  
2. 代码实现
 public @interface Book {
           // 书名
           String value();
           // 价格
           int price() default 100;
           // 多位作者
           String[] authors();
  }
3.3.2 使用注解

1. 定义类在成员方法上使用Book注解

public class BookShelf {
 
       @Book(value = "西游记", price=998, authors = {"吴承恩", "白求恩"})
       public void show() {
         
      }
  }

使用注意事项

- 如果属性有默认值,则使用注解的时候,这个属性可以不用赋值。

- 如果属性没有默认值,那么在使用注解时一定要给属性赋值。

3.4 注解之元注解

3.4.1 元注解的概述

- Java API 提供的注解
- 专门用来定义注解的注解。
- 任何 Java 官方提供的非元注解的定义中都使用到了元注解。
3.4.2 常用元注解

- @Target 注释的使用位置.
- @Retention 注解的声明周期.
3.4.2.1 元注解之@Target

- 作用:指明此注解用在哪个位置,如果不写默认是任何地方都可以使用。
  - 可选的参数值在枚举类ElemenetType中包括:
         TYPE: 用在类,接口上
         FIELD:用在成员变量上
         METHOD: 用在方法上
         PARAMETER:用在参数上
         CONSTRUCTOR:用在构造方法上
         LOCAL_VARIABLE:用在局部变量上
3.4.2.2 元注解之@Retention

- 作用:定义该注解的生命周期(有效范围)。
  - 可选的参数值在枚举类型RetentionPolicy中包括
        SOURCE:注解只存在于Java源代码中,编译生成的字节码文件中就不存在了。
        CLASS:注解存在于Java源代码、编译以后的字节码文件中,运行的时候内存中没有,默认值。
        RUNTIME:注解存在于Java源代码中、编译以后的字节码文件中、运行时内存中,程序可以通过反射获取该注解。

3.4.3 元注解使用示例

// (书名, 价格, 作者)
 
   import java.lang.annotation.ElementType;
   import java.lang.annotation.Retention;
   import java.lang.annotation.RetentionPolicy;
   import java.lang.annotation.Target;
 
   // 元注解 : Target 目标 (注解使用的位置)
   @Target({ElementType.METHOD, ElementType.TYPE})
 
   // 元注解 : Retention 保留策略 (SOURCE, CLASS, RUNTIME)
   @Retention(RetentionPolicy.RUNTIME)
   public @interface Book {
 
       String value();     // 说明 : 如果注解只有一个属性, 最好取名为 value, 因为书写时, 可以省略.
 
       int price() default 100;
 
       String[] authors();  // 多位作者
  }



BookShelf 类

   @Book(value = "西游记", price=998, authors = {"吴承恩", "xxx"})
   public class BookShelf {
 
       // 属性
       // @Book(value = "西游记", price=998, authors = {"吴承恩", "xxx"})
       private int id;
 
       @Book(value = "西游记", price=998, authors = {"吴承恩", "xxx"})
       public void show() {
           String value = "";
           int price = 0;
           String[] authors = {};
           System.out.println("书名为 : " + value);
           System.out.println("价格为 : " + price);
           System.out.println("作者为 : " + Arrays.toString(authors));
      }
  }


3.5 注解解析

3.5.1 什么是注解解析

- 通过Java技术获取注解数据的过程则称为注解解析。

3.5.2 与注解解析相关的接口

- Anontation:所有注解类型的公共接口,类似所有类的父类是Object。
- AnnotatedElement:定义了与注解解析相关的方法,常用方法:
      boolean isAnnotationPresent(Class annotationClass); 判断当前对象是否有指定的注解,有则返回true,否则返回false。
      T getAnnotation(Class<T> annotationClass);  获得当前对象上指定的注解对象。

3.5.3 获取注解数据的原理

- 注解作用在那个成员上,就通过反射获得该成员的对象(Filed)来得到它的注解。

- 如注解作用在方法上,就通过方法(Method)对象得到它的注解*

- 如注解作用在类上,就通过Class对象得到它的注解

3.5.4 使用反射获取注解的数据

3.5.4.1 需求说明

1. 定义注解Book,要求如下:
   - 包含属性:String value()   书名
   - 包含属性:double price()  价格,默认值为 100
   - 包含属性:String[] authors() 多位作者 
   - 限制注解使用的位置:类和成员方法上
   - 指定注解的有效范围:RUNTIME
2. 定义BookStore类,在类和成员方法上使用Book注解
3. 定义TestAnnotation测试类获取Book注解上的数据

3.5.4.2 代码实现

1.注解Book
   import java.lang.annotation.ElementType;
   import java.lang.annotation.Retention;
   import java.lang.annotation.RetentionPolicy;
   import java.lang.annotation.Target;
 
   @Target({ElementType.TYPE, ElementType.METHOD})
   @Retention(RetentionPolicy.RUNTIME)
   public @interface Book {
       // 书名
       String value();
       // 价格
       int price() default 100;
       // 作者 (多位作者)
       String[] authors();
  }

2.BookShelf 类

   import java.lang.reflect.Method;
   import java.util.Arrays;
 
   @Book(value = "西游记", price=998, authors = {"吴承恩", "白求恩"})
   public class BookShelf {
 
       // 属性
       // @Book(value = "西游记", price=998, authors = {"吴承恩", "白求恩"})
       private int id;
 
       @Book(value = "西游记", price=998, authors = {"吴承恩", "白求恩"})
       public void show() {
           // 定义变量
           String value = "";
           int price = 0;
           String[] authors = {};
         
           // 获取当前类的 Class 对象
           Class<? extends BookShelf> cls = this.getClass();
           try {
               // 获取当前方法对象
               Method show = cls.getMethod("show");
               // 判断当前方法上是否有注解信息
               if (show.isAnnotationPresent(Book.class)) {
                   // 条件成立, 获取到当前注解对象
                   Book book = show.getAnnotation(Book.class);
                   // 取出信息, 并实现赋值
                   value = book.value();
                   price = book.price();
                   authors = book.authors();
              }
          } catch (NoSuchMethodException e) {
               e.printStackTrace();
          }
 
           // 输出查看
           System.out.println("书名为 : " + value);
           System.out.println("价格为 : " + price);
           System.out.println("作者为 : " + Arrays.toString(authors));
      }
  }

3.TestBookShelf 类

   public class TestBookShelf {
       public static void main(String[] args) {
 
           BookShelf bookShelf = new BookShelf();
           bookShelf.show();
      }
  }
 
   输出结果 :
   书名为 : 西游记
   价格为 : 998
   作者为 : [吴承恩, 白求恩]
 
   书名为 :
   价格为 : 0
   作者为 : []



补充 : 解析类上的注解

   import java.util.Arrays;
 
   public class TestBookShelf2 {
       public static void main(String[] args) throws ClassNotFoundException {
 
           // 1. 获取类
           Class<?> cls = Class.forName("cn.itcast.annotation.BookShelf");
           // 2. 判断该类上是否有 Book 注解信息
           if (cls.isAnnotationPresent(Book.class)) {
               // 3. 获取 Book 注解对象
               Book book = cls.getAnnotation(Book.class);
               // 4. 取出注解信息
               String value = book.value();
               int price = book.price();
               String[] authors = book.authors();
               // 5. 输出查看
               System.out.println("value = " + value);
               System.out.println("price = " + price);
               System.out.println("authors = " + Arrays.toString(authors));
          }
      }
  }
 
   输出结果 :
   value = 西游记
   price = 998
   authors = [吴承恩, 白求恩]

3.6 注解案例

3.5.1 案例说明

模拟Junit测试的@Test
3.5.2 案例分析

1. 模拟Junit测试的注释@Test,首先需要编写自定义注解@MyTest,并添加元注解,保证自定义注解只能修饰方法,且在运行时可以获得。
2. 然后编写目标类(测试类),然后给目标方法(测试方法)使用 @MyTest注解,编写三个方法,其中两个加上@MyTest注解。
3. 最后编写调用类,使用main方法调用目标类,模拟Junit的运行,只要有@MyTest注释的方法都会运行。
3.5.3 案例代码

1. 注解MyTest

   import java.lang.annotation.ElementType;
   import java.lang.annotation.Retention;
   import java.lang.annotation.RetentionPolicy;
   import java.lang.annotation.Target;
 
   @Target(ElementType.METHOD)
   @Retention(RetentionPolicy.RUNTIME)
   public @interface MyTest {
  }

1. 目标类MyTestClass

   public class MyTestClass {
 
       @MyTest
       public void test01() {
           System.out.println("测试方法一被执行 ...");
      }
 
       public void test02() {
           System.out.println("测试方法二被执行 ...");
      }
 
       @MyTest
       public void test03() {
           System.out.println("测试方法三被执行 ...");
      }
  }

1. 调用类ParseAnnotation

   import java.lang.reflect.Method;
 
   // 解析注解类 :
   public class ParseAnnotation {
       public static void main(String[] args) throws Exception {
 
           // 1. 获取 MyTestClass 的 Class 对象
           Class<?> cls = Class.forName("cn.itcast.practice.MyTestClass");
           Object obj = cls.getDeclaredConstructor().newInstance();
 
           // 2. 调用 getMethods 获取所有方法
           Method[] methods = cls.getMethods();
 
           // 3. 遍历 methods 数组
           for (Method method : methods) {
 
               // 4. 判断当前方法上是否存在 MyTest 注解
               if (method.isAnnotationPresent(MyTest.class)) {
 
                   // 5. 执行当前方法
                   method.invoke(obj);
              }
          }
      }
  }
 
   输出结果 :
   测试方法一被执行 ...
   测试方法三被执行 ...

14Junit、反射、注解的更多相关文章

  1. 【Java EE 学习 24 下】【注解在数据库开发中的使用】【反射+注解+动态代理在事务中的应用service层】

    一.使用注解可以解决JavaBean和数据库中表名不一致.字段名不一致.字段数量不一致的问题. 1.Sun公司给jdbc提供的注解 @Table.@Column.@Id.@OneToMany.@One ...

  2. Javaweb学习笔记——(二十七)——————泛型、泛型的通配符、反射泛型信息、反射注解、注解

    泛型     1.泛型类:具有一个或多个类型变量的类,称之为泛型类 class A<T>{ } 2.在创建泛型实例时,需要为其类型变量赋值 A<String> a = new ...

  3. 【译】8. Java反射——注解

    原文地址:http://tutorials.jenkov.com/java-reflection/annotations.html ================================== ...

  4. JAVA-注解(2)-自定义注解及反射注解

    自定义注解开发 1.开发一个注解类 开发一个注解类的过程,非常类似于开发一个接口,只不过需要通过@interface关键字来声明 2.使用元注解修饰注解的声明 所谓的原注解是用来修饰注解声明的注释,可 ...

  5. Java通过反射注解赋值

    前段时间,领导分配一个统计销售区域汇总的数据,解决方案使用到了反射获取注解,通过注解获取属性或者设置字段属性. 问题描述 查询公司列表,分别是公司id.区域id.区域名称: 公司id 区域id 区域名 ...

  6. java反射--注解的定义与运用以及权限拦截

    自定义注解类编写的一些规则: 1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是 ...

  7. java反射注解妙用-获取所有接口说明

    转载请注明出处:https://www.cnblogs.com/wenjunwei/p/10293490.html 前言 最近在做项目权限,使用shiro实现restful接口权限管理,对整个项目都进 ...

  8. Java反射+注解案例

    注解类代码,注解的属性可以有多个: package reflect; import java.lang.annotation.Retention; import java.lang.annotatio ...

  9. 5.13Junit单元测试-反射-注解

    一.Junit单元测试 * 测试分类: 1.黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值 2.白盒测试:需要些代码的.关注程序具体的执行流程 Junit使用:白盒测试 步骤: 1.定义 ...

  10. 大数据JavaWeb之java基础巩固----Junit&反射&注解

    最近打算从0开始学学大数据,目前的主业是Android开发,但是当年毕业之后其实是搞J2EE的,所以打算没事又来拓展一下后台的技能,扩宽一下自己的知识体系对于自己的未来也能够多一些可能,另外大数据的一 ...

随机推荐

  1. Photoshop 基础三 制作简单按钮

    要求知识点:移动工具.选择工具.套索工具.多边行工具.文本工具.路径选择工具.裁剪.填充 一.制作简单按钮 1)新建画布,大小随便 2)画圆角矩形工具(同时定义背景色.边框是否需求.边框颜色) 3)打 ...

  2. AliOS-Things ESP8266 编译下载

    首先:环境搭建,可以参照https://github.com/alibaba/AliOS-Things/wiki/Quick-Start.zh:我采用的是linux系统: 其次:一般项目文件夹放置在A ...

  3. QT 按钮的使用技巧

    按钮透明 //    ui->pushButton->setFlat( true );//    ui->pushButton->setStyleSheet( "QP ...

  4. 没有内涵段子可以刷了,利用Python爬取段友之家贴吧图片和小视频(含源码)

    由于最新的视频整顿风波,内涵段子APP被迫关闭,广大段友无家可归,但是最近发现了一个"段友"的app,版本更新也挺快,正在号召广大段友回家,如下图,有兴趣的可以下载看看(ps:我不 ...

  5. java算法----排序----(2)选择排序

    package log; public class Test4 { /** * java算法---选择排序 * * @param args */ public static void main(Str ...

  6. linux驱动编写之阻塞与非阻塞

    一.概念 应用程序使用API接口,如open.read等来最终操作驱动,有两种结果--成功和失败.成功,很好处理,直接返回想要的结果:但是,失败,是继续等待,还是返回失败类型呢?  如果继续等待,将进 ...

  7. 从harbor部署到在k8s中使用

    一.概述 harbor是什么呢?英文单词的意思是:港湾.港湾用来存放集装箱(货物的),而docker的由来正是借鉴了集装箱的原理,所以harbor是用于存放docker的镜像,作为镜像仓库使用.官方的 ...

  8. Log4j2使用笔记

                 log4j2是log4j的最新版,现在已经有很多公司在使用了.log4j2和log4j的优缺点对比,请自行百度. 上一篇笔记讲了关于log4j的使用.这篇笔记主要讲解log4 ...

  9. Intel x86_64 Architecture Background 3

    多层次的cache结构解决了CPU和DRAM之间处理速度不一致的问题,在Intel体系架构下,CPU核心和主存DRAM之间有着三层的cache.其中一层缓存L1和二层缓存L2在CPU核心(core)中 ...

  10. .NetCore实践篇:成功解决分布式监控ZipKin聚合依赖问题(三)

    前言 读本篇文章之前,可以先读前两篇文章.为了照顾没看过的朋友,我也会稍作复习. 思考大纲: .Net架构篇:思考如何设计一款实用的分布式监控系统? 实践篇一:.NetCore实践篇:分布式监控客户端 ...