看一个例子:
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)对象的属性是只读,不需要修改的。
按这个设计类的思路,不能体现季节类的上述特点。因此,需要有更好的设计类的方式—— 枚举。
枚举(enumeration,简写 enum):是一组常量的集合。
可以这样理解:枚举属于一种特殊的类,里面只包含有限个特定的对象。
枚举的 2 种实现方式:
(1)自定义类实现枚举。
(2)使用 enum 关键字实现枚举。
对 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'}
对 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
使用关键字 enum 时,会隐式继承 Enum 类,这样就可以使用 Enum 类相关的方法。Enum 类源码定义为:
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
}
Enum 类中的构造方法:
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;
}
}
输出结果:
星期一
星期二
星期三
星期四
星期五
星期六
星期日
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("正在播放古典音乐...");
}
}
输出结果:
正在播放古典音乐...
注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。
和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
在 JavaSE 中,注解的使用目的比较简单,例如:标记过时的功能,忽略警告等。在 JavaEE 中,注释占据了更重要的角色,了例如用来配置应用程序的任何切面,代替 JavaEE 旧版中所遗留的繁冗代码和 XML 配置等。
使用注解时,要在前面加上@符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素。
三个基本的注解:
@Override:表示某个方法是重写了父类的方法。
@Deprecated:表示某个程序元素(类、方法等)已经过时。
@SuppressWarnings:抑制编译器警告。
@Target(ElementType.METHOD)//@Override只能修饰方法
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
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....");
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@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 | 抑制与未用的程式码及停用的程式码相关的警告 |
JDK 的元注解用于修饰其他注解,本身作用不大,了解的目的是:看源码时,可以知道它是干什么的。
元注解的种类 (了解即可):