day10--java高级编程:枚举类,注解,JUnit单元测试

3 枚举类+注解

3.1 枚举类概念

  • 枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。

  • 枚举类的例子举不胜举:

    • 星期:Monday(星期一)…Sunday(星期天)
    • 性别:Man(男)、Woman(女)
    • 月份:January(1月)…December(12月)
    • 季节:Spring(春节)…Winter(冬天)
    • 三原色:red(红色)、green(绿色)、blue(蓝色)
    • 支付方式:Cash(现金)、WeChatPay(微信)、Alipay(支付宝)、BankCard(银行卡)、CreditCard(信用卡)
    • 就职状态:Busy(忙碌)、Free(空闲)、Vocation(休假)、Dimission(离职)
    • 订单状态:Nonpayment(未付款)、Paid(已付款)、Fulfilled(已配货)、Delivered(已发货)、Checked(已确认收货)、Return(退货)、Exchange(换货)、Cancel(取消)
    • 线程状态:创建、就绪、运行、阻塞、死亡
  • 若枚举只有一个对象, 则可以作为一种单例模式的实现方式。

  • 枚举类的实现:

    • 在JDK5.0 之前,需要程序员自定义枚举类型。
    • 在JDK5.0 之后,Java支持enum关键字来快速定义枚举类型。

经验之谈:

  • 开发中,当需要定义一组常量时,强烈建议使用枚举类。
  • 开发中,如果针对于某个类,其实例是确定个数的。则推荐将此类声明为枚举类。

3.2 定义枚举类(JDK5.0 之前)

在JDK5.0 之前如何声明枚举类呢?

  • 私有化类的构造器,保证不能在类的外部创建其对象
  • 在类的内部创建枚举类的实例。声明为:public static final ,对外暴露这些常量对象
  • 对象如果有实例变量,应该声明为private final(建议,不是必须),并在构造器中初始化

3.2.1 举例1:无参构造

枚举类:

package com.cn;


public class Light {
    //创建红绿灯的3个实例:红 绿 黄
    public static final  Light RED= new Light();
    public static final  Light GREEN = new Light();
    public static final  Light YELLOW = new Light();

    //提供私有的构造方法用来创建对象(默认提供的构造方法就是私有的,所以可以省略)
    private Light(){

    }

}

在main方法中调用枚举类对象:

package com.cn;


public class LightDemo {
    public static void main(String[] args) {
        Light r = Light.GREEN;//通过类名调用静态变量对象
        System.out.println(r);//com.cn.Light@4eec7777
    }
}

在这里插入图片描述

3.2.2 举例2:有参构造

枚举类:

package com.cn;


public class Light2 {
    //4.创建当前类的实例(红绿灯),需要使用public static final修饰
    public static final Light2 RED = new Light2("红");
    public static final Light2 GREEN = new Light2("绿");
    public static final Light2 YELLOW = new Light2("黄");

    //2. 声明当前类的对象的实例变量,使用private final修饰
    //想要让外界只能获取不能修改,所以设置为final修饰的
    private final String name;

    //1. 私有化类的构造器,提供带参构造
    private Light2(String name){
        this.name=name;
    }

    //3. 提供实例变量的get方法,获取属性值
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light2{" +
                "name='" + name + '\'' +
                '}';
    }
}


在main方法中调用枚举类对象:

package com.cn;


public class LightDemo2 {
    public static void main(String[] args) {
       Light2 r2 = Light2.GREEN;

        //Light2.GREEN=null;对象是final修饰的,所以外界不能赋值

        System.out.println(r2);//Light2{name='绿'}
        System.out.println(r2.getName());//绿
    }
}

在这里插入图片描述

3.2.3 举例3:含有抽象方法

枚举类:

package com.cn;


//2.含有抽象方法的类必须是抽象类
public abstract class Light3 {
    //3.抽象类不能实例化,子类实现后必须重写所有的抽象方法(匿名子类)
    public static final Light3 RED = new Light3("红") {
        @Override
        public void show() {
            System.out.println("红");
        }
    };

    public static final Light3 GREEN = new Light3("绿"){

        @Override
        public void show() {
            System.out.println("绿");
        }
    };
    public static final Light3 YELLOW = new Light3("黄"){

        @Override
        public void show() {
            System.out.println("黄");
        }
    };


    private final String name;


    private Light3(String name){
        this.name=name;
    }


    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light3{" +
                "name='" + name + '\'' +
                '}';
    }

