Java基础:枚举和注解

文章目录

    • 一、枚举
      • 1. 对枚举的理解
      • 2. 如何定义枚举类
        • 2.1 自定义枚举类
        • 2.2 使用enum关键字定义枚举类
        • 2.3 自定义枚举类与使用enum定义枚举类的联系
      • 3. 枚举类常用方法
      • 4. 使用enum关键字定义的枚举类实现接口
    • 二、注解
      • 1. 概述
      • 2. 注解示例
      • 3. JDK提供的4种元注解
      • 4. 自定义注解
      • 5. 注意事项
      • 6. JDK8新特性
        • 1. 重复注解
        • 2. 类型注解

一、枚举

1. 对枚举的理解

  1. 枚举类的理解:类的对象只有有限个,确定的,我们此类为枚举类
  2. 当需要定义一组常量时,强烈建议使用枚举类
  3. 如果枚举类只有一个对象,则可以作为单例模式的实现方式

2. 如何定义枚举类

2.1 自定义枚举类
  1. 枚举类无法实例化对象。因此枚举类的构造器为私有的
  2. 枚举类要通过类.成员名调用成员变量,因此,成员必须是共有的
// 自定义枚举类
class  Season{
    // 1. 声明Season对象的属性
    private final String season ;
    // 2. 私有化类的有参构造器
    private Season(String season){
        this.season = season;
    }
    // 3. 定义当前枚举类的多个对象:public static final
    public static final Season SPRING = new Season("Spring");
    public static final Season SUMMER= new Season("Summer");
    public static final Season FALL= new Season("Fall");
    public static final Season WINTER= new Season("Winter");

    // 4.1 对象属性的get()
    public String getSeason(){
        return this.season;
    }
    // 4.2 提供toString方法

    @Override
    public String toString() {
        return "Season{" +
                "season='" + season + '\'' +
                '}';
    }
}
2.2 使用enum关键字定义枚举类
  1. 使用enum关键字代替class关键字来定义一个类
  2. 使用enum关键字定义枚举类时,在类的语句块中,最先要枚举出所有的变量
  3. 多个枚举对象之间使用"逗号"来分隔开
  4. 使用enum修饰枚举类时,默认的toString方法为打印当前对象的对象名(这说明使用enum修饰的枚举类的父类并不是Object)
  5. 定义的枚举类默认继承于java.lang.Enum类
enum Week{
    // 1. 提供当前枚举类的对象,多个对象之间用","隔开,末尾对象使用";"结束
    MONDAY("星期一") ,
    TUDEDAY("星期二"),
    WEDNESAY("星期三"),
    THURSDAY("星期四"),
    FRIDAY("星期五"),
    STAURDAY("星期六"),
    SUNDAY("星期天");

    // 2. 声明当前枚举类的属性,使用private final修饰
    private final String weekInfo;

    // 3. 提供带参构造方法
    private Week(String str) {
        this.weekInfo = str;
    }
    // 4. 提供使用的方法
    public String getWeekInfo() {
        return this.weekInfo;
    }
}
  • 定义枚举类时枚举的对象变量不在语句块首部,会报错:
    Java基础:枚举和注解_第1张图片
2.3 自定义枚举类与使用enum定义枚举类的联系
  • 自定义枚举类时,将枚举对象创建时,相同的部分去掉,并将枚举对象放在语句块首部,则就变成了使用enum关键字定义的枚举类
    Java基础:枚举和注解_第2张图片

3. 枚举类常用方法

  1. values():返回枚举类型的对象数组。该方法可以很方便第遍历所有的枚举值
  2. valueOf(String objName):返回枚举类中对象名是objName的对象;如果没有objName的枚举类对象,则抛出异常:IllegalArgumentException
  3. public String toString():返回枚举对象的对象名(java的Enum类进行改写后的toString(),自定义枚举类可以进行重写)

