Java基础-枚举类与注解
枚举类&注解
一、枚举类的使用
枚举类是什么?
枚举类即是对象确定且有限个的类。比如
- 星期一、星期二、星期三、...
- 男、女
- 春、夏、秋、冬
这些都可以使用枚举类进行创建。
当我们需要定义带有相同关系的一组常量时,建议使用枚举类。
枚举类的实现?
- 在JDK 1.5 之前我们需要自定义枚举类
- 而在JDK 1.5 之后新添的
enum关键字,用于定义枚举类。
若枚举类只有一个对象,则可以作为一种单例模式的实现方式。
枚举类的属性?
- 枚举类对象的属性不应允许被改动,所以应该使用
private final进行修饰。 - 枚举类的使用
private final修饰的属性应该在构造器中为其赋值。 - 若枚举类显式的定义了带参数的构造器, 则在列出枚举值时也必须对应的传入参数
自定义枚举类
自定义枚举类中有几个需要注意的点:
- 私有化的构造器,因为枚举类的对象个数是确定且有限的,即不允许在类的外部创建其他对象。
- 在类的内部创建枚举类的实例。私有化的构造器只允许在类内部创建枚举类实例。同时为了保证外部不能修改其枚举类实例,需要使用
public static final - 对象如果有实例变量,应该声明为
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 修饰。 - 必须在枚举类的第一行声明枚举类变量。
- 使用enum 定义的枚举类默认继承了
- 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的部署.

自定义注解
- 自定义新注解使用 @interface 关键字,自动继承
java.lang.annotation.Annotation接口 - 定义 Annotation 成员变量时,以 无参数方法 的形式来声明。
- 只有一个参数成员,建议使用value命名,指定默认值使用default关键字
- 如果注解没有成员表明是一个标识作用
- 使用注解的时候,需要设定一个值,如果已经有了默认值,那么,就可以不用手写默认值了。
- 自定义的注解,需要配上信息处理流程(使用反射)才有意义。
- 通常会指明两个元注解,
@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:被他修饰的注解将具有继承性,即:父类有该注解,则子类也拥有该注解.
- Retention:指定所修饰的Annotation的声明周期
利用反射获取注解信息
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{
//....
}
- MyAnnotation注解和MyAnnotations注解的@Retention和@Targer注解相同,由于生命周期、作用元素的不同可能会导致可重复注解失效。
- 生命周期MyAnnotations注解不能比MyAnnotation短,因为MyAnnotation注解使用了MyAnnotation.class作为值
- 作用元素部位也应相同,若MyAnnotation可以修饰该结构,但MyAnnotations则不一定可以修饰,也会导致报错/
@Inherited注解也应相同,否则会报错,MyAnnotation时@Inherited,而包含MyAnnotation的MyAnnotations注解则不是@Inhertied。
通过上下的比较,可以发现,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";
}

浙公网安备 33010602011771号