Java基础-枚举类与注解

枚举类&注解

一、枚举类的使用

枚举类是什么?

枚举类即是对象确定有限个的类。比如

  • 星期一、星期二、星期三、...
  • 男、女
  • 春、夏、秋、冬

这些都可以使用枚举类进行创建。

当我们需要定义带有相同关系的一组常量时,建议使用枚举类。

枚举类的实现?

  • 在JDK 1.5 之前我们需要自定义枚举类
  • 而在JDK 1.5 之后新添的enum关键字,用于定义枚举类。

若枚举类只有一个对象,则可以作为一种单例模式的实现方式。

枚举类的属性?

  • 枚举类对象的属性不应允许被改动,所以应该使用private final进行修饰。
  • 枚举类的使用private final修饰的属性应该在构造器中为其赋值。
  • 若枚举类显式的定义了带参数的构造器, 则在列出枚举值时也必须对应的传入参数

自定义枚举类

自定义枚举类中有几个需要注意的点:

  1. 私有化的构造器,因为枚举类的对象个数是确定且有限的,即不允许在类的外部创建其他对象。
  2. 在类的内部创建枚举类的实例。私有化的构造器只允许在类内部创建枚举类实例。同时为了保证外部不能修改其枚举类实例,需要使用public static final
  3. 对象如果有实例变量,应该声明为private final,并在构造器中初始化。
public class SeasonTest{
    
    public static void main(String[] args){
        //自定义枚举类
        Season spring = Season.SPRING;
        System.out.println(spring.getSeasonName());
    }
    
}


class Season{

    /**
     * 声明Season对象的属性,使用private final修饰
     */
    private final String seasonName;

    private final String seasonDesc;

