枚举和注解
对于特有的季节对象,季节的值只有四个(春夏秋冬),这些值一般都是只读,且不需要修改的
枚举是一组常量的集合,属于一种特殊的类,里面只包含一组有限的特点对象
自定义枚举类
- 不需要提供
setXxx
方法,因为枚举对象值通常为只读,不能在外界增加或者减小,并不提供修改 - 自定义枚举类的构造器是私有化的
- 对枚举对象/属性使用
final+static
共同修饰,实现底层优化 - 枚举对象名通常使用全部大写,即常量命名的规范
- 枚举对象根据需要,也可以有多个属性
public class Enumeration {
public static void main(String[] args) {
System.out.println(Season.AUTUMN); // 直接使用类
}
}
// 自定义枚举类
class Season {
private String name;
private String desc;
// 在Season内部,直接创建所有的固定对象
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("冬天", "寒冷");
// 构造器私有化,在外界就不可以new一个对象了,在类的内部还是可以使用的
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
// 只留下get相关的方法,去掉set相关的方法,防止属性被修改
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
// 打印对象的信息
@Override
public String toString() {
return "Season{" + "name='" + name + '\'' +
", desc='" + desc + '\'' + '}';
}
}
关键字实现枚举类
使用enum
关键字实现枚举类
public class Enumeration {
public static void main(String[] args) {
System.out.println(Season.AUTUMN); // 直接使用类
}
}
// 使用关键字实现枚举类,使用enum关键字替代class
enum Season {
// 在Season内部,直接创建所有的固定对象,通过常量名(实参列表) 进行简化 实参列表和构造器关联
// 使用枚举的时候,要求将定义的常量对象写在类的最前面,否则会报错
SPRING("春天", "温暖"), SUMMER("夏天", "炎热"), AUTUMN("秋天", "凉爽"), WINTER("冬天", "寒冷"); // 多个常量对象,中间使用逗号间隔
private String name;
private String desc;
// 构造器私有化,在外界就不可以new一个对象了,在类的内部还是可以使用的
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
// 只留下get相关的方法,去掉set相关的方法,防止属性被修改
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
// 打印对象的信息
@Override
public String toString() {
return "Season{" + "name='" + name + '\'' +
", desc='" + desc + '\'' + '}';
}
}
注意事项:
- 当我们使用
enum
关键字开发一个枚举类时,默认会继承Enum
类,而且是一个final
类 - 传统的
public static final Season SPRING = new Season("春天", "温暖");
对象创建简化为SPRING("春天", "温暖");
,这里需要知道,其调用哪个构造器 - 如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略
- 当有多个枚举对象时,使用逗号间隔,最后一个用分号结尾
- 枚举对象必须放在枚举类的首行
枚举常用的方法
使用enum
关键字时,会隐式的继承Enum
类,我们可以使用Enum
类的相关方法,常用的方法有:
方法名 | 描述 |
---|---|
valueOf | 传递枚举类型的Class 对象和枚举常量名称给静态方法valusOf ,会得到与参数匹配的枚举常量 |
toString | 得到当前枚举常量的名称。可以通过重写这个方法,来使得到的结果更易读 |
equals | 在枚举类型中可以直接使用== 来比较两个枚举类常量是否相等。Enum 提供的这个equals() 方法,也是可以直接使用== 实现的。它的存在是为了在Set 、List 、Map 中使用。注意,equals() 是不可变的 |
hashCode | Enum 实现了hashCode() 来和equals() 保持一致,它也是不可变的 |
name | 得到当前枚举常量的名称,但是更建议使用toString 方法 |
ordinal | 得到当前枚举常量的次序 |
compareTo | 枚举类型实现了Comparaable 接口,这样可以比较两个枚举常量的大小/编号(按照声明的顺序排列) |
clone | 枚举类型不能被Clone ,为了防止子类实现克隆方法,Enum 实现了一个仅抛出CloneNotSupportedException 异常的不变Clone() |
各种方法的使用演示:
public static void main(String[] args) {
Season autumn = Season.AUTUMN;
// 演示name方法
System.out.println(autumn.name()); // AUTUMN 输出枚举对象的名称
// 演示ordinal方法
System.out.println(autumn.ordinal()); // 2 输出枚举对象的次序/编号,从0开始编号
// values方法,返回Season数组,包含定义的所有枚举对象,可以从反编译找到values方法
Season[] values = Season.values();
for(Season season: values) { // 增强for循环,依次从values数组中取出数据,赋值给season
System.out.println(season); // 输出四个枚举对象
}
// 演示valueOf方法:将字符串转换成枚举对象,要求字符串必须为已有的常量,否则报错
// 执行流程:1.根据给定的字符串到Season的枚举类对象中去查找
// 2.如果找到了,就返回,如果没有找到,就报错
Season autumn1 = Season.valueOf("AUTUMN");
System.out.println(autumn1); // 这里的autumn1和之前的autumn指向的是同一个对象
// 演示compareTo方法
// 将前后两个对象的枚举编号进行比较
// 结果返回前面的编号减去后面的编号 Season.AUTUMN的编号 - Season.SUMMER的编号
System.out.println(Season.AUTUMN.compareTo(Season.SUMMER)); // 1
}
}
// 使用关键字实现枚举类,使用enum关键字替代class
enum Season {
SPRING("春天", "温暖"), SUMMER("夏天", "炎热"), AUTUMN("秋天", "凉爽"), WINTER("冬天", "寒冷");
private String name;
private String desc;
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 + '\'' + '}';
}
}
enum
实现接口
使用enum
关键字后,就不能再继承其他类了,因为enum
会隐式的继承Enum
类,而Java
是但继承机制的
枚举类和普通类一样,可以实现接口:enum 类名 implements 接口1, 接口2 {}
interface IPlaying {
public void playing();
}
enum Music implements IPlaying {
CLASSICMUISC;
@Override
public void playing() {
System.out.println("播放音乐");
}
}
// 主函数调用
Music.CLASSICMUISC.playing(); // 播放音乐
注解
注解也被称为元数据(Metadata
),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息
在JavaSE
中,注解的使用目的比较简单,如标记过时的功能,忽略警告等。在JavaEE
中注解占据了更重要的角色,如用来配置应用程序的任何切面,代替JavaEE
旧版中遗留的冗余代码和XML
配置等
使用注解时,需要在其前面加上@
符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素
@Override
@Override
注解用于限定某个方法,是重写父类方法,该注解只能用于方法(不能修饰其他类、包、属性等)
class Father { // 父类
public void fly() {
System.out.println("Father fly...");
}
}
class Son extends Father { // 子类
@Override
// @Override注解放在fly方法上,表示子类的fly重写了父类的fly方法
// 但是这里如果没有写@Override,还是对父类的fly方法进行重写
// @Override注解的价值是做了语法的校验:如果写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没有重写,则编译错误
public void fly() {
System.out.println("Son fly...");
}
@Override // 报错,父类没有say方法,不构成重写
public void say() {
System.out.println("Son fly...");
}
}
对于@Override
的源码定义:
@Target(ElementType.METHOD) // @Target表示注解可以放在哪些元素上,ElementType.METHOD修饰方法
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@interface
表示的是Override
是一个注解,@interface
在jdk5.0
之后加入
@Target
是修饰注解的注解,称之为元注解
@Deprecated
@Deprecated
注解用于表示某个程序元素(类、方法、字段、包和参数等)已过时(过时不能代表不能使用,被修饰已过时的元素,还是可以使用的)
@Deprecated
可以做版本升级过渡使用
@Deprecated
// @Deprecated修饰某个元素,表示该元素已经过时
// 对于过时的元素,即不在推荐使用,但是仍然可以使用,使用过时元素,其元素中间会出现一条划线
class A {
public int n1 = 10;
@Deprecated
public void hi() {}
}
对于@Deprecated
的源码定义:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@Target
中的内容依次是:构造器、属性字段、局部变量、方法、包、参数和类(类型)
@SuppressWarnings
@SuppressWarnings
注解用于抑制编译器警告
代码编译器中,有时会出现奇怪的警告,如果我们不想让其出现编译器警告,我们可以使用@SuppressWarnings
注解进行注释,让警告信息不显示
@SuppressWarnings
中的属性介绍以及类型说明
命令 | 描述 |
---|---|
all | 抑制所有警告 |
boxing | 抑制与封装/拆装作业相关的警告 |
cast | 抑制与强制转型作业相关的警告 |
dep-ann | 抑制与淘汰注释相关的警告 |
deprecation | 抑制与淘汰的相关警告 |
fallthrough | 抑制与switch 陈述式中遗漏break 相关的警告 |
finally | 抑制与未传回finally 区块相关的警告 |
hiding | 抑制与隐藏变数的区域变数相关的警告 |
incomplete-switch | 抑制与switch 陈述式(enum case )中遗漏项目相关的警告 |
javadoc | 抑制与javadoc 相关的警告 |
nls | 抑制与非nls 字串文字相关的警告 |
null | 抑制与空值分析相关的警告 |
rawtypes | 抑制与使用raw 类型相关的警告 |
resource | 抑制与使用Closeable 类型的资源相关的警告 |
restriction | 抑制与使用不建议或禁止参照相关的警告 |
serial | 抑制与可序列化的类别遗漏serialVersionUID 栏位相关的警告 |
static-access | 抑制与静态存取不正确相关的警告 |
static-method | 抑制与可能宣告为static 的方法相关的警告 |
super | 抑制与置换方法相关但不含super 呼叫的警告 |
synthetic-access | 抑制与内部类别的存取未最佳化相关的警告 |
sync-override | 抑制因为置换同步方法而遗漏同步化的警告 |
unchecked | 抑制与未检查的作业相关的警告 |
unqualified-field-access | 抑制与栏位存取不合格相关的警告 |
unused | 抑制与未用的程式码及停用的程式码相关的警告 |
public class SuppressWarnings {
// @SuppressWarnings({"all"}) // 使用all一劳永逸
@SuppressWarnings({"rawtypes", "unchecked", "unused"}) // 精准抑制
public static void main(String[] args) {
List list = new ArrayList(); // 可以使用rawtypes抑制
list.add("a"); // 可以使用unchecked抑制
list.add("b"); // 可以使用unchecked抑制
list.add("c"); // 可以使用unchecked抑制
int i; // 可以使用unused抑制
System.out.println(list.get(1));
}
}
我们可以在编辑器右边滚动条中,通过鼠标悬停查看具体的警告类型,再精准注释即可
关于
SuppressWarnings
作用范围,是和放置的位置相关,上述注解放置在main
方法上,那么抑制警告的范围就是main
方法,只管理当前的代码块,其他方法就不受抑制注解的影响,通常我们将抑制注解语句放置在语句,方法或者类上
对于@SuppressWarnings
的源码定义:
@Target({TYPE, FIELD, METHOD, PACKAGE, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
string[] value();
}
该注解类有一个数组,数组可以接收传入的内容
元注解
修饰注解的注解叫做元注解,在源码中会出现,常见的元注解有:
Retention
:指定注解的作用范围,值有三种:SOURCE
、CLASS
、RUNTIME
(源码级别,类,运行时)只能用于修饰一个注解定义,用于指定该注解可以保留多长时间
有三种策略值:保留策略从上到下越来越长
RetentionPolicy.SOURCE
:编译器使用后,直接丢弃这种策略的注释RetentionPolicy.RUNTIME
:编译器将把注解记录在class
文件中,当运行Java
程序时,JVM
不会保留注解,这个策略值是默认值RetentionPolicy.SOURCE
:编译器将把注解记录在class
文件中,当运行Java
程序时,JVM
会保留注解,程序可以通过反射获取该注释
Target
:指定注解可以在哪些地方使用具体的值为:
CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE
构造器、属性字段、局部变量、方法、包、参数和类(类型)
Documented
:指定该注解是否在javadoc
体现,即在生成文档时,可以看到该注解Inherited
:子类会继承父类注解被
Inherited
修饰的注解将具有继承性,如果某个类使用了该元注解进行修饰,则其子类将自动具有该注解