枚举和注解
枚举
1. 枚举定义
- 枚举对应英文(enumeration,简写 enum)
- 枚举是一组常量的集合
- 可以这样理解:枚举属于一种特殊的类,里面只包含一组有限制的特定的对象
2. 自定义实现枚举
创建 Season 对象有如下特点:
- 季节的值是有限的几个值(spring,summer,autumn,winter)
- 只读,不需要修改
// 创建 Season 对象
public class Enumeration01 {
public static void main(String[] args) {
// 使用
Season1 spring = new Season1("春天", "温暖");
Season1 winter = new Season1("冬天", "寒冷");
Season1 summer = new Season1("夏天", "炎热");
Season1 autumn = new Season1("秋天", "凉爽");
autumn.setName("XXX");
autumn.setDesc("非常的热...")
// 对于季节而言,它的对象(具体值)是固定的4个,不会有更多
// 按照老师设计的这个类的思路,不能体现季节是固定的4个对象
// 因此,这样的设计不好====> 枚举类【枚:一个一个,举:例举,即把具体的对象一个一个列举出来的类
// 就称为枚举类】
Season1 other = new Season1("嘿天", "~~~");
}
}
class Season1 { // 类
private String name;
private String desc; // 描述
public Season1(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
优化并修改:
- 将构造器私有化,目的防止:直接 new
- 去掉 setXxx 方法,防止属性被修改
- 在Season 内部,直接创建固定的对象
- 对外暴露对象(通过为对象添加 public static final 修饰符)
public class Enumeration02 {
public static void main(String[] args) {
System.out.println(Season.AUTUMN);
System.out.println(Season.SPRING);
}
}
// 演示自定义枚举实现
class Season{ // 类
private String name;
private String desc; // 描述
// 定义了4个对象, 固定
// 为什么是 static,因为希望它可以直接被访问
public static final Season SPRING = new Season("春天", "温暖");
public static final Season WINTER = new Season("冬天", "寒冷");
public static final Season AUTUMN = new Season("秋天", "凉爽");
public static final Season SUMMER = new Season("夏天", "炎热");
// 1)将构造器私有化,目的:防止 直接new
// 2)去掉setXxx方法 【只能读,不能改】,防止属性被修改
// 3)在Season 内部,直接创建固定的对象
// 4)优化,可以加入 final 修饰符 【final 和 static 往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理】
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
3. enum 枚举类
使用 enum 来实现枚举类
- 使用关键字 enum 替代 class
- public static final Season SPRING = new Season("春天", "温暖") 直接使用 SPRING("春天", "温暖") 即:对象名(实参列表)
- 如果有多个常量(对象),使用 逗号 间隔即可
- 如果使用 enum 来实现枚举,要求将定义的常量对象,写在前面
enum Season2 { // 类
// 如果使用了 enum 来实现枚举类
// 1)使用关键字 enum 替代 class
// 2)ublic static final Season SPRING = new Season("春天", "温暖");直接使用
// SPRING("春天", "温暖")解读 常量名(实参列表)
// 3)如果有多个常量(对象),使用逗号间隔即可
// 4)如果使用enum 来实现枚举,要求定义常量对象,写在前面
SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热");
private String name;
private String desc; // 描述
private Season2(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
4. 使用enum 实现枚举注意事项
- 当我们用 enum 关键字开发一个枚举类时,默认会继承 Enum 类,而且是一个 final 类【使用 javap 工具来演示】
- 传统的 public static final Season2 SPRING = new Season2("春天", "温暖"),简化成 SPRING("春天", "温暖"),这里必须知道,它调用的是哪个构造器
- 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
- 当有多个枚举对象时,使用 , 间隔,最后有一个分号结尾
- 枚举对象必须放在枚举类的行首
看下 Enum 类的 toString() 方法
/**
* Returns the name of this enum constant, as contained in the
* declaration. This method may be overridden, though it typically
* isn't necessary or desirable. An enum type should override this
* method when a more "programmer-friendly" string form exists.
*
* @return the name of this enum constant
*/
public String toString() {
return name; // 返回枚举常量的名字
}
5. Enum 常用方法
使用关键字 enum 时,会隐式继承 Enum 类,这样我们就可以使用 Enum 类相关方法
方法名 | 详情描述 |
---|---|
valueOf | 将字符串转换成枚举对象,要求字符串必须为已有常量名,否则报异常 |
values(通过反编译才能看到) | 返回 Seasons[],含有定义的所有枚举对象 |
toString | 默认返回枚举常量的名字 |
equals | |
hashCode | |
getDeclaringClass | |
name | 输出枚举对象的名称 |
ordinal | 该枚举对象的次序(编号:从 0 开始) |
compareTo | 比较2个枚举常量的 编号 |
clone |
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal; // 编号1 - 编号2
}
练习:
1)声明Week 枚举类,其中包含 星期一至星期日的定义:MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;
2)使用values 返回所有的枚举数组,并遍历
public class EnumExercise02 {
public static void main(String[] args) {
// 获取到所有的枚举对象, 即数组
Week[] weeks = Week.values(); // 别忘记写 main 方法
for (Week week : weeks) {
System.out.println(week);
}
}
}
enum Week {
// 定义Week的枚举对象
MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"),
FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期日");
private String name;
private Week(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
6.Enum使用细节
- 使用 enum 关键字后,就不能再继承其它类了,因为 enum 会隐式继承 Enum,而 Java 是单继承机制
- 枚举类和普通类一样,可以实现接口,如下形式
enum 类名 implements 接口1, 接口2{}
注解
- 注解(Annotatino)也称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息
- 在 JavaSE 中,注解的使用目的比较简单,例如:
- 标记过时的功能
- 忽略警告等
- 在 JavaEE 中,注解占据了更重要的角色,例如:
- 配置应用程序的任何切面
- 代替java EE旧版中所遗留的繁冗代码和XML配置等
1. 基本语法
使用 Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素
2. 三个基本的 Annotation
- @Override:限制某个方法,是重写父类的方法,该注解只能用于方法
- 如果的确写了 @Override 注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过;如果没有构成重写,则编译错误【语法校验】
// @Target 是修饰注解的注解,称为 元注解
@Target(ElementType.METHOD) // 说明只能修饰方法
@Retention(RetentionPolicy.SOURCE)
public @interface Override { // @interface 不是 interface,是注解类。 jdk5.0 之后加入的
}
- @Deprecated:用于表示某个程序元素(类,方法等)已过时
- 可以做版本升级过渡使用
public class Deprecated_ {
public static void main(String[] args) {
A a = new A();
a.hi();
System.out.println(a.n1);
}
}
// 老韩解读
// 1. @Deprecated 修饰某个元素,表示该元素已经过时
// 2. 即不再推荐使用,但是仍然可以使用
@Deprecated
class A{
@Deprecated
public int n1 = 10;
@Deprecated
public void hi(){
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
/**
* Returns the version in which the annotated element became deprecated.
* The version string is in the same format and namespace as the value of
* the {@code @since} javadoc tag. The default value is the empty
* string.
*
* @return the version string
* @since 9
*/
String since() default "";
/**
* Indicates whether the annotated element is subject to removal in a
* future version. The default value is {@code false}.
*
* @return whether the element is subject to removal
* @since 9
*/
boolean forRemoval() default false;
}
- @SuppressWarnings:抑制编译器警告
@SuppressWarnings("all")
public class SuppressWarnings_ {
// 老韩解读
// 1) 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings注解来抑制警告信息
// 2) 在{""}中,可以写入你希望抑制(不显示)警告信息
// 3) 可以抑制的警告类型在如下文档
// 4) 关于 SuppressWarnings 使用范围是和你放置的位置相关
public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mary");
int i;
System.out.println(list.get(1));
}
}
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/**
* The set of warnings that are to be suppressed by the compiler in the
* annotated element. Duplicate names are permitted. The second and
* successive occurrences of a name are ignored. The presence of
* unrecognized warning names is <i>not</i> an error: Compilers must
* ignore any warning names they do not recognize. They are, however,
* free to emit a warning if an annotation contains an unrecognized
* warning name.
*
* <p> The string {@code "unchecked"} is used to suppress
* unchecked warnings. Compiler vendors should document the
* additional warning names they support in conjunction with this
* annotation type. They are encouraged to cooperate to ensure
* that the same names work across multiple compilers.
* @return the set of warnings to be suppressed
*/
String[] value(); // 设置一个数组,比如:{"rawtypes", "unchecked", "unused"}
}
@SuppressWarning 中的属性介绍以及属性说明
all,抑制所有警告
boxing,抑制与封装/拆装作业相关的警告
cast,抑制与强制转型作业相关的警告
dep-ann,抑制与淘汰注释相关的警告
deprecation,抑制与淘汰的相关警告
fallthrough,抑制与switch陈述式中遗漏break相关的警告
finally,抑制与未传回finally区块相关的警告
hiding,抑制与隐藏变数的区域变数相关的警告
incomplete-switch,抑制与switch陈述式(enum case)中遗漏项目相关的警告
javadoc,抑制与javadoc相关的警告
nls,抑制与非nls字串文字相关的警告
null,抑制与空值分析相关的警告
rawtypes,// 忽略没有指定泛型的警告(传参时没有指定泛型的警告错误)
resource,抑制与使用Closeable类型的资源相关的警告
restriction,抑制与使用不建议或禁止参照相关的警告
serial,抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告
static-access,抑制与静态存取不正确相关的警告
static-method,抑制与可能宣告为static的方法相关的警告
super,抑制与置换方法相关但不含super呼叫的警告
synthetic-access,抑制与内部类别的存取未最佳化相关的警告
sync-override,抑制因为置换同步方法而遗漏同步化的警告
unchecked,抑制与未检查的作业相关的警告
unqualified-field-access,抑制与栏位存取不合格相关的警告
unused,抑制与未用的程式码及停用的程式码相关的警告
3. 四种元注解
JDK 的元 Annotation 用于 修饰其它 Annotation
-
Retention(保留):指定注解作用范围
- SOURCE【源码】
- 编译器使用后,直接丢弃这种策略的注解
- CLASS【类】
- 编译器把注解记录在 class 文件中,当运行 Java 程序时,JVM不会保留注解。这是默认值
- RUNTIME【运行时】
- 编译器把注解记录在 class 文件中,当运行 Java 程序时,JVM会保留注解。程序可以通过反射获取该注解
// 包含一个 RetentionPlicy 类型的成员变量 // 使用 @Rentention 时,必须为该 value 成员变量指定值 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy(策略) */ RetentionPolicy value(); }
- SOURCE【源码】
-
Target:指定注解可以在哪些地方使用
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); } // 看下 ElementType 可以取哪些值
-
Documented:指定该注解是否会在 javadoc 提取成文档,即:在生成文档时,可以看到该注解
- 说明:定义为 Documented 的注解必须设置 Retention 值为 RUNTIME
// 看下 Deprecated中有 @Decumented // 在某个方法上写了 Deprecated时,在生成 javadoc时,这个 Deprecated 会被保留在 javadoc 文档中 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE}) public @interface Deprecated {
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
-
Inherited:子类会继承父类注解