古法编程Java之枚举和注解

枚举和注解 枚举 基础知识 枚举是一组常量的集合。枚举属于一种特殊的类,里面只包含一组有限的特定的对象。 其实枚举类是可以通过传统写法自定义的,写法为: 构造器私有化 不提供set方法 在类内部预先初始化好静态的实例,并且对外暴露 代码略,直接学习如何创建真正的枚举。 使用enum关键字来代替cla...
古法编程Java之枚举和注解
古法编程Java之枚举和注解

枚举和注解

枚举

基础知识

枚举是一组常量的集合。枚举属于一种特殊的类,里面只包含一组有限的特定的对象。

其实枚举类是可以通过传统写法自定义的,写法为:

  • 构造器私有化
  • 不提供set方法
  • 在类内部预先初始化好静态的实例,并且对外暴露

代码略,直接学习如何创建真正的枚举。

  1. 使用enum关键字来代替class
  2. 直接写FALL(“秋天”,“凉爽”),效果上等价于 public static final Season FALL = new Season(“秋天”,“凉爽”);
  3. 如果有多个常量对象,使用逗号间隔即可
  4. 使用 enum 实现枚举,必须把定义的常量对象写在枚举类的最前面
  5. 使用无参构造器时,可以把括号也省略,直接写FALL,举例 FALL, SPRING, SUMMER, WINTER;
public class Enum {
    public static void main(String[] args) {
        System.out.println(Season.SPRING);
    }
}

enum Season{
    SPRING("春天","温暖"), SUMMER("夏天","炎热"),
    FALL("秋天","凉爽"), WINTER("冬天","寒冷");
    private String name;
    private String desc;
    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

.java文件可以用 Javac 编译成.class文件,.class文件也可以用 Javap 反编译成字节码文件,通过观察先编译再反编译的结果,可以看到很多隐藏的细节。

对于 Seanon 类,反编译得到的代码如下

Compiled from "Enum.java"
final class hspedu.inner.enumer.Season extends java.lang.Enum<hspedu.inner.enumer.Season> {
  public static final hspedu.inner.enumer.Season SPRING;
  public static final hspedu.inner.enumer.Season SUMMER;
  public static final hspedu.inner.enumer.Season FALL;
  public static final hspedu.inner.enumer.Season WINTER;
  public static hspedu.inner.enumer.Season[] values();
  public static hspedu.inner.enumer.Season valueOf(java.lang.String);
  public java.lang.String toString();
  static {};
}

值得关注的细节有:

  • 枚举类是 final 类型的,因此不可被继承
  • 枚举类默认继承 java.lang.Enum 类,因此不可继承其他类
  • 每一个常量都默认是 public static final 类型的

简单练习

第一题
判断语法正误
enum Gender {
    BOY, GIRL;
}
答案:
语法没有错,相当于一个“没有属性,只含有无参构造器”的枚举类

第二题
判断输出什么
enum Gender2 { 
    BOY, GIRL;
}
Gender2 boy = Gender2.BOY; 
Gender2 boy2 = Gender2.BOY;
System.out.println(boy); 
System.out.println(boy2 == boy);
答案:
BOY
true
分析:
首先,枚举类本质也是类,所以Gender2 boy = Gender2.BOY;这种写法肯定是对的
boy相当于拿到了枚举类里的public static final常量,因此boy和boy2肯定是一样的
System.out.println(boy)相当于调用Gender2的toString方法,但是显然它没有,就去找父类的toString方法,父类是java.lang.Enum(前面有提到)

Enum类的成员方法

分别是 name、ordinary、values、valueOf、compareTo方法,建议自己写一写用一用,方法的效果都写在代码里了:

public class Enum {
    public static void main(String[] args) {
        Season spring = Season.SPRING;
        // name方法,建议优先使用toString,效果类似
        System.out.println(spring.name());
        System.out.println(spring);
        // ordinary方法,输出该枚举对象的序号,从0开始
        System.out.println(spring.ordinal());
        // values方法,返回所有的枚举对象
        Season[] values = Season.values();
        for (Season value : values) {
            System.out.println(value);
        }
        // valueOf方法,返回指定名称的枚举对象
        Season valueOf = Season.valueOf("FALL");
        System.out.println(valueOf);
        // compareTo方法,比较两个枚举对象,返回它们的序号之差,在这里是spring的序号 - valueOf的序号
        System.out.println(spring.compareTo(valueOf));
    }
}

enum Season{
    SPRING("春天","温暖"), SUMMER("夏天","炎热"),
    FALL("秋天","凉爽"), WINTER("冬天","寒冷");
    private String name;
    private String desc;
    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
}

简单练习2

声明 Week 枚举类,其中包含星期一至星期日的定义;
MONDAY, TUESDAY, WEDNESDAY, THURSDAY,FRIDAY, SATURDAY, SUNDAY;
使用 values 返回所有的枚举数组,并遍历,要求打印值为“星期一”而不是“MONDAY”
public class Enum {
    public static void main(String[] args) {
        Week[] values = Week.values();
        for (Week value : values) {
            System.out.println(value);
        }
    }
}

enum Week{
    MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"),
    FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期日");
    private String name;