   //1.提供抽象方法
    public abstract void show();
}

在main方法中调用枚举类对象:

package com.cn;


public class LightDemo3 {
    public static void main(String[] args) {
       Light3 r3 = Light3.GREEN;

        System.out.println(r3);//Light2{name='绿'}
        System.out.println(r3.getName());//绿
        r3.show();//绿
    }
}

在这里插入图片描述

3.3 定义枚举类(JDK5.0 之后)

发现自己定义一个枚举类,比较麻烦,所以,java就提供了枚举类供我们使用

格式:只有枚举项的枚举类

【修饰符】 enum 枚举类名{
    枚举项1,枚举项2,枚举项3...;
}

注意事项:

  • 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。
  • 列出的实例,系统会自动添加 public static final 修饰。
  • 如果常量对象列表后面没有其他代码,那么“;”可以省略,否则不可以省略“;”。
  • 编译器给枚举类默认提供的是private的无参构造,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数
  • 如果枚举类需要的是有参构造,需要手动定义,有参构造的private可以省略,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。
  • 枚举类默认继承的是java.lang.Enum类,因此不能再继承其他的类型。
  • 枚举类也可以有抽象方法,但是枚举项必须重写该方法
  • JDK5.0 之后switch,提供支持枚举类型,case后面可以写枚举常量名,无需添加枚举类作为限定。

3.3.1 举例1:无参构造&在switch中的用法

枚举类:

package com.cc;

//自定义的枚举类默认的父类就是Enum,因此不能再继承其他的类型,哪怕是Object
//如:public enum Light2 extends Object{ }    会报错
public enum Light {
    //枚举项:创建light类的对象(枚举类的第一行必须是枚举项)
    //调用的是无参构造(默认就是private修饰的)
    RED,GREEN,YELLOW;



/*     查看底层源码,发现底层做了以下事情:
        public final class Light extends Enum {
        //创建了3个对象
        public static final Light RED = new Light("RED"",0);
        public static final Light GREEN = new Light("GREEN",1);
        public static final Light YELLOw = new Light("YELLOW",2);

        //提供了有参构造
        private Light(String s, int i) {
            super(s,i); //调用父类的有参构造
        }
    }

        public abstract class Enum ... {
        private final String name ;

        protected Enum (String name,int ordinal) {
            //会把对象的值RED赋值给name
            this.name = name ;
            this.ordinal = ordinal;
        }
        public String toString() {
           //最终通过toString()方法返回name
            return name;
        }
    }

*/

}

在main方法中调用枚举类对象:

package com.cc;


import static com.cc.Light.*;

public class LightDemo {
    public static void main(String[] args) {
        Light r = Light.RED;
        //打印的不是地址值,说明底层源码重写了toString()
        System.out.println(r);//RED


        Light ll = Light.GREEN;
        //测试枚举在switch中的用法
        //因为里面放的是枚举项,所以case后的值必须是在枚举中出现过的,否则会报错。
        switch (ll){
            case RED:
                System.out.println("红");
                break;
            case GREEN:
                System.out.println("绿");//绿
                break;
            case YELLOW:
                System.out.println("黄");
                break;
        }

    }
}


在这里插入图片描述

3.3.2 举例2:有参构造

枚举类:

package com.cc;


//自定义的枚举类默认的父类就是Enum,因此不能再继承其他的类型,哪怕是Object
//如:public enum Light2 extends Object{ }   会报错
public enum Light2 {
    //1. 必须在枚举类的开头声明多个对象。对象之间使用,隔开
    //调用有参构造
    RED("红"),GREEN("绿"),YELLOW("黄");

    /*  jdk5之前的形式,在jdk5之后必须要简写为以上方式,并且位于枚举类的首行
    public static final Light2 RED = new Light2("红");
    public static final Light2 GREEN = new Light2("绿");
    public static final Light2 AUTUMN = new YELLOW("黄");
    */



    //2. 声明当前类的对象的实例变量,使用private final修饰
    //想要让外界只能获取不能修改,所以设置为final修饰的
    private String name;

    //3. 私有化类的构造器,private可以省略
    private Light2(String name){
        this.name = name;
    }

    //4. 提供实例变量的get方法,获取属性值
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light2{" +
                "name='" + name + '\'' +
                '}';
    }
}

在main方法中调用枚举类对象:

package com.cc;


