Java复习——枚举与注解

时间:2023-02-15 19:33:57

枚举

枚举就是让某些变量的取值只能是若干固定值中的一个,否则编译器就会报错,枚举可以让编译器在编译阶段就控制程序的值,这一点是普通变量无法实现的。枚举是作为一种特殊的类存在的,使用的是enum关键字修饰

枚举常量

枚举常量默认都是使用static final修饰的,所以语法建议使用大写,一个枚举类在第一次被实例化的时候,这些常量就会被创建,这些常量都是枚举类子类的对象

public enum WeekDay{
//每一个枚举的元素(枚举常量)就是一个枚举类子类的对象,是使用static final修饰的
//第一次实例化枚举类会初始化这些对象
SUN ,MON;

}

在没有枚举类型之前,要实现类似的功能,需要这样设计:

//使用普通类来模拟Enum
public abstract class MyEnum {

//构造私有
private MyEnum(){}

//元素是子类的实例(使用匿名内部类)
public static final MyEnum SUN=new MyEnum(){};

public static final MyEnum MON=new MyEnum(){};
}

枚举这样的设计简直就是单例模式嘛,所以常用来设计单例模式,当只有一个枚举常量的时候。

构造函数私有化

构造函数私有化是枚举类的一各特点,我们可以尝试在枚举类中添加public修饰的构造函数,结果会报错。而默认的枚举常量在创建的时候调用的是无参的构造函数,可以在定义这些常量的时候给一个值,就会调用有参的构造了:

    //枚举是作为一种特殊的类存在的
public enum WeekDay{
//每一个枚举的元素(枚举常量)就是一个枚举类子类的对象,是使用static final修饰的
//所以每次调用枚举类都会初始化这些对象
SUN ,MON(100);
//枚举的构造是私有的
//枚举常量默认调用的是无参构造
private WeekDay(){
System.out.println(
"无参构造");
}

//可以通过给枚举常量添加参数列表的方式来调用有参构造
private WeekDay(int i){
System.out.println(
"有参构造");
}


}

这里的SUN和MON分别调用无参和有一个参数的构造器

枚举类中定义属性和方法

枚举类中成分(方法,属性)需要放在枚举常量的后面,注意枚举常量要以;结尾。除了构造函数必须设置为私有的之外,其他的方法没有要求,甚至可以为abstract抽象类型,不过,枚举常量就要实现这些抽象的方法了

//枚举是作为一种特殊的类存在的
public enum WeekDay{
//每一个枚举的元素(枚举常量)就是一个枚举类子类的对象,是使用static final修饰的
//所以每次调用枚举类都会初始化这些对象
SUN {
@Override
public void getTime() {
//在这里可以看到枚举常量是作为枚举类子类的对象存在的,
//需要实现枚举类的抽象方法
}
},MON(
100) {
@Override
public void getTime() {
System.out.println(time);
}
};

//枚举类中成分(方法,属性)需要放在枚举常量的后面,注意枚举常量要以;结尾
//枚举的构造是私有的
//枚举常量默认调用的是无参构造
private WeekDay(){
System.out.println(
"无参构造");
}

//可以通过给枚举常量添加参数列表的方式来调用有参构造
private WeekDay(int i){
System.out.println(
"有参构造");
this.time=i;
}

public int time;

public abstract void getTime();

}

调用枚举类和枚举常量的方法

枚举类本身提供了一些方法,例如valueOf和values方法

valueOf():返回枚举类中是否有指定名称的枚举常量,返回值为true/false

values():返回包含枚举类中常量的数组

枚举常量的方法例如:

name():返回枚举常量的名字,好像没什么用

ordinal():返回该枚举常量在枚举类中的位置,默认是0开始

public class EnumTest {

public static void main(String[] args) {

WeekDay weekDay
=WeekDay.MON;
//enum对象实现了toString方法
System.out.println(weekDay);
//name方法返回枚举元素的名字
System.out.println(weekDay.name());
//ordinal返回枚举元素的位置,从0开始
System.out.println(weekDay.ordinal());
//enum类的valueOf方法将字符串转化为枚举类中的元素,这个字符串应该存在与enum中的元素一致
System.out.println(WeekDay.valueOf("SUN"));
//values()方法返回枚举类中所有元素的数组
System.out.println(WeekDay.values().length);

weekDay.getTime();

}

//枚举是作为一种特殊的类存在的
public enum WeekDay{
//每一个枚举的元素(枚举常量)就是一个枚举类子类的对象,是使用static final修饰的
//所以每次调用枚举类都会初始化这些对象
SUN {
@Override
public void getTime() {
//在这里可以看到枚举常量是作为枚举类子类的对象存在的,
//需要实现枚举类的抽象方法
}
},MON(
100) {
@Override
public void getTime() {
System.out.println(time);
}
};

//枚举类中成分(方法,属性)需要放在枚举常量的后面,注意枚举常量要以;结尾
//枚举的构造是私有的
//枚举常量默认调用的是无参构造
private WeekDay(){
System.out.println(
"无参构造");
}

//可以通过给枚举常量添加参数列表的方式来调用有参构造
private WeekDay(int i){
System.out.println(
"有参构造");
this.time=i;
}

public int time;

public abstract void getTime();

}

}

这里我将枚举类作为内部类,其实没有什么特殊的用意,就是少写一个类文件,这里再复习一下内部类的知识:内部类可以是任意的访问修饰权限

