9. Java枚举和注解

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表示一个注解类)
     @Target(ElementType.METHOD)//@Override只能修饰方法
     @Retention(RetentionPolicy.SOURCE)
     public @interface Override {
     }
    
    @Override 只能修饰方法,不能修饰类、包、属性等,从 @Target 注解以可看出。
    @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 注解类的源码:
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
    public @interface Deprecated {
    }
    
    @Deprecated 注解可以修饰方法、类、字段、包、参数等等。
    @Deprecated 的作用:新旧版本的兼容和过渡。
  • 在 idea 中,使用 @Deprecated 修饰的元素时会有一个中划线。

2.4 @SuppressWarnings 注解

  • 不希望看到警告时,可以使用 @SuppressWarnings 注解来抑制警告信息。
  • 在{“”} 中,可以写入希望抑制(不显示)的警告类型(各种警告类型见本节末尾)。
    如下图,在 idea 中点击右侧黄线,可得到警告类型,从而设置{“”} 中 参数。
    请添加图片描述
  • @SuppressWarnings 的作用范围与放置的位置相关,比如:放置在 main 方法,抑制警告的范围就是 main 方法。
  • @SuppressWarnings 源码:
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
    	String[] value();
    }
    
    @SuppressWarnings 注解通常放置在具体的类上,放在方法、语句上也可以。
    该注解类有数组 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:子类会继承父类注解。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值