day01 注解与反射

# 今日内容
  1.Junit单元测试
  2.反射
  3.注解
## Junit单元测试
测试分类
  1.黑盒测试: 不需要写代码 给输入值 看程序是否能够输出期望的值
  2.白盒测试:需要写代码 关注程序具体执行过程

使用步骤:
  1.定义一个测试类(测试用例)

    建议 类名:被测试的类名+Test
    建议 包名:xxx.xxx.xx.test
2.定义测试方法:可以独立运行
    建议:方法名 test+测试的方法名
    建议返回值:使用void 无返回
    建议:参数列表 建议空参
    3.给方法加一个@Test
4.导入依赖

判定结果:红色 失败 绿色 成功
  一般会使用断言来测试结果: Assert.assertEquals(17,calculator.add(8,9));
  补充:@Before:修饰的方法会在测试方法之前被执行
  @After:修饰的方法会在测试方法之后执行


##反射 框架设计的灵魂
    *框架:半成品软件。可以在框架的基础上进行软件开发,简化编码

    *反射概念:将类的各个组成部分封装为其他对象 这就是反射机制

  
  好处:
    1.可以在程序运行中 操作这些对象
    2.可以解耦 提高程序的可扩展性
  *获取Class对象的方式:
    1.Class.forName(“全类名”):将字节码文件加载到内存中,返回Class对象
    *多用于配置文件 将类名定义在配置中
    2.类名.class:通过类名.class属性获取
    *多用于参数的传递
    3  .对象.getClass(): Object中的方法
      *多用于对象的获取字节码的方式
    *结论:
      同一个字节码文件(*.class)在一次程序运行过程中 只会被加载一次
      不论用什么方法获取的Class对象都是同一个

    *class对象功能:
    *获取功能:
    1.获取成员变量们
      *Field[] getFields() 获取public的
      *Field[] getField(String name)

      *Field[] getDeclaredFields() //获取所有成员变量包括private
      *Field[] getDeclaredField(String name)
    2.获取构造方法
      getConstructor(类<?>... parameterTypes) 
      getConstructors()

      getDeclaredConstructors() 
      getDeclaredConstructor(类<?>... parameterTypes) 
    3.获取成员方法们
      getMethod(String name, 类<?>... parameterTypes) 
      getMethods()

      getDeclaredMethod(String name, 类<?>... parameterTypes)
      getDeclaredMethods() 
    4.获取类名
      getName()

      *Field:成员变量
        *操作:
        1.设置值
        void set(Object obj,Obiect value);
        2.获取值
        *get(Object obj)
        3.忽略访问权限修饰符的安全检查
        * age.setAccessible(true);//暴力反射

*Constructor:构造方法
      *创建对象:
      newInstance(Object...initargs)
      *如果使用空参构造方法创建对象 可以简化 使用Class字节码对象调用newInstance方法
      *Method:方法对象
      *执行方法:
      *Object invoke(Object obj,Object...args)
      *获取方法名称
        *String getName
#反射案例 :
*需求 :写一个“框架”,可以帮我们创建任意类的对象,并且执行其中任意方法
*实现:
    1.配置文件
     2.反射
    *步骤:
      1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
      2.在程序加载 读取配置文件
      3.使用反射技术来加载类文件进内存 
      4.创建对象
      5.执行方法

   反射案例:

1 package cn.itcast.domain;
2 
3 public class Student {
4     public void sleep(){
5         System .out.println("睡觉");    }
6 
7 }

 配置文件

 

 1  public static void main(String[] args) throws Exception {
 2         // 创建Properties对象
 3         Properties pro=new Properties();
 4         //加载配置文件 转换为一个集合
 5         //获取class目录下的配置文件
 6         ClassLoader classLoader = ReflectTest.class.getClassLoader();
 7         InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
 8         pro.load(resourceAsStream);
 9 
10         //获取配置文件中定义的类名 方法
11         String classname=pro.getProperty("className");
12         String mName=pro.getProperty("methodName");
13 
14         //加载该类进内存
15         Class cls=Class.forName(classname);
16         //创建对象
17         Object o = cls.newInstance();
18         //获取方法
19         Method method = cls.getMethod(mName);
20         //执行方法
21         method.invoke(o);
22     }

 

 

##  注解:

  *概念:说明程序的 给计算机看的

  *注释:用文字描述程序的,给程序员看的

  定义:  注解(Annotation),也叫元数据。一种代码级别的说明,它是jdk1.5版本后引入的一个特性

      与类,接口,枚举是在同一个层次,它可以声明再包,类,字段,方法,局部变量,方法参数等的前面

      用来都这些元素进行说明,注释。

  *概念描述:  1.JDk1.5指挥的新特性     2.说明程序的     3.使用注解:@注解名称

  *作用分类:  

      1.编写文档:通过代码里标识的注解生成文档[生成文档doc文档]

      2.代码分析:通过代码里标识的注解对代码进行分析【使用反射】

      3.编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查

  *JDK中预定义的一些注解

       * @Override:检测被该注解标注的方法是否是继承自父类(接口)的     

 @Override
    public String toString() {
        return super.toString();
    }

      *@Deprecated:该注解标注的内容,表示已过时