常用方法测试:

  • 使用上述enum关键字定义的枚举类来测试方法
    @Test
    public void testEnumMethdo(){
        // 1. 返回枚举类所有成员的对象数据
        Week[] values = Week.values();
        for (Week value : values) {
            // 2. 对枚举类对象进行输出,自动调用toString()方法
            System.out.println(value);
        }
        // 3. 调用valueOf方法返回指定对象名的枚举类对象
        // Week monday = Week.valueOf("Monday"); // 对象名称严格区分大小写
        Week monday = Week.valueOf("MONDAY");
        System.out.println("monday = " + monday);
    }

4. 使用enum关键字定义的枚举类实现接口

  • 情况一:实现接口,在enum类中实现抽象方法
  • 情况二:让枚举类的对象分别是实现接口中的抽象方法
  1. 定义接口
enum EnumClass implements Host{
    QITIYUANLIU("气体源流"){
        @Override
        public String showHost() {
            return "张楚岚";
        }
    },
    JULINGQIANJIANG("拘灵遣将"){
        @Override
        public String showHost() {
            return "风星瞳";
        }
    },
    TONGTIANLU("通天箓"){
        @Override
        public String showHost() {
            return "张灵玉";
        }
    },
    FENGHOUQIMEN("风后奇门"){
        @Override
        public String showHost() {
            return "王也";
        }
    },
    SHENJIBAILIAN("神级百炼"){
        @Override
        public String showHost() {
            return "马仙洪";
        }
    },
    DALUODONGGUAN("大罗洞观"){
        @Override
        public String showHost() {
            return "谷畸亭";
        }
    },
    LIUKUXIANZEI("六库仙贼"){
        @Override
        public String showHost() {
            return "巴伦";
        }
    },
    SHUANGQUANSHOU("双全手"){
        @Override
        public String showHost() {
            return "吕良";
        }
    };
    private String name ;

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

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "EnumClass{" +
                "name='" + name + '\'' +
                '}';
    }
// 	  使用这种方式,所有的枚举类对象调用产生的都是同一个结果
//    @Override
//    public void showHost() {
//        System.out.println("这是一个把奇技拥有者");
//    }
}
  1. 对实现了接口的枚举类进行测试:
    @Test
    public void testMyEnumClass(){
        EnumClass[] values = EnumClass.values();
        for (EnumClass value : values) {
            System.out.println("绝技:"+value);
            System.out.println("拥有者:"+value.showHost());
        }
        EnumClass daluodongguan = EnumClass.valueOf("DALUODONGGUAN");
        System.out.println("daluodongguan = " + daluodongguan);
    }
// 结果:
绝技:EnumClass{name='炁体源流'}
拥有者:张楚岚
绝技:EnumClass{name='拘灵遣将'}
拥有者:风星瞳
绝技:EnumClass{name='通天箓'}
拥有者:张灵玉
绝技:EnumClass{name='风后奇门'}
拥有者:王也
绝技:EnumClass{name='神级百炼'}
拥有者:马仙洪
绝技:EnumClass{name='大罗洞观'}
拥有者:谷畸亭
绝技:EnumClass{name='六库仙贼'}
拥有者:巴伦
绝技:EnumClass{name='双全手'}
拥有者:吕良
daluodongguan = EnumClass{name='大罗洞观'}

二、注解

1. 概述

  1. 从JDK5.0开始,java增加了对元数据(MetaData)的支持,也就是Annotation(注解)
    • 元数据即为注解
  2. 注解其实就是代码里的特殊标记,这些标记可以在编译,类加载、运行时内读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息,代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证
  3. 注解可以像"="像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的"value=value"对中
  4. 在javaSE中,注解的使用目的比较简单。例如标记过时的功能,忽略警告等。在javaEEAndroid中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替javaEE旧版中所遗留的冗杂代码和XML配置等
  5. 未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,Struts2有一部分也是基于注解的,
  6. 注解是一种趋势,一定程度上:可以说:框架 = 注解 + 反射 + 设计模式

2. 注解示例

  1. 生成文档相关的注解
  2. 在编译时进行格式检查(JDK内置的三个基本注解)
    1. @Override: 限定重写父类方法,该注解只能用于方法
    2. @Deprecated:用以表示所修饰的元素已经过时。通常是因为所修饰的机构危险或存在更好的选择
    3. SuppressWarnings:抑制编译器警告
  3. 跟踪代码依赖性,实现代替配置文件功能

