枚举
在JDK1.5 之前,使用 public static final 定义常量,很难管理。 枚举,可以把相关的常量分组到一个枚举类型里,并且提供了比常量更多的方法。 用于定义有限数量的一组同类常量,例如: 四季: 春、夏、秋、冬
在枚举类型中定义的常量是该枚举类型的实例。
jdk1.5前
public class Level {
public static final Level LOW=new Level(1);
public static final Level Medium=new Level(50);
public static final Level High=new Level(100);
private int value;
private Level(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
定义格式
权限修饰符 enum 枚举名称 { 实例1,实例2,实例3,实例4; }
public enum Level {
LOW, MEDIUM, HIGH;
}
public enum Level {
LOW(30), MEDIUM(15), HIGH(7), URGENT(1);
private int levelValue;
private Level(int levelValue) {
this.levelValue = levelValue;
}
public int getLevelValue() {
return levelValue;
}
}
实现接口的枚举类
所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。 每个枚举对象,都可以实现自己的抽象方法
interface LShow{
void show();
}
public enum InterfaceLevel implements LShow{
LOW(1){
@Override
public void show(){
}
},HIGH(10){
@Override
public void show(){
}
};
private int levelValue;private InterfaceLevel(int levelValue) {
this.levelValue = levelValue;
}
public int getLevelValue() {
return levelValue;
}
}
注意:
- 枚举类默认继承的是java.lang.Enum类而不是Object类
- 枚举类不能有子类,默认被final修饰
- 只能有private构造方法
- 不能定义name属性,因为自带name属性
- 不要为枚举类中的属性提供set方法,不符合枚举类设计初衷。
注解
Annotation是一个接口,又称 Java 标注。注解是⼀种标记,使类,方法,成员,参数,接口,包等任何程序元素附加额外信息,帮助编译器和 JVM 完成⼀些特定功能。
和注释不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。(注解是通过反射获取的,可以通过 Class 对象的 isAnnotationPresent()方法判断它是否应 用了某个注解,再通过 getAnnotation()方法获取 Annotation 对象)它也支持自定义 Java 标注。
主要应用:
- 编译格式检查
- 反射中解析
- 生成帮助文档
- 跟踪代码依赖
重点:
- 概念
- 内置注解
- 自定义注解
- 反射中怎么获取注解内容
内置注解
- @Override
- @Deprecated
- @SuppressWarnings
- @FunctionalInterface
- ...
元注解
作用在其他注解的注解
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。用于约束生命周期,值是 RetentionPolicy 枚举常量,包括 SOURCE 源码、CLASS 字节码和 RUNTIME 运行时。
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在 class 文件中有效(即 class 保留)
- RUNTIME:在运行时有效(即运行时保留)
- @Documented - 标记这些注解是否包含在用户文档中 javadoc。
- @Target - 约束该注解作用位置,值是 ElementType 枚举常量,包括 METHOD 方法、VARIABLE 变量、TYPE 类/接口、PARAMETER 方法参数、CONSTRUCTORS 构造方法和 LOACL_VARIABLE 局部变量等。
- @Inherited - 标记这个注解是自动继承的(1. 子类会继承父类使用的注解中被@Inherited修饰的注解 2. 接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有 被@Inherited修饰 3. 类实现接口时不会继承任何接口中定义的注解)
每 1 个 Annotation 对象,都会有唯一的 RetentionPolicy 属性;有 1~n 个ElementType 属性.
当 Annotation 与某个 ElementType 关联 时,就意味着:Annotation有了某种用途。例如,若一个 Annotation 对象是 FIELD类型,则该 Annotation 只能用来修饰字段。
package java.lang.annotation;
public enum ElementType {
TYPE, /* 类、接口(包括注释类型)或枚举声明 */
FIELD, /* 字段声明(包括枚举常量) */
METHOD, /* 方法声明 */
PARAMETER, /* 参数声明 */
CONSTRUCTOR, /* 构造方法声明 */
LOCAL_VARIABLE, /* 局部变量声明 */
ANNOTATION_TYPE, /* 注释类型声明 */
PACKAGE /* 包声明 */
}
"每 1 个 Annotation" 都与 "1 个 RetentionPolicy" 关联。
- 若 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器 处理完之后,该 Annotation 就没用了。 例如," @Override" 标志就是一个 Annotation。当它修 饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处 理完后,"@Override" 就没有任何作用了。编译器可利用注解来探测错误和警告信息
- 若 Annotation 的类型为 CLASS,则意味着:编译器将 Annotation 存储于类对应的 .class 文件 中,它是 Annotation 的默认行为。软件工具可以利用注解信息来生成代码、html 文档或做其它相应处理;
- 若 Annotation 的类型为 RUNTIME,则意味着:编译器将 Annotation 存储于 class 文件中,并 且可由JVM读入。程序运行时可利用注解提取代码
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该
Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
自定义注解
- 用@Interface来声明注解(要使其正常工作,需要使用元注解)
- 定义的注解,自动继承了java.lang,annotation.Annotation接口
- 注解中的每一个方法,实际是声明的注解配置参数 (方法的名称就是 配置参数的名称 方法的返回值类型,就是配置参数的类型。只能是:基本类型/Class/String/enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串、0作为默认值。
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
参数类型 参数名() default 默认值;
}
上面的作用是定义一个 Annotation,我们可以在代码中通过 "@MyAnnotation" 来使用它。 @Documented, @Target, @Retention, @interface 都是来修饰 MyAnnotation 的。
(01) @interface 使用 @interface 定义注解时,表示它实现了 java.lang.annotation.Annotation 接口,即该注解就是 一个Annotation。 定义 Annotation 时,@interface 是必须的。 (它和通常的 implemented 实现接口的方法不同。Annotation 接口的实现细节都由编译器完 成。通过 @interface 定义注解后,该注解不能继承其他的注解或接口)
(02) @Documented 类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。 定义 Annotation 时,@Documented 可有可无;若没有定义,则 Annotation 不会出现在 javadoc 中。
(03) @Target(ElementType.TYPE) ,ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。 @Target(ElementType.TYPE) 的意思就是指定该 Annotation 的类型是 ElementType.TYPE。这就意味 着,MyAnnotation是来修饰"类、接口(包括注释类型)或枚举声明"的注解。 定义 Annotation 时,@Target 可有可无。若有 @Target,则该 Annotation 只能用于它所指定的地 方;若没有 @Target,则该 Annotation 可以用于任何地方。
(04) @Retention(RetentionPolicy.RUNTIME) ,RetentionPolicy 是 Annotation 的策略属性,而 @Retention 的作用,就是指定 Annotation 的策略属性。 @Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且 能被JVM读取。 定义 Annotation 时,@Retention 可有可无。若没有 @Retention,则默认是 RetentionPolicy.CLASS。
反射帮助程序员快速构造自定义注解处理器。
public class AnnotationDemo {
@Provider(id = 1, name = "名字", email = "邮件")
private String provider;
public void setProvider(String appleProvider) {
this.provider = appleProvider;
}
public String getProvider() {
return provider;
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface Provider {
public int id() default 0;
public String name() default "";
public String email() default "";
}
/*注解处理器*/
class Util {
public static void getInfo(Class<?> clazz) {
String provider = "";
Field[] fields = clazz.getDeclaredFields();//通过反射获取处理注解
for (Field field : fields) {
if (field.isAnnotationPresent(Provider.class)) {
Provider p = (Provider) field.getAnnotation(Provider.class);
//注解信息处理
provider = " 编号:" + p.id() + " 名称:"
+ p.name() + " 邮件:"+ p.email();
System.out.println(provider);
}
}
}
}