    Week(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }

Enum类的接口

  • Enum类本身已经有了继承关系,因此不能继承其他类
  • 但作为一个类,它仍然可以实现接口
interface Playing {
    void play();
}

enum Music implements Playing {
    HARD_ROCK, POP, CLASSIC, ROCK, JAZZ;

    @Override
    public void play() {
        
    }
}

注解

最基本的修饰符

最基本的三个修饰符分别是:

  1. Override:用来限定某个方法必须重写父类的方法,只能用于方法
  2. SuppressWarnings:抑制编译器的警告
  3. Deprecated:用来表示某个程序元素(比如类或者方法)已经过时

Override

其实对于正确的方法重写来说,加不加这个修饰符都可以。

但如果加了的话,编译器会检查你是否有正确地重写这个方法。如果不正确的话会报错,产生编译错误。

Override的源码如下,从 @Target(ElementType.METHOD) 上可以看出,这个修饰符只能用在方法上。

@interface 修饰的类都是注解类。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

顺带一提,@Target 是修饰注解的注解,也称为元注解。

Deprecated

@Deprecated 修饰符修饰的元素,暗示其已经过时了,不推荐再继续使用,但其实可以使用。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {

    String since() default "";

    boolean forRemoval() default false;
}

从源码可以看出,该修饰符可以修饰方法、字段、包、参数等。

该关键字一般用于 JDK 版本更迭时,给过时的方法打上标注。

SuppressWarnings

用来抑制编译器警告,在 ( ) 中可以填抑制的警告类型。

因为懒得打字,我直接把文档粘在这:

参数名称 作用描述 all 抑制所有警告。 unchecked 抑制与泛型相关的“未经检查的操作”警告,例如在使用原始类型时。 deprecation 抑制使用了 @Deprecated 标记的过时类或方法的警告。 rawtypes 抑制使用了泛型但未指定具体类型的“原始类型”警告。 unused 抑制代码中存在但未被使用的变量、方法或类的警告。 serial 抑制可序列化的类未定义 serialVersionUID 的警告。 null 抑制与空值分析相关的警告(如潜在的空指针)。 cast 抑制与强制类型转换操作相关的警告。 fallthrough 抑制在 switch 语句中缺少 break 而导致“直通”的警告。 finally 抑制 finally 块无法正常返回的警告。 boxing 抑制与自动装箱(boxing)和拆箱(unboxing)操作相关的警告。 static-access 抑制不正确的静态成员访问方式的警告。 dep-ann 抑制使用过时注解的警告。 incomplete-switch 抑制 switch 语句中未覆盖所有枚举常量的警告。 javadoc 抑制与 Javadoc 相关的警告。 synthetic-access 抑制内部类访问未优化的警告。 resource 抑制与资源(如 Closeable)使用相关的警告。 restriction 抑制使用了不建议或禁止引用的警告。

使用示例,代码如下:

@SuppressWarnings({"all"})
enum Music implements Playing {
    HARD_ROCK, POP, CLASSIC, ROCK, JAZZ;
    @Override
    public void play() {
    }
}

@SuppressWarnings是没有对使用位置限制的,从源码中也可以看出,它没有 @Target 去限制,源码如下:

@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

元注解

注解的注解,看源码时可能遇到,没那么重要,快速过一下。

四种元注解:

  1. Retention:指定注解的作用范围,三种值SOURCE,CLASS,RUNTIME
  2. Target:指定注解可以在哪些地方使用
  3. Documented:指定该注解是否会在javadoc体现,即在生成文档的时候,可以看到该注解
  4. Inherited:子类会继承父类注解

这部分我战略性跳过了,稍微不太好理解,也有点深入了。

1 个帖子 - 1 位参与者

阅读完整话题

来源: LinuxDo 最新话题查看原文