3. JDK提供的4种元注解

元注解是用于修饰其他注解的定义:即对现有的注解进行修饰说明的注解
4个元注解:

  1. @Retention:指定该注解的生命周期,SOURCE\CLASS(默认行为)\RUNTIME(只有声明为RUNTIME生命周期的注解,才能通过反射获取)
// Retention注解的源码定义
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value(); // 成员为一个枚举类型的变量
}
// 枚举类RetentionPolicy的源码定义
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE, // 表示被修饰的注解的有效期只存在于源代码中,编译后的.class文件通过反编译后无法看到该注解

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS, // 表示被修饰的注解的有效期保留至.class文件,.class文件可见,但对运行无影响

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME // 表示被修饰的注解保留到运行期,被JVM读取,可以通过反射被调用
}

  1. @Target:用于修饰注解定义,用于指定被修饰的注解能用于修饰那些程序元素,@Target也包含一个名为value的成员变量
// target注解的源码定义:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value(); // 成员:枚举数组
}
// ElementType的定义:枚举类,可修饰的元素类型;
ElementType的枚举成员:
 1. TYPE:表示类,接口(包含注解类型),枚举类型
 2. FIELD: 属性/成员变量(包括枚举常量)
 3. METHOD:方法
 4. PARAMETER: 参数
 5. CONSTRUCTOR:构造器
 6. LOCAL_VARIABLE:局部变量
 7. ANNOTATION_TYPE:注解类型
 8. PACKAGE:包
 9. TYPE_PARAMETER:类型参数
 10.TYPE_USE:所有使用类型的地方
/*
以上成员表示,如果一个注解被target修饰,且target的值为上述成员的某几
个,则表示被修饰的注解可以使用在上述没u成员所规定的地方
*/

@target注解测试:

  • 自定义一个注解
// 使用Target注解修饰自定义注解表明该注解可作用的范围:
//类,接口,枚举类,注解,方法,类型参数(泛型)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.TYPE_PARAMETER})
public @interface TestTargetAnnotation {
}
  • 定义一个类,使用自定义的注解进行标注:
    • 在target的值中未给出的范围,使用@TestTargetAnnotation进行修饰全部报错
      Java基础:枚举和注解_第3张图片
  1. @Documented:用于指定被该Annotation修饰的Annotation类将被javadoc工具提取成文档。默认情况下,javadoc是不包括注释的
    1. 定义为Documented的注解必须设置Retention值为RUNTIME
  2. @Inherited:被他修饰的Annotation将具有继承性。如果某个类使用了被Inherited修饰的注解,则其子类将自动具有该注解
    1. 比如:如果把标有Inherited注解的自定义的注解标注在类级别上,子类则可以继承父类类级别的注解;
    2. 实际应用中,很少使用

inherited测试:

使用Inherited修饰自定义的注解,来展现注解的继承性

  1. 自定义注解
// 
@Inherited
@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
// 需要对Inherited的继承性进行验证,就需要使用反射,则需要将注解声明周期设置到为运行期
public @interface TestInheritedAnnotation {
    String value();
}
  1. 自定义父类和子类,并用自定义的注解对父类进行标注
// 自定义父类
@SuppressWarnings("unchecked")
@Resource
@TestInheritedAnnotation("对父类进行标记")
public class SuperClass {
}
// 自定义子类
public class SubClass extends SuperClass{
}
  1. 进行测试,查看结果:
@Test
public void client(){
    System.out.println("父类的注解:");
    Class clazz = SuperClass.class;
    Annotation[] annotations = clazz.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println(annotation.annotationType());
    }
    System.out.println("子类的注解:");
    clazz = SubClass.class;
    annotations = clazz.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println(annotation.annotationType());
    }
}
  1. 结果
父类的注解:
interface javax.annotation.Resource
interface 测试inherited注解.TestInheritedAnnotation
子类的注解:
interface 测试inherited注解.TestInheritedAnnotation
  • 这表明,使用了@Inherited修饰的注解,具有继承性,其可以随着子类继承父类,从而标记在子类之上

