注解

今日学习目标 :

  1. 能够使用Junit进行单元测试
  2. 能够说出注解的作用
  3. 能够使用JDK提供的3个注解
  4. 能够根据基本语法编写自定义注解实现类
  5. 能够了解自定义注解解析
  6. 能够了解元注解使用
  7. 能够根据上课案例分析,编写模拟@Test案例
  8. 能够理解动态代理原理
  9. 能够使用动态代理Proxy编写代理类

 

 

    1. Junit介绍与注解概述 (*****了解*****)

Junit是Java语言编写单元测试框架,最直观的理解,就是取代java类中的main方法。Junit属于第三方工具,一般情况下需要导入jar包,而多数Java开发环境都集成了Junit。

 

Java Unit Java单元测试框架

 

Junit是Java语言编写单元测试框架,最直观的理解,就是取代java类中的main方法。Junit属于第三方工具,一般情况下需要导入jar包,而多数Java开发环境都集成了Junit。

 

Junit的使用

创建“MyJunit”java项目,并创建“cn.itcast.junit”包

  1. 编写测试类,简单理解Junit可以用于取代java的main方法
  2. 在测试类方法上添加注解 @Test
  3. @Test修饰的方法要求:public void 方法名() {…} ,方法名自定义建议test开头,没有参数。

  1. 添加Eclipse中集成的Junit库,鼠标点击“@Test”,使用快捷键“ctrl + 1”,点击“Add Junit …”

结果

      

  1. 使用:选中方法右键,执行当前方法;选中类名右键,执行类中所有方法(方法必须标记@Test)

  1. 常用注解

@Test,用于修饰需要执行的方法

@Before,测试方法前执行的方法

@After,测试方法后执行的方法

  1. import org.junit.After;
  2. import org.junit.Before;
  3. import org.junit.Test;
  4.  
  5. public class JunitDemo {
  6.        @Before
  7.        public void test_Before() {
  8.               System.out.println("Before 注解的方法 ...");
  9.        }
  10.       
  11.        @After
  12.        public void test_After() {
  13.               System.out.println("After 注解的方法 ...");
  14.        }
  15.       
  16.        @Test
  17.        public void test_01() {
  18.               System.out.println("测试 test01 方法 ...");
  19.        }
  20.       
  21.        @Test
  22.        public void test_02() {
  23.               System.out.println("测试 test02 方法 ...");
  24.        }
  25.       
  26.        @Test
  27.        public void test_03() {
  28.               System.out.println("测试 test03 方法 ...");
  29.        }
  30. }

 

  1. 常见使用错误,如果没有添加“@Test”,使用“Junit Test”进行运行,将抛异常

 

 

 

常见的第三方注解 :

 

Annotation 其实就是代码里的特殊标记, 有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。

掌握注解技术的要点:

   1. 如何定义注解

    2. 如何反射注解,并根据反射的注解信息,决定如何去运行类

 

* 注解不但可以通知编译器,在运行时通知信息给 JVM虚拟机.

 

对比注释:注释是给开发人员阅读的,注解是给计算机提供相应信息的。

 

  1. 什么是注解:Annotation注解,是一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次
  2. 注解的作用:
    1. 编译检查:通过代码里标识注解,让编译器能够实现基本的编译检查。例如:@Override
    2. 代码分析:通过代码里标识注解,对代码进行分析。 例如 : @Test
    3. 编写文档:通过代码里标识注解,辅助生成帮助文档对应的内容

 

目的 : 为了替代配置文件.   xml -> 替代配置文件   properities 属性集类

 

 

 

 

 

    1. JDK提供的注解 (*****了解*****)

JDK 5.0 之后出现注解的技术 :

JDK官方提供了三个注解 

@Override: 限定重写父类方法, 该注解只能用于方法 ------ 编译时检查,不构成覆盖 报错

* JDK5.0 override注解只能用于方法覆盖 JDK6.0 该注解可以用于接口的方法实现

@Deprecated: 用于表示某个程序元素(, 方法等)已过时 ----- 在编译器进行编译时 报出一个警告 

* 为什么会有过时方法: 提供了新的方法取代之前方法、发现某个方法存在安全隐患,不建议用户再使用 

@SuppressWarnings: 抑制编译器警告.  ---- 通知编译器在编译时 不要报出警告信息

* 使用 all 忽略所有警告信息

 

rawtypes ,忽略类型安全

       unused ,忽略不使用

