JavaEnhance——枚举、注解及泛型

枚举


一、概述

如果一个变量的取值要被限定在一定的范围内,例如星期的取值,限定在1~7的范围内,并且想要在编译期就能指出变量取值超出范围的错误,就可以使用枚举。枚举相当于一个类,其中也可以定义构造方法,成员变量,普通方法和抽象方法。

二、枚举的应用

1.使用普通类模拟枚举

使用普通类模拟枚举的时候,首先将构造方法私有化,然后在类的内部创建常量,那么其他类在调用这个类的时候,只能得到模拟枚举类中的常量,不能new出新的值。

示例代码:

package com.heisejiuhuche.javaenhance;

public abstract class WeekDay {
    //私有化构造方法
    private WeekDay() {}
    //创建静态常量
    public static final WeekDay SUN = new WeekDay();

    public static final WeekDay MON = new WeekDay();

}

package com.heisejiuhuche.javaenhance;

public class EnumTest {
    public static void main(String[] args) {
    //通过类名得到静态常量,赋值给WeekDay的对象
    WeekDay weekDay = WeekDay.MON;
}

这样就实现了一个最基本的枚举类,除了类中定义的两个常量外,不可能再为weekDay赋另外的值。接下来,可以在模拟枚举类中加入方法。

示例代码:

package com.heisejiuhuche.javaenhance;

public abstract class WeekDay {
    private WeekDay() {}

    public static final WeekDay SUN = new WeekDay();

    public static final WeekDay MON = new WeekDay();

    /* nextDay()方法返回下一天 */
    public WeekDay nextDay() {
        if(this == SUN) {
            return MON;
        } else {
            return SUN;
        }
    }

    /* toString()方法打印信息 */
    public String toString() {
        return this == SUN ? "SUN" : "MON";
    }
}

这里只写了两个常量,如果有七个,那么这个nextDay()方法中的if else语句就会很长,为了避免长代码,可以将nextDay()定义为抽象方法,由每个子类对象去实现。

示例代码:

package com.heisejiuhuche.javaenhance;

public abstract class WeekDay {
    private WeekDay() {}

    public static final WeekDay SUN = new WeekDay() {
        //每个对象实现抽象方法
        public WeekDay nextDay() {
            return MON;
        }
    };

    public static final WeekDay MON = new WeekDay() {
        public WeekDay nextDay() {
            return SUN;
        }
    };

    /* nextDay()声明为抽象方法 */
    public abstract WeekDay nextDay();

}

用抽象方法可以将大量的if else语句转换成由一个个独立的子类来实现该抽象方法,简化了代码。

2.定义枚举类
在用普通类模拟了枚举类之后,现在定义一个真正的枚举类。在类中定义一个内部枚举类。

示例代码:

package com.heisejiuhuche.javaenhance;

public class EnumTest {
    public static void main(String[] args) {
        /* 定义枚举变量 */
        WeekDay weekDay2 = WeekDay.FRI;
        System.out.println(weekDay2);
        /* 得到weekDay2的名字 */
        System.out.println(weekDay2.name());
        /* 得到weekDay2的位置 */
        System.out.println(weekDay2.ordinal());
        /* 得到weekDay2的字节码文件 */
        System.out.println(weekDay2.getClass());
        /* 将字符串SUN转换成枚举成员 */
        System.out.println(WeekDay.valueOf("SUN"));
        /* 将枚举成员转换成枚举数组 */
        System.out.println(WeekDay.values().length);

    }

    /* 声明枚举类 */
    public enum WeekDay {
        SUN, MON, TUE, WED, THU, FRI, SAT;

    }
}

枚举类内部实现了toString()方法。下面为枚举类定义构造方法。

示例代码:

public enum WeekDay {
    SUN, MON, TUE, WED, THU, FRI, SAT;
    private WeekDay() {

    }
}

//声明有参数的构造方法:
public enum WeekDay {
    SUN, MON, TUE, WED, THU, FRI, SAT;
    private WeekDay() {

    }

    private WeekDay(int day) {

    }
}

默认情况下,枚举的成员使用的都是无参构造方法。只要用到枚举类,里面的所有静态成员都会被初始化。如果指定要调用有参构造方法,只需在枚举成员后加上小括号,写入参数即可。

示例代码:

public enum WeekDay {
    //调用指定构造方法
    SUN(1), MON, TUE, WED, THU, FRI, SAT;
    private WeekDay() {
        System.out.println("first");
    }

    private WeekDay(int day) {
        System.out.println("second");
    }
}

最最复杂的枚举,就是带有抽象方法的枚举,下面运用枚举实现一个交通灯系统。

示例代码:

public enum TrafficLight {
    //三个枚举对象,分别实现nextLight()方法
    RED {
        public TrafficLight nextLight() {
            return GREEN;
        }
    },

    GREEN {
        public TrafficLight nextLight() {
            return YELLOW;
        }
    },