4. 自定义注解

可参考注解@SuppressWarnings进行定义

自定义注解通常都会指定两个元注解:Retention,Target

  1. 注解声明为 @interface。
  2. 内部定义成员,通常使用value表示
  3. 可以指定成员的默认值,使用default定义
    如果自定义注解没有成员,标明是一个标识作用
    如果自定义注解有成员,在使用注解是,需要指明成员的值

@SuppressWarnings的定义

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
	// 省略
    /**
     * The string "unchecked" is used to suppress unchecked warnings. 
     */
     // 字符串“unchecked”用于抑制未检查的警告。
     // 省略
    String[] value();
}

自定义的注解

  1. 使用@interface进行注解的定义
  2. 可以添加一个成员变量/不添加成员变量
  3. 可以使用元注解对自定义注解进行修饰
@Target({ElementType.TYPE_USE,})
@SuppressWarnings("unchecked") // 抑制编译器警告
public @interface MyAnnotation {
	// 注解的变量后带括号
    String name() default "乾之三爻"; // name变量的设有默认值
    String[] otherName();
}
// 自定义注解的测试:
@MyAnnotation(otherName = {"初九","风雨飘摇"}) // 对注解数组的赋值
public class TestMyAnnotation {

}

5. 注意事项

  1. 自定义类型注解自动继承了java.lang.annotation.Annotation接口
  2. Annotation的成员变量在Annotation定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称之为配置参数
    注解成员变量类型:只能是八种基本数据类型以及String类型、Class类型、enum类型、Annotation类型以及这些类型的数组
  3. 可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可以使用default关键字
  4. 如果只有一个参数成员,建议使用参数名为value
  5. 如果定义的注解含有配置参数,那么使用是必须指定参数;除非它有默认值。格式是 “参数名” = “参数值”。如果只有一个而参数成员,且名称为value,那么可以省略 value=
  6. 没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation
  7. 自定义注解必须配上注解的信息处理流程才有意义

6. JDK8新特性

1. 重复注解

重复注解:对同一个目标使用多个相同的注解,在JDK8之前,这是不允许的。
直接进行重复注解会报错
Java基础:枚举和注解_第4张图片

  1. JDK8之前实现重复注解的方式

自定义注解

// 定义注解1:定义一个实现功能的注解
public @interface MyAnnotation {
    String value();
}
// 定义注解2:定义一个注解,负责将上述定义的注解包装为数组变量
public @interface MyAnnotations {
    MyAnnotation[] values();
}

使用重复注解:

@MyAnnotations(values = {@MyAnnotation("helloworld"),@MyAnnotation("乾之三爻")}) // JDK8之前的重复注解
public class Client {
}
  1. JDK8之后实现重复注解的方式
  • 使用新注解@Repeatable对要进行重复注解的注解进行修饰,在本文中,即对@MyAnnotation进行修饰,并在赋值时,通过反射MyAnnotations.class@MyAnnotations进行绑定

依旧定义两个注解,其中一个注解的成员是注解数组,从而对另一个注解进行关联

// 定义注解1:
// 要求两个注解的变量必须是相同的名字
@Repeatable(MyAnnotations.class) 
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

// 定义注解2:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[] value();
}

使用重复注解

// JDK8之后进行重复注解:
@MyAnnotation("HelloWorld")
@MyAnnotation("QianZhiSanyao")
public class Client {
}
2. 类型注解

JDK8之后给@Target注解新增加了两个作用范围,给枚举类ElementType添加了两个成员

  1. TYPE_PARAMETER:类型参数,与泛型结合使用
  2. TYPE_USE:使用类型
 public <T>  void show(@MyAnnotation("TYPE_PARAMETER") T t){
      System.out.println(t);
  }
  public void count(@MyAnnotation("TYPE_USE") int i){
      int count = 0 ;
      for(@MyAnnotation("TYPE_USE") int j = 0 ; j < i ; j++,i--){
          count += j ;
      }
      System.out.println(count);
  }

你可能感兴趣的:(Java基础,java)