第七章 枚举和注解

7.1 枚举(Enumeration)

7.1.1 定义

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

7.1.2 自定义类实现枚举

  1. 构造器私有化。
  2. 去掉setXX方法,防止属性被修改。
  3. 在类内部,直接创建固定的对象。
  4. 对枚举对象/属性可以使用static + final修饰,实现底层优化。
  • public class Enumeration02 {
        public static void main(String[] args) {
            System.out.println(Season.AUTUMN);  // Season{name='秋天', desc='凉爽'}
            System.out.println(Season.SPRING);  // Season{name='春天', desc='温暖'}
        }
    }
    
    //演示自定义枚举实现
    class Season {//类
        private String name;
        private String desc;//描述
    
        //定义了四个对象, 固定.
        public static final Season SPRING = new Season("春天", "温暖");
        public static final Season WINTER = new Season("冬天", "寒冷");
        public static final Season AUTUMN = new Season("秋天", "凉爽");
        public static final Season SUMMER = new Season("夏天", "炎热");
    
        //1. 将构造器私有化,目的防止 直接 new
        //2. 去掉setXxx方法, 防止属性被修改
        //3. 在Season 内部,直接创建固定的对象
        //4. 优化,可以加入 final 修饰符
        private 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 + '\'' +
                    '}';
        }
    }
    

7.1.3 enum关键字实现枚举

  1. 使用关键字enum替代class。

  2. 构造器私有化。

  3. 去掉setXX方法,防止属性被修改。

  4. 使用简化版的方法来创建固定的对象。

  • public class Enumeration03 {
        public static void main(String[] args) {
            System.out.println(Season2.AUTUMN);  // Season{name='秋天', desc='凉爽'}
            System.out.println(Season2.SUMMER);  // Season{name='夏天', desc='炎热'}
        }
    }
    
    //演示使用enum关键字来实现枚举类
    enum Season2 {//类
        //1. 使用关键字 enum 替代 class
        //2. public static final Season SPRING = new Season("春天", "温暖") 直接使用
        //   SPRING("春天", "温暖") 解读 常量名(实参列表)
        //3. 如果有多个常量(对象), 使用 ,号间隔即可
        //4. 如果使用enum 来实现枚举,要求将定义常量对象,写在前面
        //5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()
       SPRING("春天", "温暖"), SUMMER("夏天", "炎热"), AUTUMN("秋天", "凉爽"),
       WINTER("冬天", "寒冷")  /*, What()*/;
    
        private String name;
        private String desc;//描述
    
        private Season2() {//无参构造器
    
        }
    
        private Season2(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 + '\'' +
                    '}';
        }
    }
    
7.1.3.1 注意事项
  1. 当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类。
  2. 传统的 public static final Season2 SPRING = new Season2(“春天”, “温暖”); 简化成 SPRING(“春天”, “温暖”), 这里必须知道,它调用的是哪个构造器。
  3. 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略。
  4. 当有多个枚举对象时,使用,间隔,最后有一个分号结尾。
  5. 枚举对象必须放在枚举类的行首。
