枚举类型
枚举的使用场景
类的对象是有限个的,确定的
星期:Monday(星期一)…Sunday(星期天) 性别:Man(男)、Woman(女) 季节:Spring(春节)…Winter(冬天) 当需要定义一组常量时,强烈建议使用枚举类
枚举类中对象外界不能创建,只能获取 枚举类中只有一个对象,可以作为单例模式的一种实现方式
枚举类的实现
JDK1.5之前需要自定义枚举类 JDK1.5新增enum关键字用于定义枚举类
如何自定义枚举类
class Season {
private final String SEASONNAME;
private final String SEASONDESC;
private Season ( String seasonName, String seasonDesc) {
this . SEASONNAME = seasonName;
this . SEASONDESC = seasonDesc;
}
public static final Season SPRING = new Season ( "春天" , "春暖花开" ) ;
public static final Season SUMMER = new Season ( "夏天" , "夏日炎炎" ) ;
public static final Season AUTUMN = new Season ( "秋天" , "秋高气爽" ) ;
public static final Season WINTER = new Season ( "冬天" , "白雪皑皑" ) ;
}
如何使用关键字enum定义枚举类
类只能使用public和默认修饰符修饰 使用enum 关键字定义的枚举类默认继承了java.lang.Enum类,因此不能再继承其他类,No extends clause allowed for enum
枚举类的构造器只能
使用private 权限修饰符 必须在枚举类的第一行
声明所有枚举类对象,枚举项之间,
逗号分隔;
分号结尾,列出的实例系统会自动添加public static final 修饰 JDK1.5中可以在switch表达式中使用Enum定义的枚举类的对象作为表达式,case子句可以直接使用枚举值的名字, 无需添加枚举类作为限定
enum SeasonEnum {
SPRING ( "春天" , "春风又绿江南岸" ) , SUMMER ( "夏天" , "映日荷花别样红" ) , AUTUMN ( "秋天" , "秋水共长天一色" ) , WINTER ( "冬天" , "窗含西岭千秋雪" ) ;
private final String seasonName;
private final String seasonDesc;
private SeasonEnum ( String seasonName, String seasonDesc) {
this . seasonName = seasonName;
this . seasonDesc = seasonDesc;
}
public String getSeasonName ( ) {
return seasonName;
}
public String getSeasonDesc ( ) {
return seasonDesc;
}
}
Enum类的主要方法
方法名 详细描述 public static <T extends Enum> T valueOf(Class enumType,String name) 传递枚举类型的Class对象和枚举常量名称给静态方法valueOf(),会得到与参数匹配的枚举常量 public String toString() 得到当前枚举常量的名称,可以重写(唯一 一个可重写的方法)这个方法得到更易读的结果 public final boolean equals(Object other) 在枚举奖型中可以直接使用==来比较两个枚举常量是否相等。Enum提供的这个equals()方法,也是直接使用==实现的。它的存在是为了在Set、List 和Map中使用 public final int hashCode() Enum实现了hashCode()来和equals()保持一致 public final Class getDeclaringClass() 得到枚举常量所属枚举类型的Class对象,可以用它来判断两个枚举常量是否属于同一个枚举类型 public final String name() 得到当前枚举常量的名称,建议优先使用toString() public final int ordinal() 得到当前枚举常量的次序(从0开始) public final int compareTo(E o) 枚举类型实现了Comparable接口,这样可以比较两个枚举常量的大小(按照声明的顺序排列) protected
final Object clone() throws CloneNotSupportedException枚举类型不能被clone
,为了防止子类实现克隆方法,Enum实现了一个仅抛出CloneNotSupponedException异常的clone()方法。Object类中clone()方法也是protected修饰的values() 返回枚举类型所有枚举值的对象数组
实现接口的枚举类
和普通Java类一样,枚举类可以实现一个或多个接口 若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可 若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法,例子参考实现枚举类型
部分
实现枚举类型
第一种格式(无成员变量无抽象方法)
enum First {
ZHEN, JIA;
}
第二种格式(有成员变量无抽象方法)
enum Second {
MON ( "星期一" ) , TUE ( "星期二" ) , WEN ( "星期三" ) ;
private String name;
Second ( String name) {
this . name = name;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
}
第三种格式(有成员变量有抽象方法)
enum Third {
MON ( "星期一" ) {
@Override
public void show ( ) {
System. out. println ( "抽象方法中的星期一" ) ;
}
} , TUE ( "星期二" ) {
@Override
public void show ( ) {
System. out. println ( "抽象方法中的星期二" ) ;
}
} , WEN ( "星期三" ) {
@Override
public void show ( ) {
System. out. println ( "抽象方法中的星期三" ) ;
}
} ;
private String name;
Third ( String name) {
this . name = name;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public abstract void show ( ) ;
}
注解
注解(Annotation)概述
JDK5.0开始,Java增加了Annotation(注解)的支持 Annotation其实就是代码里的特殊标记,这些标记可以在编译
、类加载
、运行时
被读取,并执行相应的处理 程序猿使用注解,可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。如:代码分析工具、开发工具和部署工具,可以通过这些补充信息进行验证或者进行部署 注解可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明 JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等 JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等 未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,现在的Struts2有一部分也是基于注解的了,注解是一种趋势,一定程度上可以说:框架=注解+反射+设计模式
常见的Annotation示例
生成API文档相关的注解
项目 Value 补充 @author 标明开发该类模块的作者,多个作者之间使用,分割 @author chenxubo @version 标明该类模块的版本 @version 1.0 @see 参考转向,也就是相关主题 @see Math.java @since 从哪个版本开始增加的 @param 对方法中某参数的说明,如果没有参数就不能写 只用于方法的,可以并列多个,格式要求:@param 形参名 形参类型 形参说明 @return 对方法返回值的说明,如果方法的返回值类型是void就不能写 只用于方法的,格式要求:@return 返回值类型 返回值说明 @exception 对方法可能抛出的异常进行说明,如果方法没有用throws显式抛出的异常就不能写 只用于方法的,可以并列多个,格式要求:@exception 异常类型 异常说明
编译时进行格式检查的注解
注解 Value @Override 限定重写父类方法,该注解只能用于方法 @Deprecated 用于表示所修饰的元素(类、方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择 @SuppressWarnings 抑制编译器警告
框架中实现替代配置文件的注解
注解 Value @Transactional spring框架中关于事务的管理 @WebServlet("/") JavaEE不再需要在web.xml文件中进行Servlet的部署
自定义Annotation
定义新的Annotation类型使用@interface
关键字 自定义的注解自动继承
了java.lang.annotation.Annotation接口 Annotation的成员变量在Annotation定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型。成员变量的类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型和以上所有类型的数组 可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字 如果只有一个参数成员,建议参数名为value 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是“参数名=参数值”,如果只有一个参数成员,且名称为value,可以省略“value=” 没有成员定义的Annotation称为标记,包含成员变量的Annotation称为元数据Annotation 自定义注解必须配上注解的信息处理流程才有意义
class TestTypeDefine < @TypeDefine ( ) U> {
private U u;
public < @TypeDefine ( ) T> void test ( T t) {
}
}
@Target ( { ElementType. TYPE_PARAMETER} )
@interface TypeDefine {
}
@MyAnnotation ( "chenxubo" )
class MyAnnotationTest {
public static void main ( String[ ] args) {
Class clazz = MyAnnotationTest. class ;
Annotation a = clazz. getAnnotation ( MyAnnotation. class ) ;
MyAnnotation m = ( MyAnnotation) a;
String info = m. value ( ) ;
System. out. println ( info) ;
}
}
@Retention ( RetentionPolicy. RUNTIME)
@Target ( ElementType. TYPE)
@interface MyAnnotation {
String value ( ) default "cxb" ;
}
JDK中的元注解
JDK的元注解用于修饰其他Annotation的定义(修饰注解的注解称元注解) JDK5.0提供了4个标准的元注解,分别是:
@Retention @Target @Documented @Inherited
Java注解中的元注解
@Retention
修饰Annotation的定义,指定该Annotation的生命周期 @Rentention包含一个RetentionPolicy
类型的成员变量,使用@Rentention时必须为该value成员变量指定值 RetentionPolicy.SOURCE:在源文件中有效(源文件保留,编译器直接丢弃这种策略的注释) RetentionPolicy.CLASS:在class文件中有效(字节码文件中保留,当运行Java程序时,JVM不会保留注解) RetentionPolicy.RUNTIME:在运行时有效(运行时保留,当运行Java程序时,JVM会保留注解,程序可以通过反射获取该注解) RetentionPolicy.SOURCE和RetentionPolicy.CLASS策略下获取注解的值抛出java.lang.NullPointerException
@Target
修饰Annotation的定义,用于指定被修饰的Annotation能用于修饰哪些程序元素 @Target包含了一个名为value的ElementType[]
类型的成员变量
@Documented
@Documented修饰的Annotation将被javadoc工具提取成文档,默认情况下javadoc是不包括注解的
@Inherited
@Inherited修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation, 则其子类将自动具有该注解 把标有@Inherited注解的自定义的注解标注在类级别上,子类则可以继承父类类级别的注解
利用反射获取注解信息(在反射部分涉及)
利用反射获取注解信息需要修饰注解的@Retention元注解取值为RUNTIME,也就是必须被虚拟机保留 当@Retention注解的取值不是RUNTIME的时候,就不能通过反射来获取注解信息了,但是可以使用javac来处理注解 JDK5.0在java.lang.reflect包下新增了AnnotatedElement接口,AnnotatedElement代表在当前运行的JVM中一个可以被注解的元素,这个接口允许通过反射读取元素上面的注解
限定词 解释 directly present 元素上显式声明的注解 indrectly present 可重复注解和包含注解类型的情况 present 在directly present基础上增加了继承的情况 associated 在directly present和indrectly的基础上增加了继承的情况
限定词作用:AnnotatedElement中的方法都是根据这些限定词来返回注解的,也就是说,不同的方法可能返回不同限定词指定的注解
方法 Directly Present Indirectly Present Present Associated T getAnnotation(Class) ✅ Annotation[] getAnnotations ✅ T[] getAnnotationsByType(Class) ✅ T getDeclaredAnnotation(Class annotationClass) ✅ T[] getDeclaredAnnotationsByType(Class annotationClass) ✅ ✅ Annotation[] getDeclaredAnnotations() ✅
import java. lang. annotation. *;
import java. util. Arrays;
@Retention ( RetentionPolicy. RUNTIME)
@Target ( ElementType. TYPE)
@interface DirectlyPresent {
int id ( ) ;
String description ( ) default "this is a common description" ;
}
@Retention ( RetentionPolicy. RUNTIME)
@Target ( ElementType. TYPE)
@Repeatable ( IndirectlyPresent. class )
@interface IndirectlyPresentValue {
String name ( ) ;
}
@Target ( ElementType. TYPE)
@Retention ( RetentionPolicy. RUNTIME)
@interface IndirectlyPresent {
IndirectlyPresentValue[ ] value ( ) ;
}
@Target ( ElementType. TYPE)
@Retention ( RetentionPolicy. RUNTIME)
@Inherited
@interface InheritablePresent {
}
@InheritablePresent ( )
class ParentPresentElement {
}
@DirectlyPresent ( id = 2 , description = "this is a DirectlyPresentElement" )
@IndirectlyPresent ( { @IndirectlyPresentValue ( name = "firstName" ) } )
public class PresentElement extends ParentPresentElement {
public static void main ( String[ ] args) {
Class< PresentElement> directlyPresentElementClass = PresentElement. class ;
Annotation[ ] declaredAnnotations = directlyPresentElementClass. getDeclaredAnnotations ( ) ;
System. out. println ( Arrays. toString ( declaredAnnotations) ) ;
DirectlyPresent[ ] declaredAnnotationsByType1 = directlyPresentElementClass. getDeclaredAnnotationsByType ( DirectlyPresent. class ) ;
System. out. println ( Arrays. toString ( declaredAnnotationsByType1) ) ;
IndirectlyPresent[ ] declaredAnnotationsByType2 = directlyPresentElementClass. getDeclaredAnnotationsByType ( IndirectlyPresent. class ) ;
System. out. println ( Arrays. toString ( declaredAnnotationsByType2) ) ;
IndirectlyPresentValue[ ] declaredAnnotationsByType3 = directlyPresentElementClass. getDeclaredAnnotationsByType ( IndirectlyPresentValue. class ) ;
System. out. println ( Arrays. toString ( declaredAnnotationsByType3) ) ;
Annotation[ ] annotations = directlyPresentElementClass. getAnnotations ( ) ;
System. out. println ( Arrays. toString ( annotations) ) ;
}
}
JDK8中注解的新特性
Java8对注解处理提供了两点改进:可重复
注解及类型
注解 Java8以前,同一个程序元素前最多只能有一个相同类型的注解,Java8新增了重复注解@Repeatable
Java8以前,注解只能用在各种程序元素(定义类、定义接口、定义方法、定义成员变量…)上,Java8开始,类型注解可以用在任何使用到类型的地方 Java8关于元注解@Target的参数类型ElementType枚举值多了两个:TYPE_PARAMETER、TYPE_USE
ElementType.TYPE_PARAMETER:表示该注解能写在类型参数的声明语句中。类型参数声明如: <T>、<T extends Person> ElementType.TYPE_USE:表示注解可以在任何用到类型的地方使用,比如创建对象、类型转换、使用implements实现接口、使用throws声明抛出异常
@MyAnnotation
class AnnotationTest < U> {
@MyAnnotation
private String name;
public static void main ( String[ ] args) {
AnnotationTest< @MyAnnotation String> t = null;
int a = ( @MyAnnotation int ) 2 L;
@MyAnnotation
int b = 10 ;
}
public static < @MyAnnotation T> void method ( T t) {
}
public static void test ( @MyAnnotation String arg) throws @MyAnnotation Exception {
}
}
@Target ( ElementType. TYPE_USE)
@interface MyAnnotation {
}
JDK8新特性|重复注解与类型注解
注解使用时注意点
注解中属性只有一个并且为value时可以省略属性名,直接写属性值 注解中属性给默认值,使用时可以不给该属性赋值 注解中属性的类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型和以上所有类型的数组,数组只有一个元素大括号可以不写
@interface MyAnnotation {
String name ( ) ;
int age ( ) default 10 ;
String value ( ) ;
boolean [ ] is ( ) ;
}
class MyAnnotationTest {
@MyAnnotation ( name = "aaa" , value = "bbb" , is = true )
public static void m1 ( ) {
}
}