public class LightDemo2 {
    public static void main(String[] args) {
        Light2 r2 = Light2.RED;

        //打印的值不再是源码中toString返回的 RED,
        // 而是自己重写后的toString方法的返回值 红
        System.out.println(r2);//Light2{name='红'}
        System.out.println(r2.getName());//红

    }
}

在这里插入图片描述

3.3.3 举例3:含有抽象方法

枚举类:

package com.cc;

//含有抽象方法的枚举类不需要添加abstract
public enum Light3 {
    //枚举项:创建light类的对象
    //调用有参构造
    RED("红"){
        @Override
        public void show() {
            System.out.println("红");
        }
    },
    GREEN("绿"){
        @Override
        public void show() {
            System.out.println("绿");
        }
    },
    YELLOW("黄"){
        @Override
        public void show() {
            System.out.println("黄");
        }
    };

    private String name;

    private Light3(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light3{" +
                "name='" + name + '\'' +
                '}';
    }
    //提供抽象类
    public abstract void show();
}

在main方法中调用枚举类对象:

package com.cc;


public class LightDemo3 {
    public static void main(String[] args) {
        Light3 r3 = Light3.RED;

        //打印的值不再是源码中toString返回的 RED,
        // 而是自己重写后的toString方法的返回值 红
        System.out.println(r3);//Light3{name='红'}
        System.out.println(r3.getName());//红
        r3.show();//红

    }
}

在这里插入图片描述

3.4 enum中的常用方法

String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
    
static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法
    
static 枚举类型 valueOf(String name):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentExceptionString name():得到当前枚举常量的名称。建议优先使用toString()int ordinal():返回当前枚举常量的次序号,默认从0开始

测试:

枚举类:

package com.cc;


public enum Light2 {

    //创建枚举类对象
    RED("红"),GREEN("绿"),YELLOW("黄");



    private String name;


    private Light2(String name){
        this.name = name;
    }


    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light2{" +
                "name='" + name + '\'' +
                '}';
    }
}

在main方法中调用枚举类对象:

package com.cc;


public class LightDemo2 {
    public static void main(String[] args) {

        Light2 r2 = Light2.RED;

        //测试方法
        //1.toString()
        //没重写输出的是 对象名: RED
        //重写后输出的是 重写后的返回值: Light2{name='红'}
        System.out.println(r2);

        //2.name()
        //得到当前枚举常量的名称: RED  跟重不重写oString没关系
        System.out.println(r2.name());

        //3.values()
        //静态方法,返回枚举类型的对象数组
        Light2[] l = Light2.values();
        for (int i=0;i<l.length;i++){
           /* Light2{name='红'}
            Light2{name='绿'}
            Light2{name='黄'}*/
            System.out.println(l[i]);
        }

        //4.valueOf(String name)
        //返回当前枚举类中名称为objName的枚举类对象。
        //如果枚举类中不存在objName名称的对象,则报错。
        String objName = "YELLOW";
        Light2 ll = Light2.valueOf(objName);
        System.out.println(ll);//Light2{name='黄'}

        //5.ordinal()
        //当前对象在枚举类中第几个声明的对象,默认从0开始
        System.out.println(r2.ordinal());//0





    }
}

3.5 枚举类实现接口

  • 和普通 Java 类一样,枚举类可以实现一个或多个接口
  • 若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。
  • 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法

3.5.1 情况1:执行的是同一个方法

情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法。

枚举类:

package com.cc;

//1定义接口
interface Info{
    void show();
}

//2枚举类实现接口
public enum Light2 implements Info{

    //创建枚举类对象
    RED("红"),GREEN("绿"),YELLOW("黄");



    private String name;


    private Light2(String name){
        this.name = name;
    }


    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light2{" +
                "name='" + name + '\'' +
                '}';
    }


    //3重写的抽象方法
    @Override
    public void show() {
        System.out.println("重写的抽象方法");
    }
}


在main方法中调用枚举类对象:

package com.cc;


public class LightDemo2 {
    public static void main(String[] args) {

        Light2[] l = Light2.values();
        for (int i=0;i<l.length;i++){
           /* 重写的抽象方法
              重写的抽象方法
              重写的抽象方法*/
            l[i].show();
        }



    }
}

在这里插入图片描述

3.5.2 情况2:执行的不是同一个方法

情况二:让枚举类的对象分别实现接口中的抽象方法,每个对象调这个方法时输出的内容不一样。

枚举类:

package com.cc;

//1定义接口
interface Info{
    void show();
}

//2枚举类实现接口
public enum Light2 implements Info{