    YELLOW {
        public TrafficLight nextLight() {
            return RED;
        }
    };
    //声明nextLight()抽象方法,由子类对象去实现
    public abstract TrafficLight nextLight();
}

三、枚举总结

1.枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后面要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面, 编译器会报错。

2.带构造方法的枚举

(1) 构造方法必须定义成私有
(2) 如果有多个构造方法,在枚举元素后面加上小括号来调用指定的构造方法
(3) 枚举元素MONMON()的效果一样,都是调用默认的构造方法

3.带抽象方法的枚举

每个元素都是由枚举类的子类来生成的实例对象,这些子类可以采用类似内部类的方式来定义并实现枚举中的抽象方法。

4.枚举只有一个成员时,就可以作为一个单例的实现方式。


注解


一、概述

注解是JDK1.5开始的新特性,一个注解就是一个类,运用注解的时候就是创建了一个注解的实例对象。现行的框架,如HibernateSpringEJB以及Struts2的一部分,都是基于注解技术。注解相当于一个标记,可以添加在包、类、字段、方法、方法的参数以及局部变量上。

二、常见注解

1.@SuppressWarnings
该注解用于去除编译期出现的警告提示,如调用了过时方法时的警告提示

2.@Deprecated
该注解用于标识一个方法等已经过时,不建议使用;添加了该注解的方法等,或被一根横线划过

3.@Override
该注解用于标识一个方法是其父类中方法的复写;如果方法与父类中的方法不一致,会得到报错提示

三、元注解

元注解就是添加在注解类上的注解,用于标识该注解的生命周期以及可以添加的位置等。

1.@Retention

@Retention注解有三个取值,分别对应着注解的生命周期,它们是:
@RetentionPolicy.SOURCE@RetentionPolicy.CLASS@RetentionPolicy.RUNTIME
由于javac在编译源文件的时候或是classLoaderclass文件加载到内存的过程中,会将一些注解去掉,会导致注解失效

2.@Target
@Target元注解接收一个ElementType数组作为参数,指定某个注解类能被添加到哪些地方
@Target({ElementType.METHOD, ElementType.TYPE})表示这个注解能被添加到方法和类上

四、注解的应用

1.创建自定义注解
注解是一个类似接口的类,用@interface + 类名来表示。

示例代码:

package com.heisejiuhuche.javaenhance;

public @interface MyAnnotation {}

将自定义注解类添加到一个类上

示例代码:

package com.heisejiuhuche.javaenhance;

@MyAnnotation
public class AnnotationTest {

    public static void main(String[] args) {
        if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation ma = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);

            System.out.println(ma);
        }
    }
}

程序无打印结果,是因为自定义注解上没有添加标识该注解声明周期的元注解。

添加元注解:

package com.heisejiuhuche.javaenhance;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}

这样,就可以打印出注解信息。

五、为注解添加属性

Java可以为注解增加属性,以便更好地使用注解。注解是一个类似接口的类,属性以抽象方法形式出现。

示例代码:

package com.heisejiuhuche.javaenhance;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
    /* 字符串属性 */
    String color() default "blue";
    String value();
    /* 数组属性 */
    int[] arrayAttr() default {3, 4, 5};
    /* 枚举属性 */
    TrafficLight light() default TrafficLight.RED;
    /* 注解属性 */
    Anno anno() default @Anno("itheima");
}

为属性赋值并调用:

package com.heisejiuhuche.javaenhance;


@MyAnnotation(color = "red", value = "abc", arrayAttr=1, anno=@Anno("mars")) //数组类型的属性只有1个元素的时候可以不加大括号
public class AnnotationTest {
    @MyAnnotation("abc")
    public static void main(String[] args) {
        if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation ma = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
            System.out.println(AnnotationTest.class.getAnnotation(MyAnnotation.class));
            System.out.println(ma);
            System.out.println(ma.arrayAttr().length); //大印数组长度
            System.out.println(ma.light().nextLight()); //调用枚举方法
            System.out.println(ma.anno()); //打印注解属性
        }
    }
}

泛型高级应用


泛型高级应用中,演示如何通过反射的方法拿到泛型的类型。想要直接通过一个带有泛型的类来获取该泛型的类型是不行的,只有通过一个接收该泛型类的方法来实现。

示例代码:

package com.heisejiuhuche.javaenhance;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;

public class GenericTest {
    public static void main(String[] args) throws Exception {
        //得到所有泛型参数的类型数组
        Type[] types = GenericTest.class.getMethod("applyGeneric", Vector.class).getGenericParameterTypes();
        //得到泛型类型的对象
        ParameterizedType paraType = (ParameterizedType)types[0];
        //打印泛型类型对象的原始类型
        System.out.println(paraType.getRawType());
        //打印泛型类型对象的泛型的类型,getActualTypeArguments方法返回的是一个类型数组,所以要指定下标打印相应的类型
        System.out.println(paraType.getActualTypeArguments()[0]);
    }

    public static void applyGeneric(Vector<Date> v) {

    }
}

程序输出结果:

class java.util.Vector
class java.util.Date