serial, 忽略序列号

       all,忽略所有

 

 

  1. package cn.itcast.annotation;
  2.  
  3. import java.util.ArrayList;
  4.  
  5. @SuppressWarnings("all")
  6. public class JDKAnnotationDemo01 {
  7.        public static void main(String[] args) {
  8.               // JDK提供的注解 (了解)
  9.              
  10.               Wolf wolf = new Wolf();
  11.               wolf.eat();
  12.              
  13.               wolf.lovingSheep();
  14.              
  15.               // @SuppressWarnings("unused")
  16.               int sum = 10 + 20;
  17.              
  18.               // @SuppressWarnings({"rawtypes", "unused"})
  19.               ArrayList list = new ArrayList();
  20.        }
  21. }
  22.  
  23. interface Inter {
  24.        void shout();
  25. }
  26.  
  27. class Animal {
  28.        public void eat() {
  29.               System.out.println("随便吃...");
  30.        }
  31. }
  32.  
  33. class Wolf extends Animal implements Inter {
  34.        // @Override 修饰的方法必须是父类中重写的方法或是接口中的抽象方法
  35.        @Override
  36.        public void eat() {
  37.               System.out.println("狼吃肉...");
  38.        }
  39.       
  40.        @Override
  41.        public void shout() {
  42.               System.out.println("嗷嗷嗷...");
  43.        }
  44.       
  45.        @Deprecated
  46.        public void lovingSheep() {
  47.               System.out.println("狼爱上了羊...");
  48.        }
  49. }

 

 

 

 

 

 

 

    1. 自定义注解:定义—基本语法 (*****重点*****)

 

首先介绍一下注解的分类 :

 

 

 

 

元注解类型 :

 

@Target 与 @Retention 取值类型 :

 

 

如何自定义注解 :

 

 

所有的Annotation自动实现java.lang.annotation.Annotation接口 

 

注解支持类型:八种基本数据类型和StringEnumClassannotation以及以上类型的数组) 

 

  1. // 注解的作用域
  2. @Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
  3. // 注解的保留策略 (生命周期)
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Inherited
  6. @Documented
  7. @interface Description {
  8.        // 格式 : 数据类型 属性名();
  9.        // 合法类型 : 8中基本数据类型, String, Class, Annotation, Enumeration. (12中类型限制)
  10.        // 注解可以没有任何成员, 没有成员的注解被称为 `标识注解`.
  11.        String desc();
  12.       
  13.        String author();
  14.       
  15.        int age() default 18;
  16. }

 

 

    1. 自定义注解:使用  (*****重点*****)

 

 

  1. // 自定义一个类, 使用自定义注解
  2. class Student {
  3.        // 格式: @注解名(成员1=1,成员2=2...)
  4.        @Description(desc="学霸兼学渣", author="Jack", age=30)
  5.        public void introduce() {
  6.               System.out.println("大家好, 我是学生.");
  7.        }
  8. }

 

 

 

    1. 自定义注解 : 解析 (*****重点*****)

 

 

  1. // 自定义注解 : 解析
  2. public class CustomAnnotationDemo02 {
  3.        public static void main(String[] args) throws Exception {
  4.              
  5.               // 需求 : 解析 Student 类中 introduce() 方法上定义的注解信息
  6.              
  7.               /*
  8.               // 说明 : 该类必须是一个独立的 .java 文件, 类的修饰符必须为 public
  9.               Class<?> cls1 = Class.forName("cn.itcast.annotation.Student");
  10.               System.out.println(cls1 == cls2);
  11.               */
  12.               // 1. 获取 Student 类的 Class 对象
  13.               Class<?> cls = Student.class;
  14.              
  15.               // 2. 通过反射获取到 introduce() 方法对象
  16.               Method method = cls.getMethod("introduce", new Class[]{});
  17.              
  18.               // 3. 判断该方法上是否存在指定类型的注解信息
  19.               if (method.isAnnotationPresent(Description.class)) {
  20.                     Description annotation = method.getAnnotation(Description.class);
  21.                     // 根据获取的 annotation 对象, 取出注解中定义的信息
  22.                     String desc = annotation.desc();
  23.                     String author = annotation.author();
  24.                     int age = annotation.age();
  25.                     System.out.println(desc + ", " + author + ", " + age);
  26.               }
  27.        }
  28. }

 

 

 

详细讲解 :

 

声明注解的属性 :

如果注解中有一个名称value的属性,那么使用注解时可以省略value=部分

特殊属性String value, String[] value;

 

 

自定义一个注解类 :

 

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5.  
  6. //@Target(value={ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
  7. //@Retention(value=RetentionPolicy.RUNTIME)
  8. @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. public @interface MyAnnotation {
  11.        String value();
  12. }

 