    //3.创建枚举类对象
    //不在枚举类中直接重写抽象方法了,而是使用匿名子类的方式分别重写抽象方法
    RED("红"){
        @Override
        public void show() {
            System.out.println(111);
        }
    },GREEN("绿"){
        @Override
        public void show() {
            System.out.println(222);
        }
    },YELLOW("黄"){
        @Override
        public void show() {
            System.out.println(333);
        }
    };




    private String name;


    private Light2(String name){
        this.name = name;
    }


    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light2{" +
                "name='" + name + '\'' +
                '}';
    }


}



在main方法中调用枚举类对象:

package com.cc;


public class LightDemo2 {
    public static void main(String[] args) {

        Light2[] l = Light2.values();
        for (int i=0;i<l.length;i++){
            /*111
              222
              333*/
            l[i].show();
        }

    }
}

在这里插入图片描述

3.6 注解

总结:为了优化以前的开发模式,提出注解开发。减少了代码量,提高了注解的复用。标志是@ Annotation

在这里插入图片描述
在这里插入图片描述

3.7 注解的使用示例

3.7.1 生成文档相关的注解

在这里插入图片描述

3.7.2 JDK内置的注解

package com.atguigu.java1;

import java.util.ArrayList;
import java.util.Date;

/**
 * 注解的使用
 *
 * 1. 理解Annotation:
 * ① jdk 5.0 新增的功能
 *
 * ② Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用 Annotation,
 * 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。
 *
 * ③在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android
 * 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗
 * 代码和XML配置等。
 *
 * 2. Annocation的使用示例
 * 示例一:生成文档相关的注解
 * 示例二:在编译时进行格式检查(JDK内置的三个基本注解)
 *      @Override: 限定重写父类方法, 该注解只能用于方法
 *      @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
 *      @SuppressWarnings: 抑制编译器警告
 *      @SafeVarargs jdk1.7出现,堆污染,不常用
 *      @FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
 *
 * 示例三:跟踪代码依赖性,实现替代配置文件功能
 * @author shkstart
 * @create 2019 上午 11:37
 */
public class AnnotationTest {

    public static void main(String[] args) {
        Person p = new Student();
        p.walk();

        Date date = new Date(2020, 10, 11);//已过时,但仍然能用,源码中用@Deprecated注解修饰了
        System.out.println(date);

        @SuppressWarnings("unused")//加上注解警告会消失,unused属性:没有用。  里面的值可以看成是成员变量
        int num = 10;//定义了一个变量没有用,在eclipse中会有一个警告:定义了变量没有用。在idea中变量名变为灰色。

        @SuppressWarnings({ "unused", "rawtypes" })  //变量没有用,没有添加泛型
        ArrayList list = new ArrayList();



    }



}



class Person{
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void walk(){
        System.out.println("人走路");
    }
    public void eat(){
        System.out.println("人吃饭");
    }
}

interface Info{
    void show();
}

class Student extends Person implements Info{

    @Override//不加上注解不一定不是重写,加上注解会在编译期检查是否为重写父类的方法 如果不是则报错。
    public void walk() {
        System.out.println("学生走路");
    }

