枚举和注解
枚举
基础知识
枚举是一组常量的集合。枚举属于一种特殊的类,里面只包含一组有限的特定的对象。
其实枚举类是可以通过传统写法自定义的,写法为:
- 构造器私有化
- 不提供set方法
- 在类内部预先初始化好静态的实例,并且对外暴露
代码略,直接学习如何创建真正的枚举。
- 使用enum关键字来代替class
- 直接写FALL(“秋天”,“凉爽”),效果上等价于 public static final Season FALL = new Season(“秋天”,“凉爽”);
- 如果有多个常量对象,使用逗号间隔即可
- 使用 enum 实现枚举,必须把定义的常量对象写在枚举类的最前面
- 使用无参构造器时,可以把括号也省略,直接写FALL,举例
FALL, SPRING, SUMMER, WINTER;
public class Enum {
public static void main(String[] args) {
System.out.println(Season.SPRING);
}
}
enum Season{
SPRING("春天","温暖"), SUMMER("夏天","炎热"),
FALL("秋天","凉爽"), WINTER("冬天","寒冷");
private String name;
private String desc;
Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
.java文件可以用 Javac 编译成.class文件,.class文件也可以用 Javap 反编译成字节码文件,通过观察先编译再反编译的结果,可以看到很多隐藏的细节。
对于 Seanon 类,反编译得到的代码如下
Compiled from "Enum.java"
final class hspedu.inner.enumer.Season extends java.lang.Enum<hspedu.inner.enumer.Season> {
public static final hspedu.inner.enumer.Season SPRING;
public static final hspedu.inner.enumer.Season SUMMER;
public static final hspedu.inner.enumer.Season FALL;
public static final hspedu.inner.enumer.Season WINTER;
public static hspedu.inner.enumer.Season[] values();
public static hspedu.inner.enumer.Season valueOf(java.lang.String);
public java.lang.String toString();
static {};
}
值得关注的细节有:
- 枚举类是 final 类型的,因此不可被继承
- 枚举类默认继承 java.lang.Enum 类,因此不可继承其他类
- 每一个常量都默认是 public static final 类型的
简单练习
第一题
判断语法正误
enum Gender {
BOY, GIRL;
}
答案:
语法没有错,相当于一个“没有属性,只含有无参构造器”的枚举类
第二题
判断输出什么
enum Gender2 {
BOY, GIRL;
}
Gender2 boy = Gender2.BOY;
Gender2 boy2 = Gender2.BOY;
System.out.println(boy);
System.out.println(boy2 == boy);
答案:
BOY
true
分析:
首先,枚举类本质也是类,所以Gender2 boy = Gender2.BOY;这种写法肯定是对的
boy相当于拿到了枚举类里的public static final常量,因此boy和boy2肯定是一样的
System.out.println(boy)相当于调用Gender2的toString方法,但是显然它没有,就去找父类的toString方法,父类是java.lang.Enum(前面有提到)
Enum类的成员方法
分别是 name、ordinary、values、valueOf、compareTo方法,建议自己写一写用一用,方法的效果都写在代码里了:
public class Enum {
public static void main(String[] args) {
Season spring = Season.SPRING;
// name方法,建议优先使用toString,效果类似
System.out.println(spring.name());
System.out.println(spring);
// ordinary方法,输出该枚举对象的序号,从0开始
System.out.println(spring.ordinal());
// values方法,返回所有的枚举对象
Season[] values = Season.values();
for (Season value : values) {
System.out.println(value);
}
// valueOf方法,返回指定名称的枚举对象
Season valueOf = Season.valueOf("FALL");
System.out.println(valueOf);
// compareTo方法,比较两个枚举对象,返回它们的序号之差,在这里是spring的序号 - valueOf的序号
System.out.println(spring.compareTo(valueOf));
}
}
enum Season{
SPRING("春天","温暖"), SUMMER("夏天","炎热"),
FALL("秋天","凉爽"), WINTER("冬天","寒冷");
private String name;
private String desc;
Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
}
简单练习2
声明 Week 枚举类,其中包含星期一至星期日的定义;
MONDAY, TUESDAY, WEDNESDAY, THURSDAY,FRIDAY, SATURDAY, SUNDAY;
使用 values 返回所有的枚举数组,并遍历,要求打印值为“星期一”而不是“MONDAY”
public class Enum {
public static void main(String[] args) {
Week[] values = Week.values();
for (Week value : values) {
System.out.println(value);
}
}
}
enum Week{
MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"),
FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期日");
private String name;
Week(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
Enum类的接口
- Enum类本身已经有了继承关系,因此不能继承其他类
- 但作为一个类,它仍然可以实现接口
interface Playing {
void play();
}
enum Music implements Playing {
HARD_ROCK, POP, CLASSIC, ROCK, JAZZ;
@Override
public void play() {
}
}
注解
最基本的修饰符
最基本的三个修饰符分别是:
- Override:用来限定某个方法必须重写父类的方法,只能用于方法
- SuppressWarnings:抑制编译器的警告
- Deprecated:用来表示某个程序元素(比如类或者方法)已经过时
Override
其实对于正确的方法重写来说,加不加这个修饰符都可以。
但如果加了的话,编译器会检查你是否有正确地重写这个方法。如果不正确的话会报错,产生编译错误。
Override的源码如下,从 @Target(ElementType.METHOD) 上可以看出,这个修饰符只能用在方法上。
@interface 修饰的类都是注解类。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
顺带一提,@Target 是修饰注解的注解,也称为元注解。
Deprecated
用 @Deprecated 修饰符修饰的元素,暗示其已经过时了,不推荐再继续使用,但其实可以使用。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
String since() default "";
boolean forRemoval() default false;
}
从源码可以看出,该修饰符可以修饰方法、字段、包、参数等。
该关键字一般用于 JDK 版本更迭时,给过时的方法打上标注。
SuppressWarnings
用来抑制编译器警告,在 ( ) 中可以填抑制的警告类型。
因为懒得打字,我直接把文档粘在这:
参数名称 作用描述all
抑制所有警告。
unchecked
抑制与泛型相关的“未经检查的操作”警告,例如在使用原始类型时。
deprecation
抑制使用了 @Deprecated 标记的过时类或方法的警告。
rawtypes
抑制使用了泛型但未指定具体类型的“原始类型”警告。
unused
抑制代码中存在但未被使用的变量、方法或类的警告。
serial
抑制可序列化的类未定义 serialVersionUID 的警告。
null
抑制与空值分析相关的警告(如潜在的空指针)。
cast
抑制与强制类型转换操作相关的警告。
fallthrough
抑制在 switch 语句中缺少 break 而导致“直通”的警告。
finally
抑制 finally 块无法正常返回的警告。
boxing
抑制与自动装箱(boxing)和拆箱(unboxing)操作相关的警告。
static-access
抑制不正确的静态成员访问方式的警告。
dep-ann
抑制使用过时注解的警告。
incomplete-switch
抑制 switch 语句中未覆盖所有枚举常量的警告。
javadoc
抑制与 Javadoc 相关的警告。
synthetic-access
抑制内部类访问未优化的警告。
resource
抑制与资源(如 Closeable)使用相关的警告。
restriction
抑制使用了不建议或禁止引用的警告。
使用示例,代码如下:
@SuppressWarnings({"all"})
enum Music implements Playing {
HARD_ROCK, POP, CLASSIC, ROCK, JAZZ;
@Override
public void play() {
}
}
@SuppressWarnings是没有对使用位置限制的,从源码中也可以看出,它没有 @Target 去限制,源码如下:
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
元注解
注解的注解,看源码时可能遇到,没那么重要,快速过一下。
四种元注解:
- Retention:指定注解的作用范围,三种值SOURCE,CLASS,RUNTIME
- Target:指定注解可以在哪些地方使用
- Documented:指定该注解是否会在javadoc体现,即在生成文档的时候,可以看到该注解
- Inherited:子类会继承父类注解
这部分我战略性跳过了,稍微不太好理解,也有点深入了。
1 个帖子 - 1 位参与者