定义一个类, 使用自定义注解 :

 

  1. // @MyAnnotation(value="Jack")
  2. @MyAnnotation("Jack")
  3. public class AnnotationClass {
  4.        // 属性
  5.        @MyAnnotation("itcast007")
  6.        private String id;
  7.       
  8.        // 行为
  9.        @MyAnnotation("Java")
  10.        public void study() {
  11.               System.out.println("好好学习, 天天向上!");
  12.        }
  13. }

 

定义一个测试类, 解析注解中的信息 :

 

  1. import java.lang.reflect.Field;
  2. import java.lang.reflect.Method;
  3.  
  4. public class AnnotationDemo02 {
  5.        public static void main(String[] args) throws Exception {
  6.              
  7.               // 1. 获取 AnnotationClass 类的字节码对象
  8.               Class<?> cls = AnnotationClass.class;
  9.              
  10.               // 2. 获取类上的注解
  11.               if (cls.isAnnotationPresent(MyAnnotation.class)) {
  12.                     MyAnnotation anno = cls.getAnnotation(MyAnnotation.class);
  13.                     String value = anno.value();
  14.                     System.out.println("类上的注解信息为 : " + value);
  15.               }
  16.              
  17.               // 3. 获取属性上的注解
  18.               Field field = cls.getDeclaredField("id");
  19.               field.setAccessible(true);
  20.               if (field.isAnnotationPresent(MyAnnotation.class)) {
  21.                     MyAnnotation anno = field.getAnnotation(MyAnnotation.class);
  22.                     String value = anno.value();
  23.                     System.out.println("属性上的注解信息为 : " + value);
  24.               }
  25.              
  26.               // 4. 获取方法上的注解
  27.               Method method = cls.getMethod("study", new Class[]{});
  28.               if (method.isAnnotationPresent(MyAnnotation.class)) {
  29.                     MyAnnotation anno = method.getAnnotation(MyAnnotation.class);
  30.                     String value = anno.value();
  31.                     System.out.println("方法上的注解信息为 : " + value);
  32.               }
  33.        }
  34. }

 

 

 

    1. 案例:自定义@Test (*****练习*****)
      1. 案例分析
  1. 模拟Junit测试,首先需要编写自定义注解@MyTest,并添加元注解,保证自定义注解只能修饰方法,且在运行时可以获得。
  2. 其次编写目标类(测试类),然后给目标方法(测试方法)使用@MyTest注解
  3. 最后编写测试类,使用main方法模拟Junit的右键运行。

 

 

注解程序开发流程

1、编写注解类

使用@interface 定义

所有注解类都默认继承 java.lang.annotation.Annotation 接口

注解支持类型:八种基本数据类型和StringEnumClassAnnotation以及以上类型的数组) 

如果注解属性提供默认值 ,使用注解时可以不设置新的值

如果注解只有一个value 属性,使用注解 省略value= 

2、在一个类 应用注解类

3、通过反射技术获得注解信息

通过java.lang.reflect.AnnotatedElement 在程序运行时 获得注解信息 

 

元注解:修饰注解的注解

@Retention 声明注解存活生命周期 --Source Class Runtime --- 开发中主要使用Runtime

@Target 声明注解可以修饰对象类型 ---- FIELD TYPEMETHOD

@Documented 注解可以被 生成API文档

@Inherited 注解在使用时,可以被子类继承

 

 

 

 

      1. 案例实现 – MyTest 实现
  1. 步骤1:编写自定义注解类@MyTest

 

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface MyTest {
  4.  
  5. }

 

 

  1. 步骤2:编写目标类MyTestClass

 

  1. public class MyTestClass {
  2.       
  3.        @MyTest
  4.        public void demo01() {
  5.               System.out.println("测试一方法执行...");
  6.        }
  7.       
  8.        @MyTest
  9.        public void demo02() {
  10.               System.out.println("测试二方法执行...");
  11.        }
  12.       
  13.        @MyTest
  14.        public void demo03() {
  15.               System.out.println("测试三方法执行...");
  16.        }
  17. }

 

 

 

  1. 步骤3:编写测试方法

 

  1. import java.lang.reflect.Method;
  2.  
  3. public class MyTestDemo {
  4.        public static void main(String[] args) throws Exception {
  5.              
  6.               // 需求 : 运行指定类中所有被 @MyTest 注解修饰的方法.
  7.              
  8.               // 1. 获取指定该类的 Class 对象
  9.               Class<?> cls = MyTestClass.class;
  10.               // 2. 创建一个cls表示的实例对象
  11.               Object obj = cls.newInstance();
  12.               // 3. 获取该类中的所有公共方法
  13.               Method[] methods = cls.getMethods();
  14.               // 4. 遍历方法数组, 获取每一个方法对象
  15.               for (Method method : methods) {
  16.                     // 4.2 判断方法上是否存在 MyTest 类型的注解
  17.                     if (method.isAnnotationPresent(MyTest.class)) {
  18.                            // 4.3 如果存在, 反射执行该方法
  19.                            // method.invoke(obj, new Object[]{});
  20.                            method.invoke(obj);
  21.                     }
  22.               }
  23.        }
  24. }

 

 

 