    public void show() {

    }
}




3.7.3 跟踪代码依赖性,实现替代配置文件功能

在这里插入图片描述
在这里插入图片描述

3.8 元注解

3.8.1 概念

在这里插入图片描述

3.8.2 测试

自定义注解中加上元注解:

package com.atguigu.java1;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

/**
 * 1.如何自定义注解:参照@SuppressWarnings定义
 * ① 注解声明为:@interface
 * ② 内部定义成员,通常使用value表示
 * ③ 可以指定成员的默认值,使用default定义
 * ④ 如果自定义注解没有成员,表明是一个标识作用。public @interface MyAnnotation {},使用时也不需要写属性值
 *
 * 注意:
 * 如果注解有成员,在使用注解时,需要指明成员的值。
 * 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
 * 自定义注解通过都会指明两个元注解:Retention、Target
 *
 *  2.jdk 提供的4种元注解
 *        元注解:对现有的注解进行解释说明的注解
 *      Retention:指定所修饰的 Annotation 的生命周期:SOURCE(在源文件中有效)\CLASS(默认行为, 在class文件中有效)\RUNTIME(在运行时有效)
 *                                               只有声明为RUNTIME生命周期的注解,才能通过反射获取。
 *      Target:用于指定被修饰的 Annotation 能用于修饰哪些程序元素:如果不指定则代表那都可以用。
 *            ElementType.ANNOTATION_TYPE 		应用于注释类型
 *            ElementType.CONSTRUCTOR 			应用于构造函数
 *            ElementType.FIELD 				应用于字段或属性
 *            ElementType.LOCAL_VARIABLE 		应用于局部变量
 *            ElementType.METHOD 				应用于方法级
 *            ElementType.PACKAGE 				应用于包声明
 *            ElementType.PARAMETER 			应用于方法的参数
 *            ElementType.TYPE 					应用于类的元素
 *      *******出现的频率较低*******
 *      Documented:表示所修饰的注解在被javadoc解析时,保留下来。默认情况下,javadoc是不包括注解的。定义为Documented的注解必须设置Retention值为RUNTIME。
 *
 *      Inherited:被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation, 则其子类将自动具有该注解。
 *                如何证明子类继承了注解--->通过反射获取注解信息 ---到反射内容时系统讲解
 */

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER,TYPE_USE})
@Inherited
public @interface MyAnnotation {
    //如果只有一个参数成员,建议使用参数名为value。注意这是一个属性不是方法,虽然有()。
    //String value() ;//形式一:可以是任意类型的,如果value值可以指定多个可以定义为数组如:String[] value() ;
    String value() default "hello";//形式二:通过default关键字指定一个默认值
}



使用注解:

package com.atguigu.java1;

public class AnnotationTest {

    public static void main(String[] args) {
        Person p = new Student();
        p.walk();
    }
}


//@MyAnnotation(value="hello")//形式一:使用注解,因为自定义注解@MyAnnotation有变量value,所以这里需要指定一个值 否则会报错。暂时随便指定个值,没有特殊含义.只有一个值可以简写为:@MyAnnotation("hello")
@MyAnnotation()//形式二:因为使用的value属性有默认值,此时不用指定,也不会报错。不想要默认值,可以进行修改如:@MyAnnotation(value="hi")
class Person{
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void walk(){
        System.out.println("人走路");
    }
    public void eat(){
        System.out.println("人吃饭");
    }
}

interface Info{
    void show();
}

//student继承了person类,person添加了自定义注解@MyAnnotation(),又因为自定义注解设置了元注解@Inherited具有可继承性,所以
//student也有@MyAnnotation()注解。如何证明student继承了注解??---->通过反射
class Student extends Person implements Info{

    @Override
    public void walk() {
        System.out.println("学生走路");
    }

    public void show() {

    }
}




3.9 自定义注解

3.9.1 概述

在这里插入图片描述

3.9.2 测试

定义注解:

package com.atguigu.java1;

/**
 * 如何自定义注解:参照@SuppressWarnings定义
 * ① 注解声明为:@interface
 * ② 内部定义成员,通常使用value表示
 * ③ 可以指定成员的默认值,使用default定义
 * ④ 如果自定义注解没有成员,表明是一个标识作用。public @interface MyAnnotation {},使用时也不需要写属性值
 *
 * 注意:
 *1. 如果注解有成员,在使用注解时,需要指明成员的值。
 *2. 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
 *3. 自定义注解通过都会指明两个元注解:Retention、Target---详情查看3.6元注解
 * 
 */


public @interface MyAnnotation {
    //如果只有一个参数成员,建议使用参数名为value。注意这是一个属性不是方法,虽然有()。
    //String value() ;//形式一:可以是任意类型的,如果value值可以指定多个可以定义为数组如:String[] value() ;
    String value() default "hello";//形式二:通过default关键字指定一个默认值
}



使用注解:

package com.atguigu.java1;

public class AnnotationTest {

    public static void main(String[] args) {
        Person p = new Student();
        p.walk();
    }
}


//@MyAnnotation(value="hello")//形式一:使用注解,因为自定义注解@MyAnnotation有变量value,所以这里需要指定一个值 否则会报错。暂时随便指定个值,没有特殊含义。只有一个值可以简写为:@MyAnnotation("hello")
@MyAnnotation()//形式二:因为使用的value属性有默认值,此时不用指定,也不会报错。不想要默认值,可以进行修改如:@MyAnnotation(value="hi")
class Person{
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void walk(){
        System.out.println("人走路");
    }
    public void eat(){
        System.out.println("人吃饭");
    }
}

