文章目录
1. 枚举
1.1 为什么需要枚举
看一个例子:
public class Test {
public static void main(String[] args) {
Season spring = new Season("春天", "温暖");
Season summer = new Season("夏天", "炎热");
Season autumn = new Season("秋天", "凉爽");
Season winter = new Season("冬天", "寒冷");
autumn.setName("XXX");
autumn.setDesc("非常的热..");
Season other = new Season("红天", "~~~");
}
}
class Season{
private String name;
private String desc;//描述
public Season(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;
}
}
季节类有如下特点:
(1)它的对象是固定的4个对象:spring, summer, autumn, winter。
(2)对象的属性是只读,不需要修改的。
按这个设计类的思路,不能体现季节类的上述特点。因此,需要有更好的设计类的方式—— 枚举。
1.2 枚举(enumeration)介绍
枚举(enumeration,简写 enum):是一组常量的集合。
可以这样理解:枚举属于一种特殊的类,里面只包含有限个特定的对象。
枚举的 2 种实现方式:
(1)自定义类实现枚举。
(2)使用 enum 关键字实现枚举。
1.3 自定义类实现枚举
对 9.1 例子的优化方法 1:
public class Test {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.WINTER);
}
}
class Season{
//枚举对象根据需要可以有多个属性
private String name;
private String desc;//描述
//1. 将构造器私有化,防止在其他类中直接new
//2. 去掉set方法, 防止属性被修改
//3. 在Season内部,直接创建固定的对象,对象名大写(命名规范)
//4. static修饰,在其他类中不创建对象,直接访问
//5. final修饰,常量对象
public final static Season SPRING = new Season("春天", "温暖");
public final static Season SUMMER = new Season("夏天", "炎热");
public final static Season AUTUMN = new Season("秋天", "凉爽");
public final static Season WINTER = new Season("冬天", "寒冷");
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 + '\'' +
'}';
}
}
输出结果:
Season{name='春天', desc='温暖'}
Season{name='冬天', desc='寒冷'}
自定义类实现枚举要点:
(1)构造器私有化,防止在其他类中直接new。
(2)可以提供 get 方法,但不提供 set 方法,因为属性只读。
(3)本类内部创建一组固定的对象(如上面的:春夏秋冬),对象名大写。
(4)对象用 public final static 修饰。(static:其他类中不创建对象即可访问,final:常量)
【注】被 final 修饰的对象引用,指向的对象不能改变,对象的属性可以改变(若允许访问 set 方法)。
public class Test {
public static void main(String[] args) {
System.out.println(AA.aa);
AA.aa.setName("tom");
System.out.println(AA.aa);
//AA.aa = new AA("jerry");//错误
}
}
class AA{
private String name;
public static final AA aa = new AA("jack");
public AA(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "AA{" +
"name='" + name + '\'' +
'}';
}
}
输出结果:
AA{name='jack'}
AA{name='tom'}
1.4 enum 关键字实现枚举
对 9.1 例子的优化方法 2:
public class Test {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.WINTER);
}
}
enum Season{
//1. 使用关键字enum替代class
//2. SPRING("春天", "温暖") <==> public static final Season SPRING = new Season("春天", "温暖")
//3. 如果有多个常量(对象),使用“,”号间隔
//4. 多个常量(对象)一起写在前面,可一行或多行
//5. 如果使用的是无参构造器,创建常量对象时可以省略 ()
SPRING("春天", "温暖"), //参数传给构造器,对象名为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 关键字实现枚举要点:
(1)使用关键字 enum 替代 class。
(2)构造器私有化,防止在其他类中直接new。
(3)可以提供 get 方法,但不提供 set 方法,因为属性只读。
(4)当有多个枚举对象时,多个枚举对象一起写在前面,可一行或多行;枚举对象之间使用 ”,“ 间隔,最后有一个分号结尾。
(5)如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略。
(6)使用 enum 关键字开发一个枚举类时,默认继承 Enum 类,而且开发的枚举类是一个 final 类。可以用 javap工具来验证。
【例1】下面代码是否正确, 并说明表示的含义?
enum Gender{
BOY, GIRL;
}
上面语法正确。
有一个枚举类 Gender, 没有属性;有两个枚举对象 BOY、GIRL,使用无参构造器创建。
如果有其他构造器,必须写上无参构造器,因为有了其他构造器,系统就不再添加默认的无参构造器:
enum Gender{
BOY, GIRL;
private Gender(String name){};
private Gender(){};
}
【例2】下面代码输出什么?
public class Test {
public static void main(String[] args) {
Gender2 boy = Gender2.BOY;
Gender2 boy2 = Gender2.BOY;
System.out.println(boy);//输出BOY//本质就是调用Gender2的父类Enum的toString
System.out.println(boy2 == boy);//true
}
}
enum Gender2{//父类Enum的toString
BOY, GIRL;
//public static final Gender2 BOY = new Gender2();
}
执行 System.out.println(boy) 时,自动调用 toString 方法,Gender2 类没有,就调用父类 Enum 的 toString 方法。Enum 的 toString 方法如下:
所以,Enum 的 toString 方法返回枚举常量名(即对象名),执行 System.out.println(boy) 输出 boy 对应的 Gender2 中的常量对象名 “BOY”。
至于 boy2 == boy,因为两者指向同一个对象,所以为true。
输出结果:
BOY
true
1.5 enum 成员方法
使用关键字 enum 时,会隐式继承 Enum 类,这样就可以使用 Enum 类相关的方法。Enum 类源码定义为:
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
}
Enum 类中的构造方法:
- toString:Enum 类重写了 Object 类的 toString 方法,返回当前的枚举对象名;子类还可以再重写该方法,用于返回对象的属性信息。
- name:返回当前枚举对象名,子类不能重写该方法。
- ordinary:返回当前对象编号,默认从 0 开始。
- values:返回当前枚举类中的所有常量对象。
- valueOf:将字符串转换成枚举对象,去枚举类中查找该对象并返回。要求字符串必须为枚举类中的常量对象名,否则报异常。
- compareTo:比较两个枚举常量,返回序号之差。
Enum 类中有唯 一 一 个构造方法:
程序员不能调用此构造方法。它供编译器响应枚举类型时使用。枚举类在创建常量对象的时候自动调用了父类 Enum 的构造方法,自己的名字和编号传给父类的属性。
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public class EnumMethod {
public static void main(String[] args) {
Season autumn = Season.AUTUMN;
//枚举对象的名字
System.out.println(autumn.name());
//枚举对象的次序/编号
System.out.println(autumn.ordinal());
//获取枚举类中的所有枚举对象
//values方法 ctrl+b定位不到,class文件时反编译可以看到
Season[] values = Season.values();
for (Season season : values) {//增强for循环
System.out.println(season);
}
//根据参数“AUTUMN”到Season的枚举对象中去查找,如果找到了就返回;如果没找到就报错。
Season autumn1 = Season.valueOf("AUTUMN");
System.out.println(autumn1);
System.out.println(autumn == autumn1);
//Season.SPRING的编号[0] - Season.WINTER的编号[3]
System.out.println(Season.SPRING.compareTo(Season.WINTER));
}
}
输出结果:
AUTUMN
2
Season{name='春天', desc='温暖'}
Season{name='夏天', desc='炎热'}
Season{name='秋天', desc='凉爽'}
Season{name='冬天', desc='寒冷'}
Season{name='秋天', desc='凉爽'}
true
-3
【例】声明 Week 枚举类,其中包含星期一至星期日的定义:MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY。使用values返回所有的枚举数组,并遍历,输出如下信息。
星期一
星期二
星期三
星期四
星期五
星期六
星期日
package enum_.week;
public class TestWeek {
public static void main(String[] args) {
Week[] weeks = Week.values();
for (Week week : weeks) {
System.out.println(week);
}
}
}
package enum_.week;
public enum Week {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
private String name;
private Week(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
输出结果:
星期一
星期二
星期三
星期四
星期五
星期六
星期日
1.6 枚举类使用细节
- 枚举类不能再继承其他类,因为会隐式继承 Enum 类,而 Java 是单继承机制。
- 枚举类和普通类一样,可以实现接口。
package enum_;
public class TestMusic {
public static void main(String[] args) {
Music.CLASSIC_MUSIC.play();
}
}
interface IPlay {
void play();
}
enum Music implements IPlay{
CLASSIC_MUSIC;
@Override
public void play() {
System.out.println("正在播放古典音乐...");
}
}
输出结果:
正在播放古典音乐...
2. 注解
2.1 基本介绍
-
注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。
-
和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
-
在 JavaSE 中,注解的使用目的比较简单,例如:标记过时的功能,忽略警告等。在 JavaEE 中,注释占据了更重要的角色,了例如用来配置应用程序的任何切面,代替 JavaEE 旧版中所遗留的繁冗代码和 XML 配置等。
-
使用注解时,要在前面加上@符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素。
三个基本的注解:
@Override:表示某个方法是重写了父类的方法。
@Deprecated:表示某个程序元素(类、方法等)已经过时。
@SuppressWarnings:抑制编译器警告。
2.2 @Override 注解
- @Override 注解,表示子类的方法重写了父类的方法。
- 如果代码上构成重写但没有 ”@Override“,仍是重写。如果有 ”@Override“,编译器就检查是否真的重写了父类方法,若不构成重写,则编译错误。
- 下面是 ”@Override“ 的定义(@interface表示一个注解类)
@Override 只能修饰方法,不能修饰类、包、属性等,从 @Target 注解以可看出。@Target(ElementType.METHOD)//@Override只能修饰方法 @Retention(RetentionPolicy.SOURCE) public @interface Override { }
@Target 注解是修饰注解的注解,称为元注解。
package annotation_;
public class Override_ {
public static void main(String[] args) {
}
}
class Father{//父类
public void fly(){
System.out.println("Father fly...");
}
}
class Son extends Father {//子类
//1.@Override注解,表示子类的fly方法重写了父类的fly
//2.这里如果没有@Override,仍是重写
//3.如果有,编译器就检查是否真的重写了父类方法,若不构成重写,则编译错误
//4.下面是@Override的定义(@interface表示一个注解类)
//@Target(ElementType.METHOD)//只能修饰方法
//@Retention(RetentionPolicy.SOURCE)
//pub
lic @interface Override {
//}
@Override
public void fly() {
System.out.println("Son fly....");
}
}
2.3 @Deprecated 注解
- @Deprecated 用于表示某个程序元素(类, 方法等)已过时,即不再推荐使用,但是仍然可以使用。
- @Deprecated 注解类的源码:
@Deprecated 注解可以修饰方法、类、字段、包、参数等等。@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
@Deprecated 的作用:新旧版本的兼容和过渡。 - 在 idea 中,使用 @Deprecated 修饰的元素时会有一个中划线。
2.4 @SuppressWarnings 注解
- 不希望看到警告时,可以使用 @SuppressWarnings 注解来抑制警告信息。
- 在{“”} 中,可以写入希望抑制(不显示)的警告类型(各种警告类型见本节末尾)。
如下图,在 idea 中点击右侧黄线,可得到警告类型,从而设置{“”} 中 参数。
- @SuppressWarnings 的作用范围与放置的位置相关,比如:放置在 main 方法,抑制警告的范围就是 main 方法。
- @SuppressWarnings 源码:
@SuppressWarnings 注解通常放置在具体的类上,放在方法、语句上也可以。@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
该注解类有数组 String[] values(),用于接收若干警告类型字符串,如 {“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 | 抑制与使用 raw 类型相关的警告 |
resource | 抑制与使用 Closeable 类型的资源相关的警告 |
restriction | 抑制与使用不建议或禁止参照相关的警告 |
serial | 抑制与可序列化的类别遗漏 serialVersionUID 栏位相关的警告 |
static-access | 抑制与静态存取不正确相关的警告 |
static-method | 抑制与可能宣告为 static 的方法相关的警告 |
super | 抑制与置换方法相关但不含 super 呼叫的警告 |
synthetic-access | 抑制与内部类别的存取未最佳化相关的警告 |
sync-override | 抑制因为置换同步方法而遗漏同步化的警告 |
unchecked | 抑制与未检查的作业相关的警告 |
unqualified-field-access | 抑制与栏位存取不合格相关的警告 |
unused | 抑制与未用的程式码及停用的程式码相关的警告 |
2.5 元注解
JDK 的元注解用于修饰其他注解,本身作用不大,了解的目的是:看源码时,可以知道它是干什么的。
元注解的种类 (了解即可):
- Retention:指定注解的作用范围,三种:SOURCE(源码)、CLASS(类)、RUNTIME(运行时)。
- Target:指定注解可以在哪些地方使用。
- Documented:指定该注解是否会在 javadoc 体现(javadoc中是否会记录该注解)。
- Inherited:子类会继承父类注解。