补充 : 添加 @MyBefore 和 @MyAfter 注解 :

 

 

2. 动态代理 (*****了解*****)

  1. 定义接口和实现类, 并完成测试

 

 

  1. public interface ArithmeticInter {
  2.        // 抽象方法
  3.        void sum(int num1, int num2);
  4.        void minus(int num1, int num2);
  5.        void multiply(int num1, int num2);
  6.        void divide(int num1, int num2);
  7. }

 

  1. public class ArithmeticImpl1 implements ArithmeticInter {
  2.  
  3.        @Override
  4.        public void sum(int num1, int num2) {
  5.               int result = num1 + num2;
  6.               System.out.println(result);
  7.        }
  8.  
  9.        @Override
  10.        public void minus(int num1, int num2) {
  11.               int result = num1 - num2;
  12.               System.out.println(result);
  13.        }
  14.  
  15.        @Override
  16.        public void multiply(int num1, int num2) {
  17.               int result = num1 * num2;
  18.               System.out.println(result);
  19.        }
  20.  
  21.        @Override
  22.        public void divide(int num1, int num2) {
  23.               int result = num1 / num2;
  24.               System.out.println(result);
  25.        }
  26. }

 

  1. import org.junit.Test;
  2.  
  3. public class DynamicProxyDemo {
  4.  
  5.        @Test
  6.        public void demo01() {
  7.               ArithmeticInter arithmetic = new ArithmeticImpl1();
  8.               arithmetic.sum(10, 20);
  9.               arithmetic.minus(20, 8);
  10.               arithmetic.multiply(5, 5);
  11.               arithmetic.divide(20, 5);
  12.        }
  13. }

 

 

  1. 增强一些功能

 