7.1.3.2 常用方法
  • Season2 spring = Season2.SPRING;
    Season2 summer = Season2.SUMMER;
    Season2 autumn = Season2.AUTUMN;
    Season2 winter = Season2.WINTER;
    
  1. toString:Enum 类已经重写过了,返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息。

    System.out.println(spring.toString());  // Season{name='春天', desc='温暖'}
    System.out.println(summer.toString());  // Season{name='夏天', desc='炎热'}
    
  2. name:返回当前对象名(常量名),子类中不能重写

    System.out.println(spring.name());  // SPRING
    System.out.println(summer.name());  // SUMMER
    
  3. ordinal:返回当前对象的位置号,默认从 0 开始。

    System.out.println(autumn.ordinal());  // 2
    System.out.println(winter.ordinal());  // 3
    
  4. values:返回当前枚举类中所有的常量。

    Season2[] values = Season2.values();
    for (Season2 value : values) {
        System.out.println(value);
    }
    /* 
    Season{name='春天', desc='温暖'}
    Season{name='夏天', desc='炎热'}
    Season{name='秋天', desc='凉爽'}
    Season{name='冬天', desc='寒冷'}
    */
    
  5. valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常!

    Season2 autumn1 = Season2.valueOf("AUTUMN");  
    System.out.println("autumn1=" + autumn1);  // autumn1=Season{name='秋天', desc='凉爽'}
    System.out.println(autumn == autumn1);  // true
    Season2 autumn2 = Season2.valueOf("AUTUMNx");  // 报错
    
  6. compareTo:比较两个枚举常量,比较的就是编号!

    System.out.println(spring.compareTo(summer));  // -1
    System.out.println(summer.compareTo(spring));  // 1
    
7.1.3.3 使用细节
  1. 使用 enum 关键字后,就不能再继承其它类了,因为 enum 会隐式继承 Enum,而 Java 是单继承机制。

  2. 枚举类和普通类一样,可以实现接口,如下形式。

    enum 类名 implements 接口 1,接口 2{}
    

7.2 注解(Annotation)

7.2.1 定义

  1. 定义:从Java 5版本之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解(Annotation),是 Java 平台中非常重要的一部分。

  2. 作用范围:注解(Annotation)也被称为元数据(Metadata),用于修饰解释 方法属性构造器局部变量等数据信息。

  3. 类型:无论是哪一种注解,本质上都一种数据类型,是一种接口类型。到 Java 8 为止 Java SE 提供了 11 个内置注解。其中有 5 个是基本注解,它们来自于 java.lang 包有 6 个是元注解,它们来自于 java.lang.annotation 包,自定义注解会用到元注解

  4. 注解和注释的区别:和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息

7.2.2 作用

  1. JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 Java EE 旧版中所遗留的繁冗代码和 XML 配置等
  2. 生成帮助文档,这是最常见的,也是 Java 最早提供的注解。常用的有 @see@param@return 等。
  3. 在编译时进行格式检查,如把 @Override 注解放在方法前,如果这个方法并不是重写了父类方法,则编译时就能检查出。

7.2.3 基本注解

7.2.3.1 Override(重写)
  1. 定义:@Override表示指定重写父类的方法(从编译层面验证),如果父类没有定义该方法,则会报错。

  2. 如果不写@Override注解,而父类仍有该方法,依然构成重写。

  3. 限定某个方法,是重写父类方法,该注解只能用于方法,不能修饰其他类、包、属性等。

    public class Person {
        private String name = "";
        private int age;
        ...
        @Override
        public String toString() { //toString()
            return "Person [name=" + name + ", age=" + age + "]";
        }
    }
    
