枚举与注解
- 一、枚举(enumeration)
- 1.1 自定义类实现枚举
- 1.2 enum关键字实现枚举
- 1.2.1 enum的注意事项
- 1.2.2 enum的使用练习
- 1.2.3 enum的常用方法
- 1.2.4 enum的使用细节及注意事项
- 1.2.5 enum练习
- 二、注解(Annotation)
- 2.1 @Override(☆)
- 2.2 @Deprecated(☆)
- 2.3 @SuppressWarnings
- 2.4 元 Annotation
- 2.4.1 @Retention
- 2.4.2 @Target
- 2.4.3 @Documented
- 2.4.4@Inherited
- 注解小结
一、枚举(enumeration)
枚举(enum):一组常量的集合,属于一种特殊的类,里面只包含一组有限的特定的对象。
● 枚举的两种实现方式:
- 自定义类实现枚举
- 使用enum关键字实现枚举
1.1 自定义类实现枚举
- 不需要提供setXxx方法,因为枚举对象值通常为只读。
- 对枚举对象 / 属性使用 final + static 共同修饰,实现底层优化。
- 枚举对象通常使用全部大写,常量的命名规范。
- 枚举对象根据需要,也可以有多个属性
● 自定义枚举应用案例:
要求创建季节(Seanson)对象,给出属性、构造器等结构,再添加枚举去实现调用并输出结果。
public class Enumeration01{
public static void main(String[] args){
System.out.println(Season.AUTUMN);
System.out.println(Season.SPRING);
}
}
class Season{ //类
private String name;
private String desc; //描述
public static final Seanson SPRING = new Season("春天","温暖");
public static final Seanson WINTER = new Season("冬天","寒冷");
public static final Seanson AUTUMN = new Seanson("秋天","凉爽");
public static final Seanson SUMMER = new Seanson("夏天","炎热");
//1.将构造器私有化,目的是防止直接创建对象
//2.去掉setXxx方法,防止属性被修改
//3.在Season内部,直接创建固定的对象
//4.优化:加入final修饰符
private Season(String name,String desc){
this.name = name;
this.desc = desc;
}
public String getName(){
return name;
}
public String getDesc(){
return desc;
}
public String toString(){
return "Seanson{"+
"name=" + name + '\'' +
",desc=" + desc + '\'' +
'}';
}
}
运行效果
● 自定义类实现枚举小结
- 构造器私有化
- 本类内部创建一组对象【比如上面代码的春夏秋冬】
- 对外暴露对象(通过对象添加public final static 修饰符)
- 可以提供get方法,但是不要提供set方法
1.2 enum关键字实现枚举
使用enum来实现前面的枚举案例
如果使用了enum来实现枚举类
- 使用关键字 enum 替代 class
- public static final Season SPRING = new Season(“春天”,“温暖”)可以替换为:
SPRING(“春天”,“温暖”) ==> 常量名(参数列表)- 如果有多个常量(对象),使用 " , " 号间隔即可
- 如果使用 enum 来实现枚举,要求将定义常量对象,写在最前面。
public class Enumeration01{
public static void main(String[] args){
System.out.println(Season.AUTUMN);
System.out.println(Season.SPRING);
}
}
enum Season{ //类
//public static final Season SPRING = new Season("春天","温暖")可以替换为:
//SPRING("春天","温暖") ==> 常量名(参数列表)
SPRING("春天","温暖"),WINTER("冬天","寒冷"),AUTUMN("秋天","凉爽"),SUMMER("夏天","炎热");
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;
}
public String toString(){
return "Seanson{"+
"name=" + name + '\'' +
",desc=" + desc + '\'' +
'}';
}
}
运行效果
1.2.1 enum的注意事项
- 当我们使用 enum 关键字开发一个枚举时,默认会继承Enum类,而且是一个final类。
- 传统的 public static final Season SPRING = new Season(“春天”,“温暖”);简化成SPRING(“春天”,“温暖”),这里必须知道,它调用的是哪个构造器。(这里指的是:有两个字符串的构造器)
- 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略。【如果我们使用的是无参构造器,创建常量对象,则可以省略( )】
SUMMER("夏天","炎热"),Season;
- 当有多个枚举对象时,使用,间隔,最后一个分号结尾。
enum Season{
spring,summer;
}
- 枚举对象必须放在枚举类的行首。
enum Season{
spring,summer;
构造器...
}
1.2.2 enum的使用练习
- 下面代码是否正确,并说明表示的含义。
enum Gender{
BOY,GIRL;
}
答案:
上面的语法是正确的;有一个枚举类:Gender,没有属性;有两个枚举对象 BOY,GIRL,使用的是无参构造器创建。
如果修改一下上面的代码,添加一个有参构造器的话,结果又会怎么样?
enum Gender{
BOY,GIRL;
private Gender(String name){}
}
答案:
因为添加了构造器,程序检测时就会把原本默认的构造器给覆盖(把原本默认的无参构造器给覆盖了),变为有参构造器,所以添加了构造器的Gender类需要加上无参构造器。
enum Gender{
BOY,GIRL;
private Gender(String name){}
private Gender(){}
}
1.2.3 enum的常用方法
在使用关键字enum时,会隐式继承Enum类,这样我们就可以使用Enum类相关的方法。
常用的enum方法有:
方法名 | 详细描述 |
---|---|
valueOf | 传递枚举类型的Class对象和枚举常量名称给静态方法valueOf,会得到与参数匹配的枚举常量 |
toString | 得到当前枚举常量的名称。一般可以通过重写这个方使得到的结果更易读。 |
equals | 在枚举类型中可以直接使用 " == " 来比较两个枚举常量是否相等。 |
hashCode | Enum实现了hashCode()来和equals()保持一致。它也是不可变的。 |
getDeclaringClass | 得到枚举常量所属枚举类型的Class对象。可以用它来判断两个枚举常量是否属于同一个枚举类型。 |
name | 得到当前枚举常量的名称。建议先试用toString()。 |
ordinal | 得到当前枚举常量的次序 |
compareTo | 枚举类型实现了Comparable接口,这样可以比较两个枚举常量的大小(按照声明的顺序排列) |
clone | 枚举类型不能被Clone,为了防止子类实现克隆方法,Enum实现了一个仅抛出CloneNotSupporttedException异常的不变Clone()。 |
1. name:得到当前枚举常量的名称。建议先试用toString()。
//使用Season2 枚举类,来演示各种方法
Season2 autumn = Season2.AUTUMN;
//输出枚举对象的名字
System.out.println(autumn.name());
运行效果
2. ordinal:输出的是该枚举对象的次序/编号,从0开始编号
//ordinal() 输出的是该枚举对象的次序/编号,从0开始编号
//AUTUMN 枚举对象是第三个 , 因此输出2
System.out.println(autumn.ordinal());
运行效果
这里补充一个增强for
//这里补充一个增强for
int[] nums = {1,2,9};
//普通的for循环
System.out.println("=====普通的for=====");
for(int i = 0; i < nums.length; i++){
}
System.out.println("=====增强的for=====");
//执行流程是:依次从nums数组中取出数据,赋给i,如果取出完毕,则退出for
for(int i : nums){
System.out.println("i=" + i);
}
}
运行效果
3. values :从反编译可以看出values方法,返回Season2[]含有定义的所有枚举对象
注意:Java编译器会自动在enum类型中插入values方法,Enum类里面没有
//从反编译可以看出values方法,返回Season2[]含有定义的所有枚举对象
Season2[] values = Season2.values();
System.out.println("===遍历输出枚举对象(增强for)====");
for(Season2 season: values){//增强for循环
System.out.println(season);
}
运行效果
4. values:返回当前枚举类中所有的常量
//从反编译可以看出,values方法,返回Season2[]
//含有定义的所有枚举对象
Season2[] values = Season2.values();
System.out.println("===遍历取出枚举对象(增强for)====");
for(Season2 season : values){
System.out.println(season);
}
运行效果
5. valuesOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常!
valuesOf的执行流程:
- 根据你输入的值,到枚举对象去查找。
- 如果找到了,就返回,如果没有找到,则报错。
Season2 autumn1 = Season2.valuesOf("AUTUMN");
system.out.println("autumn1=" + autumn1);
system.out.println(autumn == autumn1);
运行结果
6. compareTo:比较两个枚举常量,比较的就是位置号(编号)!
compareTo运行流程:
就是把一个枚举对象与另一个枚举对象的编号进行比较
//这里的compareTo就是把Season2.AUTUMN枚举对象的编号和Season2.SUMMER枚举对象的编号进行比较
//而此时的Season2.AUTUMN的编号为:[2] - Seaso.SUMMER的编号:[3],所以得到结果为:-1
System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));
运行效果
1.2.4 enum的使用细节及注意事项
- 使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制。
- 枚举类和普通类一样,可以实现接口,形式如下:
enum 类名 implements 接口1,接口2{}
enum实现的枚举类,仍然是一个类,所以还是可以实现接口的。
interface IPlaying{
public void playing();
}
enum Music implements IPlaying{
CLASSICMUISC;
public void playing(){
System.out.println("播放音乐...");
}
}
1.2.5 enum练习
题目如下:
- 声明Week枚举类,其中包含星期一至星期日的定义;
MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY、SUNDAY; - 使用values返回所有的枚举数组,并遍历,输出下图的效果。
代码实现:
public static void main(String[] args){
//获取到所有的枚举对象,即数组
Week[] weeks = Week.values();
//遍历,使用增强for
System.out.println("===所有星期的信息如下===");
for(Week week : weeks){
System.out.println(vweek + '/n');
}
}
Week class{
//定义Week的枚举对象
MONDAY("星期一"),TUESDAY("星期二"),WEDNESDAY("星期三"),THURSDAY("星期四"),FRIDAY("星期五"),SATURDAY("星期六"),SUNDAY("星期日");
private String name;
//构造器
private Week(String name){
this.name = name;
}
//重写toString
public String toString(){
return name;
}
}
运行效果
二、注解(Annotation)
● 注解的理解
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释:包类、方法、属性、构造器、局部变量等数据信息。
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
- 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
● Annotation的使用
使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素
● 三个基本的Annotation:
- @Override:限定某个方法,是重写父类方法,该注解只能用于方法。
- @Deprecated:用于表示某个程序元素(类、方法等)已过时
- @SuppressWarnings:抑制编译器警告
2.1 @Override(☆)
@Override:限定某个方法,是重写父类方法,该注解只能用于方法
- Overrdie注解,表示指定重写父类的方法(从编译层面验证),如果父类没有此方法,则会报错。
- 但是如果没有写Override,它依旧还是会重写父类。
- 如果你写了@Override注解,编译器就会去检查该方法是否真的重写了。如果的确重写了,则编译通过,如果没有构成重写,则编译错误。
- @Override 只能修饰方法,不能修饰其他类,包,属性等等。
- @Target是修饰注解的注解,称为元注解。
Override使用演示
class Father{ //父类
public void fly(){
System.out.println("Father fly...");
}
}
class Son extends Father{ //子类
//1.Overrdie注解,放在fly方法上,表示子类的fly方法时重写了父类的fly
//2.这里如果没有写@Override,还是重写了父类fly
//3.如果你写了@Override注解,编译器就会去检查该方法是否真的重写了
//父类的方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误
//4.@Override的定义
//如果发小@interface,则表示一个注解类
/*
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override{
}
*/
@Override //说明
public void fly(){
System.out.println("Son fly....");
}
}
2.2 @Deprecated(☆)
@Deprecated:用于表示某个程序元素(类、方法等)已过时
@Deprecated的说明:
- 用于表示某个程序元素(类,方法等)已过时
- 可以修饰方法、类、字段、包、参数等等
- 查看@Deprecated的注解:@Target(value={CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER、TYPE})
- @Deprecated的作用可以做到新旧版本的兼容和过渡
public class Deprecated01{
public static void main(String[] args){
A a = new A();
a.hi();
System.out.println();
}
}
@Deprecated
class A {
public int n1 = 10;
public void hi(){
}
}
如果在n1和hi的上面添加上Deprecated,则在你使用n1和hi的时候,会有中划线。
修改后:
@Deprecated
class A {
@Deprecated
public int n1 = 10;
@Deprecated
public void hi(){
}
}
主方法调用时的中划线:
2.3 @SuppressWarnings
@SuppressWarnings:抑制编译器警告
当我们不希望看到系统给的黄色警告时,我们可以使用@SuppressWarnings注解来抑制警告信息
● SuppressWarnings作用范围
关于SuppressWarnings作用范围是和你放置的位置相关,比如:
@SuppressWarnings放置在main方法,那么抑制警告的范围就是main
通常我们可以放置具体的语句,比如:方法、类。
@SuppressWarnings使用演示
在使用Idea工具时,往往会出现很多黄色的警告,比如下面的代码,在写完时Idea右上角就会出现黄色的三角警告:
public class SuppressWarnings01{
public static void main(String[] args){
List list = new ArrayList();
list.add("");
list.add("");
list.add("");
int i;
System.out.println(list.get(1));
}
}
三角警告
如果我们不希望看到这些警告的时候,就可以使用@SuppressWarnings注解来抑制这些警告:
//在{""}中,可以写入你希望抑制的(不显示)警告信息
public class SuppressWarnings01{
@SuppressWarnings({"rawtypes"})
public static void main(String[] args){
List list = new ArrayList();
list.add("");
list.add("");
list.add("");
int i;
System.out.println(list.get(1));
}
}
这时候就可以看见右上角的警告变少了
再加上其他的类型,就可以屏蔽掉所有的警告了。
@SuppressWarnings({"rawtypes","unchecked","unused"})
● SuppressWarnings能指定的警告类型有:
all | 抑制所有警告 |
---|---|
boxing | 抑制与封装/拆装作业相关的警告 |
cast | 抑制与强制转换型作业相关的警告 |
dep-ann | 抑制与淘汰注释相关的警告 |
deprecation | 抑制与淘汰的相关警告 |
fallthrough | 抑制与switch陈述式中遗漏break相关的警告 |
finally | 抑制与未传回finally区块相关的警告 |
hiding | 抑制与隐藏变数的取余变数相关的警告 |
incomplete-switch | 抑制与switch陈述式(enum case)中遗漏项目相关的警告 |
javadoc | 抑制与javadoc相关的警告 |
nls | 抑制与非nls子串文字相关的警告 |
null | 抑制与空值分析相关的警告 |
rawtypes | 抑制与eaw类型相关的警告 |
resource | 抑制与使用Closeable类型相关的资源相关的警告 |
restnicthon | 抑制与使用不建议或进制参照相关的警告 |
serial | 抑制与可序列化的类别遗漏serialVresionUID栏位相关的警告 |
static-access | 抑制与静态存取不正确相关的警告 |
static-method | 抑制与可能警告为static的方法相关的警告 |
super | 抑制与置换方法相关但不含super呼叫的警告 |
synthetic-access | 抑制与内部类别的存取未最佳化相关的警告 |
sync-override | 抑制因为置换同步方法面遗漏同步化的警告 |
unchecked | 抑制与未检查的作业相关的警告 |
unqualified-field-access | 抑制与栏位存取不合格相关的警告 |
unused | 抑制与未用的程式码及停用的程式码相关的警告 |
● @SuppressWarnings注解的各种值
- unchecked : 忽略没有检查的警告
- rawtypes : 忽略没有指定泛型的警告(传参时没有指定泛型的警告错误)
- unused : 忽略没有使用某个变量的警告错误
- @SuppressWarnings : 可以修饰的程序元素为:
查看@Target
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings{
String[] value();
}
(1) 可修饰的值就为大括号内的值:TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE
(2)该注解类有数组 String[] values() 设置一个数组,比如{“rawtypes”,“unchecked”,“unused”}
- 生成@SuppressWarnings时,不用背,直接点击左侧的黄色提示,就可以选择(注意可以指定生成的位置)
2.4 元 Annotation
● 元注解的基本介绍
JDK 的元 Annotaion 用于修饰其他 Annotation
元注解:本身作用不大,主要是看源码时知道是个什么东西。
● 元注解的种类(使用不多,了解,不用深入)
1)Retention //指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
2)Target //指定注解可以在哪些地方使用
3)Documented //指定该注解是否会在javadoc体现
4)Inherited //子类会继承父类注解
2.4.1 @Retention
@Retention:用于指定该Annotation可以保留多长时间,只能用于修饰一个Annotation定义。
@Retention包含一个RetentionPolicy类型的成员变量,使用@Retention时必须为该value成员变量指定值
● @Retention的三种值
- RetentionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注释
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override{
}
- RetentionPolicy.CLASS:编译器将把注解记录在class文件中,当运行Java程序时,JVM不会保留注解。【这是默认值】
- RetentionPolicy.RUNTIME:编译器将把注释记录在class文件中,当运行Java程序时,JVM会保留注解,程序可以通过反射获取该注解。
2.4.2 @Target
Target:用于修饰Annotation定义。
用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰程序的元素为:
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,PACKAGE,PARAMETER})
@Target也包含一个名为value的成员变量。
2.4.3 @Documented
@Documented:用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档,即在生成文档时,可以看到该注解。
注意:
定义为Documented的注解必须设置Retention值为RUNTIME。
2.4.4@Inherited
Inherited:被它修饰的Annotation将具有继承性,如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解
注意:
实际应用中,使用较少,了解即可。
注解小结
- @Override:限定某个方法,是重写父类方法,该注解只能用于方法
- @Deprecated:用于表示某个程序元素(类、方法等)已过时
- @SuppressWarnings:抑制编译器警告