直接修改实现类的每个方法中的代码 :

 

  1. public class ArithmeticImpl1 implements ArithmeticInter {
  2.  
  3.        @Override
  4.        public void sum(int num1, int num2) {
  5.               System.out.println("The method sum begins with " + num1 + ", " + num2 + " ^v^");
  6.               int result = num1 + num2;
  7.               System.out.println(result);
  8.               System.out.println("The method sum ends with " + num1 + ", " + num2 + " ^v^");
  9.        }
  10.  
  11.        @Override
  12.        public void minus(int num1, int num2) {
  13.               System.out.println("The method minus begins with " + num1 + ", " + num2 + " ^v^");
  14.               int result = num1 - num2;
  15.               System.out.println(result);
  16.               System.out.println("The method minus ends with " + num1 + ", " + num2 + " ^v^");
  17.        }
  18.  
  19.        @Override
  20.        public void multiply(int num1, int num2) {
  21.               System.out.println("The method multiply begins with " + num1 + ", " + num2 + " ^v^");
  22.               int result = num1 * num2;
  23.               System.out.println(result);
  24.               System.out.println("The method multiply ends with " + num1 + ", " + num2 + " ^v^");
  25.        }
  26.  
  27.        @Override
  28.        public void divide(int num1, int num2) {
  29.               System.out.println("The method divide begins with " + num1 + ", " + num2 + " ^v^");
  30.               int result = num1 / num2;
  31.               System.out.println(result);
  32.               System.out.println("The method divide ends with " + num1 + ", " + num2 + " ^v^");
  33.        }
  34. }

 

 

  1. import org.junit.Test;
  2.  
  3. public class DynamicProxyDemo {
  4.  
  5.        @Test
  6.        public void demo01() {
  7.               ArithmeticInter arithmetic = new ArithmeticImpl1();
  8.               arithmetic.sum(10, 20);
  9.               arithmetic.minus(20, 8);
  10.               arithmetic.multiply(5, 5);
  11.               arithmetic.divide(20, 5);
  12.        }
  13. }

 

 

  1. 动态代理的实现思路详解

 

  1. 动态代理的代码实现 (*****练习*****)

 

 

  1. @Test
  2. public void demo02() {
  3.        // 1. 创建了一个 `实现类` 的对象
  4.        final ArithmeticInter arithmetic = new ArithmeticImpl2();
  5.       
  6.        // 2. 使用 `动态代理` 实现方法的调用
  7.        // 准备参数
  8.        // 参数1 : ClassLoader loader 运行时新创建的类, 需要被加载到内存, 只有类加载器负责将类加载到内存
  9.        ClassLoader loader = DynamicProxyDemo.class.getClassLoader();
  10.        // 参数2 : Class[] interfaces 需要确定代理类实现的所有接口
  11.        Class<?>[] interfaces = ArithmeticImpl2.class.getInterfaces();
  12.        // 参数3 : InvocationHandler 接口, 动态代理具体处理的实现代码, 一般都使用匿名内部类实现
  13.        ArithmeticInter proxy = (ArithmeticInter) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
  14.               @Override
  15.               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  16.                     // 参数1 : Object proxy 代理类本身 (很少使用)
  17.                     // 参数2 : Method method 当前执行的方法
  18.                     // 参数3 : Object[] args 当前方法的实际参数
  19.                     System.out.println("The method "+method.getName()+" begins with "+Arrays.toString(args)+" ^v^");
  20.                     Object obj = method.invoke(arithmetic, args);
  21.                     System.out.println("The method "+method.getName()+" ends with "+Arrays.toString(args)+" ^v^");
  22.                     return obj;
  23.               }
  24.        });
  25.       
  26.        // 使用代理调用方法
  27.        proxy.sum(10, 20);
  28.        proxy.minus(20, 8);
  29.        proxy.multiply(5, 5);
  30.        proxy.divide(20, 5);
  31. }

 

 

 

 

  1. 模拟一个歌手的动态代理实现 : (*****练习*****)

 

  1. package cn.itcast.dynamicproxy2;
  2.  
  3. public interface SuperStar {
  4.        // 方法
  5.        void sing(int money);
  6.        void liveShow(int money);
  7.        void sleep();
  8. }

 

 

  1. package cn.itcast.dynamicproxy2;
  2.  
  3. public class LiuYan implements SuperStar {
  4.  
  5.        @Override
  6.        public void sing(int money) {
  7.               System.out.println("唱了一首 <<真的爱你>>");
  8.               System.out.println("赚了: " + money + " ");
  9.        }
  10.  
  11.        @Override
  12.        public void liveShow(int money) {
  13.               System.out.println("参加了 Running Man 节目");
  14.               System.out.println("赚了: " + money + " ");
  15.        }
  16.  
  17.        @Override
  18.        public void sleep() {
  19.               System.out.println("好累, 休息一下, 马上回来...");
  20.        }
  21. }

 

 

  1. public class SuperStarDemo {
  2.  
  3.        @Test
  4.        public void demo01() {
  5.               LiuYan liuYan = new LiuYan();
  6.              
  7.               liuYan.sing(5);
  8.               liuYan.liveShow(10);
  9.               liuYan.sleep();
  10.        }
  11.       
  12.        @Test
  13.        public void demo02() {
  14.               final LiuYan liuYan = new LiuYan();
  15.              
  16.               // 有一个代理 (proxy)
  17.               ClassLoader loader = SuperStarDemo.class.getClassLoader();
  18.               Class<?>[] interfaces = liuYan.getClass().getInterfaces();
  19.               SuperStar proxy = (SuperStar) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
  20.                     @Override
  21.                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  22.                            String methodName = method.getName();
  23.                            if ("sing".equals(methodName)) {
  24.                                   int money = (int) args[0];
  25.                                   if (money < 10000) {
  26.                                          System.out.println(", 你这个穷丝, 回家撸代码去...");
  27.                                          return null;
  28.                                   }
  29.                                   System.out.println("代理抽取了 : " + money * 0.3);
  30.                                   return method.invoke(liuYan, (int)(money * 0.7));                           
  31.                            } else if ("liveShow".equals(methodName)) {
  32.                                   int money = (int) args[0];
  33.                                   if (money < 100000) {
  34.                                          System.out.println(", 怎么还是你这个穷丝, 继续回家撸代码去...");
  35.                                          return null;
  36.                                   }
  37.                                   System.out.println("代理抽取了 : " + money * 0.3);
  38.                                   return method.invoke(liuYan, (int)(money * 0.7));                           
  39.                            }  else {                               
  40.                                   return method.invoke(liuYan, args);
  41.                            }
  42.                     }
  43.               });
  44.              
  45.               proxy.sing(5);
  46.               proxy.liveShow(10);
  47.               proxy.sleep();
  48.        }
  49. }

 

 

代理 : 对被代理对象的拦截和控制.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/u/3892666/blog/2249298

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值