interface Info{
    void show();
}

class Student extends Person implements Info{

    @Override
    public void walk() {
        System.out.println("学生走路");
    }

    public void show() {

    }
}




3.10 jdk1.8注解新特性

3.10.1 可重复注解 和 类型注解

类AnnotationTest:

package com.atguigu.java1;

import java.util.ArrayList;

public class AnnotationTest {

    public static void main(String[] args) {
        Person p = new Student();
        p.walk();
    }
}


//@MyAnnotation(value="hello")//形式一:使用注解,因为自定义注解@MyAnnotation有变量value,所以这里需要指定一个值 否则会报错。暂时随便指定个值,没有特殊含义.只有一个值可以简写为:@MyAnnotation("hello")
//@MyAnnotation()//形式二:因为使用的value属性有默认值,此时不用指定,也不会报错。不想要默认值,可以进行修改如:@MyAnnotation(value="hi")

//jdk 8之前的写法想要使用重复的注解:想要写多个注解,在自定义注解MyAnnotations中声明为MyAnnotation类型的数组:MyAnnotation[] value();
//@MyAnnotations({@MyAnnotation(value="hi"),@MyAnnotation(value="hi")})
//jdk 8开始想要这样使用重复的注解:
@MyAnnotation(value="hi")
@MyAnnotation(value="abc")
class Person{
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void walk(){
        System.out.println("人走路");
    }
    public void eat(){
        System.out.println("人吃饭");
    }
}

interface Info{
    void show();
}

//student继承了person类,person添加了自定义注解@MyAnnotation(),又因为自定义注解设置了元注解@Inherited具有可继承性,所以
//student也有@MyAnnotation()注解。如何证明student继承了注解??---->通过反射
class Student extends Person implements Info{

    @Override
    public void walk() {
        System.out.println("学生走路");
    }

    public void show() {

    }
}
//类型注解的使用:
class Generic<@MyAnnotation T>{

    public void show() throws @MyAnnotation RuntimeException{

        ArrayList<@MyAnnotation String> list = new ArrayList<>();

        int num = (@MyAnnotation int) 10L;
    }

}

注解MyAnnotation:

package com.atguigu.java1;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;

/**
 * 1.如何自定义注解:参照@SuppressWarnings定义
 * ① 注解声明为:@interface
 * ② 内部定义成员,通常使用value表示
 * ③ 可以指定成员的默认值,使用default定义
 * ④ 如果自定义注解没有成员,表明是一个标识作用。public @interface MyAnnotation {},使用时也不需要写属性值
 *
 * 注意:
 * 如果注解有成员,在使用注解时,需要指明成员的值。
 * 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
 * 自定义注解通过都会指明两个元注解:Retention、Target
 *
 *  2.jdk 提供的4种元注解
 *        元注解:对现有的注解进行解释说明的注解
 *      Retention:指定所修饰的 Annotation 的生命周期:SOURCE(在源文件中有效)\CLASS(默认行为, 在class文件中有效)\RUNTIME(在运行时有效)
 *                                               只有声明为RUNTIME生命周期的注解,才能通过反射获取。
 *      Target:用于指定被修饰的 Annotation 能用于修饰哪些程序元素:如果不指定则代表那都可以用。
 *            ElementType.ANNOTATION_TYPE 		应用于注释类型
 *            ElementType.CONSTRUCTOR 			应用于构造函数
 *            ElementType.FIELD 				应用于字段或属性
 *            ElementType.LOCAL_VARIABLE 		应用于局部变量
 *            ElementType.METHOD 				应用于方法级
 *            ElementType.PACKAGE 				应用于包声明
 *            ElementType.PARAMETER 			应用于方法的参数
 *            ElementType.TYPE 					应用于类的元素
 *      *******出现的频率较低*******
 *      Documented:表示所修饰的注解在被javadoc解析时,保留下来。默认情况下,javadoc是不包括注解的。定义为Documented的注解必须设置Retention值为RUNTIME。
 *
 *      Inherited:被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation, 则其子类将自动具有该注解。
 *                如何证明子类继承了注解--->通过反射获取注解信息 ---到反射内容时系统讲解
 * 3.可重复注解:① 在MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
 *             ② MyAnnotation的Target和Retention等元注解(如:@Inherited)与MyAnnotations相同。
 * 4. 类型注解写在@Target注解中:
 *      ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)。
 *      ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。
 */

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER,TYPE_USE})
@Inherited
@Repeatable(MyAnnotations.class)//jdk 8开始想要这样使用重复的注解
public @interface MyAnnotation {
    //如果只有一个参数成员,建议使用参数名为value。注意这是一个属性不是方法,虽然有()。
    //String value() ;//形式一:可以是任意类型的,如果value值可以指定多个可以定义为数组如:String[] value() ;
    String value() default "hello";//形式二:通过default关键字指定一个默认值
}