注解

注解就相当于一种标记,有这种标记就做相应的事情,没有就不做。枚举可以加在包,类,属性,方法,方法的参数以及局部变量上。在Java中真的就是万事万物皆对象,注解和上面的枚举都是作为一种特殊的类存在的。与注解相关的操作都在 java.lang.annotation 这个包下,我们在框架中使用注解开发也很常见,下面我们就来了解一下注解

lang包中自带的三个注解

@Override

这个注解应该说是较为常见的,我们通常在重写父类方法的时候添加这个注解用于效验是否是在重写,而不是因为参数列表的不同而变成重载,或者是变成一个同名的新方法,是起一个效验的作用,但并不说重写方法一定要加这个注解

@SuppressWarnings("deprecation")

这个注解可以消除编译器的警告,我们在写程序的时候可能会使用过时的方法,这时编译的时候会有警告,就可以使用这个注解消除警告了,括号中没有指定哪个属性默认的是就是使用value

@Deprecated

想知道如何将一个方法变成过时的吗?使用@Deprecated这个注解在方法上即可,这样调用我们的这个方法的时候就会有过时的警告

自定义注解

自定义注解就是使用@interface来修饰一个类,这个类就作为注解类存在:

/**
* 注解也是一种特殊的类存在
*
*/

public @interface MyAnnotation {}

我们接下来可是将这个注解加在一个类上,并通过反射来获得注解的一些信息

@MyAnnotation(value="abc",array={"3"},array2=6)
public class AnnotationTest {

/**
* 消除编译器警告的注解
*
@param args
*/

public static void main(String[] args) {

//通过反射可以检查注解Class类的isAnnotationPresent方法检查某个类是否存在注解
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
//得到注解
MyAnnotation myAnnotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
//想要使用注解,就需要设置注解存活时间
System.out.println(myAnnotation);
}

}

}

这时你会发现什么都没输出,不要怀疑这段反射代码是不是写错了,真正的原因在我们在自定义注解类的时候没有设置它的存活期限,默认只存在与source源码阶段,当变成class文件,或者是之后的运行时就已将失效了,所以不起作用

下面就为 MyAnnotation 这个自定义注解设置存活期限,使用的是 @Retention 

其value值可以是

RetentionPolicy.Source:编译阶段就会丢弃的注释

RetentionPolicy.CLASS:编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。

RetentionPolicy.RUNTIME:编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。

我们还可以为自定义的注解指定可以添加的位置,默认是包,类,属性,方法,变量等都可添加,指定位置使用的是@Target ,其value值可以是一个数组

ElementType.TYPE:可以添加到类、接口(包括注释类型)或枚举声明上

ElementType.FIELD:可以添加到字段声明(包括枚举常量)

ElementType.METHOD:添加到方法上

ElementType.PACKAGE:添加到包上

等等

这两个注解我们称之为元注解

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

/**
* 注解也是一种特殊的类存在
*
@author LZ
*
*/
//设置注解类存活时间
@Retention(RetentionPolicy.RUNTIME)
//还可以限制注解的存在的位置(默认可以在任何地方比如类上,方法上,属性上,参数上等等)
//这里设置的MyAnnotation注解可以在类,方法上.{...}这是一个数组
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {}

给注解添加属性

我们在使用 @SuppressWarnings("deprecation") 这个注解的时候可以使用括号来添加值,如何让我们的自定义注解也可以添加属性呢?

就可以在我们自定义的这个注解类中使用 : 类型 属性名() [default] 默认值 的语法来添加属性

和普通类不同的一点在于属性后面竟然是一个括号,搞的像一个方法一样,但实际上我们通过反射得到注解的属性的时候,就是将这些属性类似于方法在调用。注解的属性类型可以是基本类型,字符串,数组类型,枚举,注解,Class类型:

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

/**
* 注解也是一种特殊的类存在
*
*/
//设置注解类存活时间
@Retention(RetentionPolicy.RUNTIME)
//还可以限制注解的存在的位置(默认可以在任何地方比如类上,方法上,属性上,参数上等等)
//这里设置的MyAnnotation注解可以在类,方法上.{...}这是一个数组
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {

//为注解添加属性
String value();

//添加默认值
int num() default 0;

//数组类型
String[] array();

int[] array2() default{3,2,1};

//枚举
WeekDay day() default WeekDay.SUN;

//注解类型
MyAnnotation2 myAnnotation2() default @MyAnnotation2(value="666");

//Class类型
Class clazz() default Integer.class;

}

可以在添加了我们这个自定义注解的类上来设置注解属性值,在方法中使用反射来获得这些值:

@MyAnnotation(value="abc",array={"3"},array2=6)
public class AnnotationTest {

public static void main(String[] args) {
System.runFinalizersOnExit(
true);

//通过反射可以检查注解Class类的isAnnotationPresent方法检查某个类是否存在注解
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
//得到注解
MyAnnotation myAnnotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
//想要使用注解,就需要设置注解存活时间
System.out.println(myAnnotation);
System.out.println(myAnnotation.value());
System.out.println(myAnnotation.num());
System.err.println(myAnnotation.array().length);
System.out.println(myAnnotation.array2().length);
System.out.println(myAnnotation.day());
System.out.println(myAnnotation.myAnnotation2());
System.out.println(myAnnotation.clazz());
}

}
}