@Deprecated
    public void show1(){
        //有缺陷
    }
    public void show2(){
        //替代show1方法
    }
    public void demo(){
       show1();    //这里会提示你show不推荐使用
    }

      *@SuppressWarnings:压制警告   一般传递一个参数("all")

1   @SuppressWarnings("all")
2     public void show2(){
3         //替代show1方法
4     }

  *自定义注解

      *格式:

        元注解

        public @interface 注解名称{ }

public @interface MyAnno {
    //属性:成员方法
    int show2();
    String name();
}

      *本质:注解本质上就是一个接口,该接口默认继承Annotation接口

        *public interface MyAnno extends java.lang.annotation.Annotation{  }

      *属性:接口中的抽象方法

         *要求:

            1.属性的返回值类型  有下列取值 其他都不行

              *基本数据类型

              *String

              *枚举

              *注解

              *以上类型的数组

            2.定义了属性,再使用时需要给属性赋值

              1.如果定义属性时:使用default关键字给属性默认初始值   则使用注解时可以不用赋值

              2.如果只有一个属性需要赋值  并且属性的名称是value 则可以省略属性名  

              3.数组赋值时,则使用{ }包裹。如果数组中只有一个值 大括号可以省略     

 @MyAnno(show2 = 5,name = "我去")
    public void show2(){
     
    }

 

      *元注解:用于描述注解的注解

          *@Target:描述注解能够作用的位置

@Target(value = ElementType.TYPE)  //表示该注解只能作用于类上
public @interface MyAnno3 { //METHOD:可以作用于方法上   FIELD:作用于成员变量上
    
}

        

          *@Retention:描述注解被保留的阶段  

@Retention(RetentionPolicy.RUNTIME)//当前被描述的注解,会被保留到class字节码文件中 并被jvm读取到
public @interface MyAnno3 {

}

          *@Documented:描述注解是否被抽取到api文档中

          *@Inherited:描述注解是否被子类继承

@Inherited
public @interface MyAnno3 {

}

 

*在程序中使用(解析)注解

      1.获取注解定义的位置的对象(class method Field)

      2.获取指定的注解

          * getAnnotation(Class)

      3.调用注解中的抽象方法获取配置的属性值

/*
* 自定义Pro注解描述需要执行的类名和方法名
* 1*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    String className();
    String methodName();
}
/*
* 测试注解
* */
@Pro(className = "cn.itcast.annotation.demo1",methodName = "show")
public class ReflectTest {
        /*
        * 不能更改该类的代码
        * */
    public static void main(String[] args) throws Exception {
      //1.解析注解
        //2.获取该类的字节码文件对象
        Class reflectTestClass=ReflectTest.class;
        //3.获取上边的注释对象
        Pro an = (Pro) reflectTestClass.getAnnotation(Pro.class);//在内存中去生成了一个该注解接口的子类实现对象
        String s = an.className();
        String s1 = an.methodName();

        //加载该类进内存
        Class cls=Class.forName(s);
        //创建对象
        Object o = cls.newInstance();
        //获取方法
        Method method = cls.getMethod(s1);
        //执行方法
        method.invoke(o);
    }
}

 * 小结 

    1.以后大多数时候,我们会使用注解  而不是自定义注解

    2.注解给谁用?

      1.编译器

      2.给解析程序使用

    3.注解不是程序的一部分,可以理解为注解就是一个标签

 

  注解案例:

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface Check {
4 }
 1 *
 2 * 计算机类
 3 * */
 4 public class Calculator {
 5     /*
 6     * 加法
 7     * */
 8     @Check
 9     public int add(int a,int b){
10         return a/b;
11     }
12     /*
13     * 减法
14     * */
15     public int sub(int a,int b){
16         return a-b;
17     }
18 }
 1 /*
 2 * 简单的测试框架
 3 * 当主方法执行后 会自动自行被检测的所有方法(加了Check的方法)判断方法是否有异常 记录到文件中
 4 * */
 5 public class TestCheck {
 6     public static void main(String[] args) throws IOException {
 7         //创建计算机对象
 8         Calculator calculator=new Calculator();
 9         //获取字节码对象
10         Class cls=calculator.getClass();
11         //获取所有方法
12         Method[] methods = cls.getMethods();
13 
14         int num=0;//出现异常的次数
15         BufferedWriter bw=new BufferedWriter(new FileWriter("bug.txt"));
16         //判断方法是否有Check
17         for (Method method : methods) {
18           if (method.isAnnotationPresent(Check.class)){
19               //有check注解就执行
20               try {
21                   //执行方法
22                   method.invoke(calculator,5,0);
23               } catch (Exception e) {
24                   //捕获异常
25                   //记录到文件中
26                   num++;
27                   bw.write(method.getName()+"方法出现异常");
28                   bw.newLine();
29                   bw.write("异常的名称是"+e.getCause().getClass().getSimpleName());
30                   bw.newLine();
31                   bw.write("异常的原因是:"+e.getCause().getMessage());
32                   bw.newLine();
33                   bw.write("-----------------");
34               }
35           }
36         }
37         bw.write("本次测试一共出现"+num+"次异常");
38         bw.flush();
39         bw.close();
40     }
41 }

 注解解析图:

 

posted @ 2020-06-21 13:07  LinResolute  阅读(77)  评论(0)    收藏  举报