注解MyAnnotations:

package com.atguigu.java1;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Inherited
public @interface MyAnnotations {
    MyAnnotation[] value();// jdk 8之前的写法:想要写多个注解声明为数组
}

4 JUnit单元测试

4.1 测试分类

黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。

白盒测试:需要写代码的。关注程序具体的执行流程。

在这里插入图片描述

在这里插入图片描述

4.2 JUnit单元测试介绍

JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个测试框架(regression testing framework),供Java开发人员编写单元测试之用。

JUnit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。

要使用JUnit,必须在项目的编译路径中引入JUnit的库,即相关的.class文件组成的jar包。jar就是一个压缩包,压缩包都是开发好的第三方(Oracle公司第一方,我们自己第二方,其他都是第三方)工具类,都是以class文件形式存在的。

此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试,JUnit目前最新版本是5

4.3 使用main方法测试的缺点

  1. 只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响。
  2. 无法得到测试的结果报告,需要程序员自己去观察测试是否成功。
  3. 无法实现自动化测试。
    在这里插入图片描述

4.4 Junit单元测试的优点

  1. JUnit可以灵活的选择执行哪些测试方法,也可以一键执行全部测试方法。
  2. JUnit可以生测试报告,如果测试良好则是绿色;如果测试失败则是红色
  3. 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
    在这里插入图片描述

4.5 引入本地JUnit.jar

说明

  • IDEA通常整合好了Junit框架,一般不需要导入。
    • 解释:很多idea只是把Junit框架集成了并没有下载,所以你第一次使用时可以根据注解提示的报错进行自动下载(前提是联网),在之后使用中就不需要导包了。
  • 如果IDEA没有整合好,需要自己手工导入如下2个JUnit的jar包到模块
    在这里插入图片描述

第1步:在项目中File-Project Structure中操作:添加Libraries库

在这里插入图片描述

在这里插入图片描述

其中,junit-libs包内容如下:

在这里插入图片描述

第2步:选择要在哪些module中应用JUnit库

在这里插入图片描述

第3步:检查是否应用成功

在这里插入图片描述

注意Scope:选择Compile,否则编译时,无法使用JUnit。

第4步:下次如果有新的模块要使用该libs库,这样操作即可

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.6 编写和运行@Test单元测试方法

JUnit4版本,要求@Test标记的方法必须满足如下要求:

  • 所在的类必须是public的,非抽象的,包含唯一的无参构造器。
  • @Test标记的方法本身必须是public,非抽象的,非静态的,void无返回值,()无参数的。
package com.atguigu.junit;

import org.junit.Test;

public class TestJUnit {
    @Test
    public void test01(){
        System.out.println("TestJUnit.test01");
    }

    @Test
    public void test02(){
        System.out.println("TestJUnit.test02");
    }

    @Test
    public void test03(){
        System.out.println("TestJUnit.test03");
    }
}

在这里插入图片描述

4.7 设置执行JUnit用例时支持控制台输入

1. 设置数据:

默认情况下,在单元测试方法中使用Scanner时,并不能实现控制台数据的输入。需要做如下设置:

idea64.exe.vmoptions配置文件中加入下面一行设置,重启idea后生效。

-Deditable.java.test.console=true

2. 配置文件位置:

在这里插入图片描述

在这里插入图片描述

添加完成之后,重启IDEA即可。

3. 如果上述位置设置不成功,需要继续修改如下位置

修改位置1:IDEA安装目录的bin目录(例如:D:\develop_tools\IDEA\IntelliJ IDEA 2022.1.2\bin)下的idea64.exe.vmoptions文件。

修改位置2:C盘的用户目录C:\Users\用户名\AppData\Roaming\JetBrains\IntelliJIdea2022.1 下的idea64.exe.vmoptions`件。

4.8 定义test测试方法模板

选中自定义的模板组,点击”+”(1.Live Template)来定义模板。

在这里插入图片描述

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值