7.2.3.2 Deprecated(弃用)
  1. 定义:用于表示某个程序元素(包、类、方法、字段、参数等)已过时,当其他程序使用已过时的元素时,编译器将会给出警告。

  2. @Deprecated的作用可以做到新旧版本的兼容和过渡,Java 9 为 @Deprecated 注解增加了以下两个属性:

    1. forRemoval:该 boolean 类型的属性指定该 API 在将来是否会被删除。
    2. since:该 String 类型的属性指定该 API 从哪个版本被标记为过时。
    @Deprecated(since = "9", forRemoval = true)
    public class Person {
        @Deprecated
        protected String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Deprecated
        public void setNameAndAge(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
7.2.3.3 SuppressWarnings(抑制警告)
  1. 定义:注解指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。

  2. 注解的使用有以下三种:

    1. 抑制单类型的警告:@SuppressWarnings(“unchecked”)
    2. 抑制多类型的警告:@SuppressWarnings(“unchecked”,“rawtypes”)
    3. 抑制所有类型的警告:@SuppressWarnings(“unchecked”)
  3. 在{“}中,可以写入你希望抑制的警告信息。

    @SuppressWarnings({"rawtypes", "unchecked", "unused"})
    public class HelloWorld {
        @SuppressWarnings({ "deprecation" })
        public static void main(String[] args) {
            Person p = new Person();
            p.setNameAndAge("C语言中文网", 20);
            p.name = "Java教程";
        }
    }
    
  4. 抑制警告的关键字如下表所示:

    关键字用途
    all抑制所有警告
    boxing抑制装箱、拆箱操作时候的警告
    cast抑制映射相关的警告
    dep-ann抑制启用注释的警告
    deprecation抑制过期方法警告
    fallthrough抑制在 switch 中缺失 breaks 的警告
    finally抑制 finally 模块没有返回的警告
    hiding抑制相对于隐藏变量的局部变量的警告
    incomplete-switch忽略不完整的 switch 语句
    nls忽略非 nls 格式的字符
    null忽略对 null 的操作
    rawtypes使用 generics 时忽略没有指定相应的类型
    restriction抑制禁止使用劝阻或禁止引用的警告
    serial忽略在 serializable 类中没有声明 serialVersionUID 变量
    static-access抑制不正确的静态访问方式警告
    synthetic-access抑制子类没有按最优方法访问内部类的警告
    unchecked抑制没有进行类型检查操作的警告
    unqualified-field-access抑制没有权限访问的域的警告
    unused抑制没被使用过的代码的警告
7.2.3.4 FunctionInterface(函数式接口)
  • 学习 Lambda 表达式时,我们提到如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口。@FunctionalInterface 就是用来指定某个接口必须是函数式接口,所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。

  • @FunctionalInterface
    public interface FunInterface {
        static void print() {
            System.out.println("你好");
        }
        default void show() {
            System.out.println("你干嘛啊哎哟");
        }
        void test(); // 只定义一个抽象方法
    }
    

7.2.4 元注解

  • 元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。Java 5 定义了 4 个注解,分别是 @Documented@Target@Retention@Inherited。Java 8 又增加了 @Repeatable@Native 两个注解。这些注解都可以在 java.lang.annotation 包中找到。下面主要介绍每个元注解的作用及使用。
7.2.4.1 Documented
  • @Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。

  • 案例:

    @Documented
    @Target({ ElementType.TYPE, ElementType.METHOD })
    public @interface MyDocumented {
        public String value() default "这是@Documented注解";
    }
    
    @MyDocumented
    public class DocumentedTest {
        /**
         * 测试document
         */
        @MyDocumented
        public String Test() {
            return "C语言中文网Java教程";
        }
    }
    

    打开 Java 文件所在的目录,分别输入如下两条命令行:运行成功后,打开生成的帮助文档,可以看到在类和方法上都保留了MyDocument 的注解信息。

    javac MyDocumented.java DocumentedTest.java
    javadoc -d doc MyDocumented.java DocumentedTest.java
    

7.2.4.2 Target
  • @Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量。

    名称说明
    CONSTRUCTOR用于构造方法
    FIELD用于成员变量(包括枚举常量)
    LOCAL_VARIABLE用于局部变量
    METHOD用于方法
    PACKAGE用于包
    PARAMETER用于类型参数(JDK 1.8新增)
    TYPE用于类、接口(包括注解类型)或 enum 声明
  • 比如下述代码中的自定义注解的作用范围为方法,当在变量上添加该注解时会出现编译性错误。

    @Target({ ElementType.METHOD })
    public @interface MyTarget {
    }
    class Test {
        @MyTarget
        String name;
    }
    
7.2.4.3 Retention
  • @Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量,如下所示。

    1. SOURCE:在源文件中有效(即源文件保留)

    2. CLASS:在 class 文件中有效(即 class 保留)

    3. RUNTIME:在运行时有效(即运行时保留)

      生命周期大小排序为 SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS 注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。

7.2.4.4 Inherited
  • @Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。

  • 案例:创建一个自定义注解,代码如下所示:

    @Target({ ElementType.TYPE })
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyInherited {
    }
    

    测试代码:

    @MyInherited
    public class TestA {
        public static void main(String[] args) {
            System.out.println(TestA.class.getAnnotation(MyInherited.class));
            System.out.println(TestB.class.getAnnotation(MyInherited.class));
            System.out.println(TestC.class.getAnnotation(MyInherited.class));
        }
    }
    class TestB extends TestA {
    }
    class TestC extends TestB {
    }
    

    运行结果:

    @MyInherited()
    @MyInherited()
    @MyInherited()
    
7.2.4.5 Repeatable
  • @Repeatable 注解是 Java 8 新增加的,它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。

  • Java 8之前的做法:

    public @interface Roles {
        Role[] roles();
    }
    public @interface Roles {
        Role[] value();
    }
    public class RoleTest {
        @Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
        public String doString(){
            return "这是C语言中国网Java教程";
        }
    }
    
  • Java 8 之后增加了重复注解,使用方式如下:

    public @interface Roles {
        Role[] value();
    }
    @Repeatable(Roles.class)
    public @interface Role {
        String roleName();
    }
    public class RoleTest {
        @Role(roleName = "role1")
        @Role(roleName = "role2")
        public String doString(){
            return "这是C语言中文网Java教程";
        }
    }
    

    不同的地方是,创建重复注解 Role 时加上了 @Repeatable 注解,指向存储注解 Roles,这样在使用时就可以直接重复使用 Role 注解。从上面例子看出,使用 @Repeatable 注解更符合常规思维,可读性强一点。

    两种方法获得的效果相同。重复注解只是一种简化写法,这种简化写法是一种假象,多个重复注解其实会被作为“容器”注解的 value 成员的数组元素处理。

7.2.4.6 Native
  • 使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可。

7.2.5 自定义注解

  • 当基本注解和元注解,如果这两种注解不能满足你的需求,可以自定义注解。下面介绍如何自定义注解。

  • 声明自定义注解使用 @interface 关键字(interface 关键字前加 @ 符号)实现。定义注解与定义接口非常像,如下代码可定义一个简单形式的注解类型。此外注解中的成员变量也可使用 default 关键字来定义默认值。

    public @interface Test {
        String name() default "张三";
        int age();
    }
    
  • 根据注解是否包含成员变量,可以分为如下两类。

    1. 标记注解:没有定义成员变量的注解类型被称为标记注解。这种注解仅利用自身的存在与否来提供信息,如前面介绍的 @Override、@Test 等都是标记注解。
    2. 元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。

7.3 多态练习

  • public class Homework06 {
        public static void main(String[] args) {
            person tang = new person("唐僧", new Horse());
            tang.common();
            tang.PassRiver();
            tang.fly();
        }
    }
    
    interface Vehicles {
        public void work();
    }
    
    class Horse implements Vehicles {
        @Override
        public void work() {
            System.out.println("一般情况下,使用马儿前进");
        }
    }
    
    class Boat implements Vehicles {
        @Override
        public void work() {
            System.out.println("过河的时候,使用小船前进");
        }
    }
    
    class Plane implements Vehicles {
        @Override
        public void work() {
            System.out.println("过火焰山的时候,使用飞机前进");
        }
    }
    
    
    class VehicleFactory {
        // 创建交通工具工厂类,有两个方法分别获得交通工具Horse和Boat
        private static Horse horse = new Horse();  // 创建始终为一的白龙马
    
        public static Horse getHorse() {
    //        return new Horse();
            return horse;
        }
    
        public static Boat getBoat() {
            return new Boat();
        }
    
        public static Plane getPlane() {
            return new Plane();
        }
    
    }
    
    class person {
        private String name;
        private Vehicles vehicles;
    
        public person() {
        }
    
        public person(String name, Vehicles vehicles) {
            this.name = name;
            this.vehicles = vehicles;
        }
    
        // 过河
        public void PassRiver() {
            if (!(vehicles instanceof Boat)) {  // 需要船时
                vehicles = VehicleFactory.getBoat();  // // 从工厂获得船
            }
    //        Boat boat = VehicleFactory.getBoat();  // 从工厂获得船
            vehicles.work();  // 用船过河
        }
    
        // 一般情况
        public void common() {
            if (!(vehicles instanceof Horse)) {  // 需要白龙马时
                vehicles = VehicleFactory.getHorse();  // 从工厂获得马
            }
    //        Horse horse = VehicleFactory.getHorse();  // 从工厂获得马
            vehicles.work();  // 用马走路
        }
    
        // 一般情况
        public void fly() {
            if (!(vehicles instanceof Plane)) {  // 需要飞机时
                vehicles = VehicleFactory.getPlane();  // 从工厂获得飞机
            }
    //        Plane plane = VehicleFactory.getPlane();  // 从工厂获得马
            vehicles.work();  // 用飞机飞
        }
    }
    

7.4 枚举练习

  • public class Homework08 {
        public static void main(String[] args) {
            Color blue = Color.BLUE;
            blue.show();
            switch (blue) {
                case RED:
                    System.out.println("成功匹配红色");
                    break;
                case BLUE:
                    System.out.println("成功匹配蓝色");
                    break;
                default:
                    System.out.println("匹配颜色失败");
            }
        }
    }
    /*
    属性值为0,0,255
    成功匹配蓝色
    */
    interface ColorInterface {
        void show();
    }
    
    enum Color implements ColorInterface {
        RED(255, 0, 0), BLUE(0, 0, 255), BLACK(0, 0, 0),
        YELLOW(255, 255, 0), GREEN(0, 255, 0);
        private int redValue;
        private int greenValue;
        private int blueValue;
    
        Color() {
        }
    
        Color(int redValue, int greenValue, int blueValue) {
            this.redValue = redValue;
            this.greenValue = greenValue;
            this.blueValue = blueValue;
        }
    
        @Override
        public void show() {
            System.out.println("属性值为" + redValue + "," + greenValue + "," + blueValue);
        }
    
    }
    

部分内容参考:

Java元注解作用及使用 (biancheng.net)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过使用ds注解实现切换数据源的步骤如下: 1. 首先,在项目的依赖管理中添加相关的依赖,包括MyBatis、Spring JDBC、Druid等。 2. 在Spring的配置文件中配置第一个数据源的相关信息,包括数据源的url、username、password等。 3. 在Spring的配置文件中配置第二个数据源的相关信息,同样包括数据源的url、username、password等。 4. 创建两个数据源的Bean,使用@Bean注解进行声明,并在Bean的方法中配置相应的数据源信息。 5. 创建一个数据源切换注解类,使用@Inherited注解声明该注解可以被继承,然后在该注解类中定义一个枚举类型的属性,用于标识数据源的名称。 6. 在切换数据源的方法上使用@Ds注解,并设置该方法使用的数据源的名称。在方法执行之前,通过AOP的方式切面拦截该方法,获取到数据源注解,并根据注解中定义的数据源名称,将数据源切换为指定的数据源。 7. 在需要切换数据源的方法上添加@Ds注解,注明使用的数据源名称。这样,在执行这个方法时,会根据注解指定的数据源名称,自动切换数据源。 8. 最后,通过配置AOP的方式,将切面注解的拦截处理类应用到方法上。这样,当调用带有@Ds注解的方法时,会自动切换到指定的数据源。 综上所述,通过使用ds注解实现切换数据源的关键步骤包括:配置数据源信息、定义数据源切换注解类、使用@Ds注解标识需要切换数据源的方法、配置AOP拦截处理类。通过这些步骤,就可以实现在不同的方法中切换不同的数据源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值