    /**
     * 构造器必须设置为私有的,因为枚举类的对象确定的,所以不能由外部调用构造器来创建对象
     * 并为对象属性初始化
     */
    private Season(String seasonName, String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    /**
     * 提供当前枚举类的多个对象 : public static final
     */
    public static final Season SPRING = new Season("春天","穿暖花开");
    public static final Season SUMMER = new Season("夏天","夏日炎炎");
    public static final Season AUTUMN = new Season("秋天","秋高气爽");
    public static final Season WINTER = new Season("冬天","冰天雪地");

    /**
     * 获取枚举类对象的属性
     */
    public String getSeasonDesc() {
        return seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

    /**
     * 提供tostring方法
     */
    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

使用enum定义枚举类

  • 使用说明
    • 使用enum 定义的枚举类默认继承了java.lang.Enum类,因此不能再继承其他类
    • 枚举类的构造器只能使用 private 权限修饰符
    • 枚举类的所有实例必须在枚举类中显示列出(,分隔,;结尾)。列出的实例系统会自动添加public static final 修饰。
    • 必须在枚举类的第一行声明枚举类变量。
  • JDK 1.5 中可以在switch表达时中使用Enum定义的枚举类的对象作为表达式。case子句可以直接使用枚举值的名字,无需添加枚举类作为限定。
public class SpringTest1 {

    public static void main(String[] args) {
        //使用enum关键字创建枚举类
        Season1 summer = Season1.SUMMER;
        System.out.println(summer);

        //根据获得其父类,可得知:enum定义的枚举类默认继承于java.lang.Enum类
        System.out.println(Season1.class.getSuperclass());
    }

}

enum Season1{

    /**
     * 提供当前枚举类的多个对象 : public static final
     * 而且必须在枚举类的第一行声明枚举类变量。
     */
    SPRING("春天","穿暖花开"),
    SUMMER("夏天","夏日炎炎"),
    AUTUMN("秋天","秋高气爽"),
    WINTER("冬天","冰天雪地");

    /**
     * 声明Season对象的属性,使用private final修饰
     */
    private final String seasonName;

    private final String seasonDesc;


    /**
     * 构造器必须设置为私有的,因为枚举类的对象确定的,所以不能由外部调用构造器来创建对象
     * 并为对象属性初始化
     */
    private Season1(String seasonName, String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    /**
     * 获取枚举类对象的属性
     */
    public String getSeasonDesc() {
        return seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

}

枚举类主要方法

  • values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
  • valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException
  • toString():返回当前枚举类对象常量的名称
public class TestEnumMethod{

	public static void main(String[] args){
		//自定义枚举类
        Season1 spring = Season1.SPRING;
        //输出:春天
        System.out.println(spring.getSeasonName());

        Season1[] values = Season1.values();
        //输出:SPRING SUMMER AUTUMN WINTER
        for (Season1 value : values) {
            System.out.println(value);
        }
        
        
        Thread.State[] values1 = Thread.State.values();
        //遍历出线程状态枚举类的所有枚举值
        for (Thread.State value : values1){
            System.out.println(value);
        }
        
        
        //返回枚举类中对象名是objName的对象
        Season1 winter = Season1.valueOf("WINTER");
        //如果没有找到,则抛出异常IllegalArgumentException
        //Season1 winter1 = Season1.valueOf("WINTER1");
        System.out.println(winter);
        
        
        //Enum类自动重写了toString方法,方法中return name;
        //输出:SPRING
        System.out.println(spring.toString())
	}

}

实现接口的枚举类

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

情况一:实现接口,在enum类中实现接口

interface Info {
    //显示对象的名字
    void show();
}

enum Season implements Info {

    //多个对象之间用逗号,最后一个对象用分号结束
    SPRING("SPRING", "春天"),
    SUMMER("SUMMER", "夏天"),
    AUTUMN("AUTUMN", "秋天"),
    WINTER("WINTER", "冬天");

    //1. 声明Seasons对象的属性
    private  final String SeasonName;
    private  final String SeasonDescrp;

    //2. 私有化类的构造器, 并给对象赋值初始化
    private Season(String SeasonName, String SeasonDescrp) {
        this.SeasonDescrp = SeasonDescrp;
        this.SeasonName   = SeasonName;
    }

    //实现了接口方法
    @Override
    public void show() {
        System.out.println("这是一个季节");
    }
}

情况二:让枚举类的对象,分别实现接口中的方法

interface Info {
    //显示对象的名字
    void show();
}
enum Season implements Info {
    //多个对象之间用逗号,最后一个对象用分号结束
    SPRING("SPRING", "春天"){
        @Override
        public void show() {
			//春天的方法
            System.out.println("春天");
        }
    },
    SUMMER("SUMMER", "夏天") {
        @Override
        public void show() {
        	System.out.println("夏天");
        }
    },
    AUTUMN("AUTUMN", "秋天") {
        @Override
        public void show() {
        	System.out.println("秋天");
        }
    },
    WINTER("WINTER", "冬天") {
        @Override
        public void show() {
        	System.out.println("冬天");
        }
    };

    //1. 声明Seasons对象的属性
    private  final String SeasonName;
    private  final String SeasonDescrp;

    //2. 私有化类的构造器, 并给对象赋值初始化
    private Season(String SeasonName, String SeasonDescrp) {
        this.SeasonDescrp = SeasonDescrp;
        this.SeasonName   = SeasonName;
    }
}

二、注解的使用

概述

  • 从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)
  • Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
  • Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在 Annotation 的 “name=value” 对中。
  • 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能, 忽略警告等。在 JavaEE/Android 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
  • 未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的 Struts2有一部分也是基于注解的了,注解是一种趋势,一定程度上可以说:框架 = 注解 + 反射 + 设计模式。

常见的Annotation示例

示例一:在 javadoc 生成文档中使用注解

类似于:@author、@version、@see、@since、@param等等

在生成文档中可以显示出来。

示例二:在编译时进行格式检查(JDK内置的三个基本注解)

  • @Override: 限定重写父类方法, 该注解只能用于方法
  • @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
  • @SuppressWarnings: 抑制编译器警告
class Person {
    
    public void walk() {
        System.out.println("人走路");
    }
    
	//下面的用于标记,这个方法已经过时了
    @Deprecated
    public void eat() {
        System.out.println("人吃饭");
    }
}
interface Info {
    void show();
}
class Student extends Person implements Info {
    
	//标记用于重写父类中的方法,以及实现接口中的方法
    @Override
    public void walk() { System.out.println("学生走路"); }
    @Override
    public void show() { System.out.println("我是学生"); }
    
    public static main(String[] args) {
        
        
        //在代码中,创建的变量未使用会有警告Waring
        //这里的unused就是表示忽略,没使用这个警告的
        @SuppressWarnings("unused")
        int num = 100;
        //System.out.println(num);
        
        //下面的这种"rawtypes"适用于使用泛型的时候并没有选取类型的警告
        @SuppressWarnings({"unused", "rawtypes"})
        ArrayList list = new ArrayList();
    }
}

示例三:跟踪代码依赖性,实现替代配置文件功能

Servlet3.0提供了注解(annotation),使得不再需要在web.xml文件中进行Servlet的部署.

image-20200226121316865

自定义注解

  1. 自定义新注解使用 @interface 关键字,自动继承java.lang.annotation.Annotation接口
  2. 定义 Annotation 成员变量时,以 无参数方法 的形式来声明。
  3. 只有一个参数成员,建议使用value命名,指定默认值使用default关键字
  4. 如果注解没有成员表明是一个标识作用
  5. 使用注解的时候,需要设定一个值,如果已经有了默认值,那么,就可以不用手写默认值了。
  6. 自定义的注解,需要配上信息处理流程(使用反射)才有意义。
  7. 通常会指明两个元注解,@Retention@Target
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE,TYPE_PARAMETER,TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    //可以使用default设置默认值
    String value() default "hello";

}

元注解

  • JDK中的元注解本质上就是:修饰注解的注解
  • JDK 1.5 提供了4中标准的元注解分别是
    • Retention:指定所修饰的Annotation的声明周期
      • 其中包含了一个RetentionPolicy类型的成员变量:SOURCE/CLASS(默认)/RUNTIME
      • 只有声明为runtime声明周期的注解,才能通过反射获取。
    • Target:用于修饰 Annotation 定义, 用于指定被修饰的 Annotation 能用于修饰哪些程序元素。
    • Documented:用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档。默认情况下,javadoc是不包括注解的。
      • 定义为Documented的注解必须设置Retention值为RUNTIME。
    • Inherited:被他修饰的注解将具有继承性,即:父类有该注解,则子类也拥有该注解.

利用反射获取注解信息

JDK8中注解新特性

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

此外, 反射也得到了加强,在Java8中能够得到方法参数的名称。这会简化标注在方法参数上的注解

可重复注解

在JDK 8 之前如果想使用重复注解时:

public @interface MyAnnotations {
    MyAnnotation[] value();
}

@interface MyAnnotation {
    String value() default "hello";
}
/**
 * jdk8之前的写法
 */
@MyAnnotations(
    value = {@MyAnnotation(value = "hi"),@MyAnnotation(value = "abc")}
)
class Person{
	//.....
}

而在JDK 8之后,我们可以使用@Repeatable注解来声明可重复注解。

@Repeatable的成员值为MyAnnotation.class

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
public @interface MyAnnotations {

    MyAnnotation[] value();

}

@Inherited
//该注解声明为可重复注解
@Repeatable(value = MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {

    String value() default "hello";

}
//JDK 8之后的写法
@MyAnnotation("Hello")
@MyAnnotation("hi")
class Person{
	//....
}
  1. MyAnnotation注解和MyAnnotations注解的@Retention和@Targer注解相同,由于生命周期、作用元素的不同可能会导致可重复注解失效。
  • 生命周期MyAnnotations注解不能比MyAnnotation短,因为MyAnnotation注解使用了MyAnnotation.class作为值
  • 作用元素部位也应相同,若MyAnnotation可以修饰该结构,但MyAnnotations则不一定可以修饰,也会导致报错/
  1. @Inherited注解也应相同,否则会报错,MyAnnotation时@Inherited,而包含MyAnnotation的MyAnnotations注解则不是@Inhertied。

  2. 通过上下的比较,可以发现,JDK8的新特性是对原来注解嵌套的简化。

类型注解

在Java 8之前,注解只能是在声明的地方所使用,Java8开始,注解可以应用 在任何地方。

  • ElementType.TYPE_PARAMETER:表示该注解能写在类型变量的声明语 句中(如:泛型声明)。
  • ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中。
class Generic<@MyAnnotation T>{

    public void show() throws @MyAnnotation RuntimeException{
        List<@MyAnnotation String> list = new ArrayList<String>();


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

}

//TYPE_PARAMETER TYPE_USE
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE,TYPE_PARAMETER,TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {

    String value() default "hello";

}
posted @ 2020-08-22 19:31  SalemG  阅读(121)  评论(0)    收藏  举报