代码编织梦想

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)来定义模板。

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/aa35434/article/details/135311360

13天java进阶笔记-day12-junit单元测试、反射、注解、动态代理_madao10086+的博客-爱代码爱编程

第一章 单元测试 单元测试是指程序员写的测试代码给自己的类中的方法进行预期正确性的验证。 单元测试一旦写好了这些测试代码,就可以一直使用,可以实现一定程度上的自动化测试。 单元测试一般要使用框架进行。 什么是框架? 框架是前人或者一些牛逼的技术公司在实战或者研发中设计的一些优良的设计方案或者成型的 代码功能,作为一个完整的技术体系发行出来称为框架。

【hm-02java进阶-day14-junit单元测试-反射-注解】_学点没用的的博客-爱代码爱编程

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

eclipse-爱代码爱编程

一,基础注解 junit的基础注解有@BeforeClass,Before,Test(),After,@AfterClass 执行顺序为:@BeforeClass>Before>Test1>After>Before>Test2>After>AfterClass 具体用法看一下代码: publ

spring cloud 概述及项目创建-爱代码爱编程

本篇主要介绍什么是Spring Cloud,以及Spring Cloud工程的创建 目录 一、什么是微服务? 集群  分布式 微服务 二、Spring Cloud 什么是Spring Cloud Spring Cloud 版本 Spring Cloud实现方案 Spring Cloud 工程创建 创建父工程 创建子工程 一

java导出excel动态加载多sheet多复杂表头-爱代码爱编程

java导出excel动态加载多sheet多复杂表头 实体实现类sheet方法业务工具方法实现效果 实体 import com.fasterxml.jackson.annotation.JsonFo

spring-爱代码爱编程

1. 微服务架构 1.1 单体应用架构---内部项目【OA WMS等】 将项目所有模块(功能)打成jar或者war,然后部署一个进程 优点: 1:部署简单:由于是完整的结构体,可以直接部署在一个服务器上即可。 2:技术单一:项目不需要复杂的技术栈,往往一套熟悉的技术栈就可以完成开发(ssm+数据库)。 缺点:

springmvc基础-爱代码爱编程

目录 一:什么是SpringMVC 二:SpringMVC快速入门 1.导入依赖 2.创建核心配置类 3.创建controller关键类 4.创建访问的资源 5.配置tomcat 6.浏览器测试 7.解决中文乱码问题:   三:测试工具:Postman 1.软件介绍: 2.测试请求数据  Get请求: 2.1:普通参数—参数名相

遍历二叉树的统一迭代法-爱代码爱编程

二叉树的统一迭代法 普通迭代遍历方式无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况,前中后序的迭代法各有区别,那么如何实现统一的代码写法呢 那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。 如何标记呢?  就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。

mysql-爱代码爱编程

问题排查的思路与方向 问题排查思路 分析问题:根据理论知识+经验分析问题,判断问题可能出现的位置或可能引起问题的原因,将目标缩小到一定范围;排查问题:基于上一步的结果,从引发问题的“可疑性”角度出发,从高到低依次进行排查

jar包增量更新分析-爱代码爱编程

jdk自带工具jdeps,可分析class依赖关系(依赖的其它类和jar)。 团队,可以在此工具结果的基础上再详细分析对比出增量文件; 思路如下: jdeps分别分析出旧包和新包的文件依赖关系。并对比出新增的文件列表、删

kubernetes 审计日志采集与分析最佳实践-爱代码爱编程

Kubernetes 审计日志概述 Kubernetes 在 1.7 版本中发布了审计(Audit)日志功能,审计(Audit)提供了安全相关的时序操作记录(包括时间、来源、操作结果、发起操作的用户、操作的资源以及请求/响应的详细信息等),通过审计日志,我们能够非常清晰的知道 K8S 集群到底发生了什么事情,包括但不限于: 当前/历史上集群发生了哪些变

命令执行漏洞(二)-爱代码爱编程

Struts2框架命令执行漏洞 环境准备:虚拟机win2003     jspstudy2016     Struts Showcase靶场环境 查看历史相关漏洞在线平台 : 乌云(WooYun.org)历史漏洞查询: https://wy.zone.ci/searchbug.php?q=Struts2 知道创宇 Seebug 漏洞平