文章目录
一、枚举
1. 对枚举的理解
- 枚举类的理解:类的对象只有有限个,确定的,我们此类为枚举类
- 当需要定义一组常量时,强烈建议使用枚举类
- 如果枚举类只有一个对象,则可以作为单例模式的实现方式
2. 如何定义枚举类
2.1 自定义枚举类
- 枚举类无法实例化对象。因此枚举类的构造器为私有的
- 枚举类要通过
类.成员名
调用成员变量,因此,成员必须是共有的
// 自定义枚举类
class Season{
// 1. 声明Season对象的属性
private final String season ;
// 2. 私有化类的有参构造器
private Season(String season){
this.season = season;
}
// 3. 定义当前枚举类的多个对象:public static final
public static final Season SPRING = new Season("Spring");
public static final Season SUMMER= new Season("Summer");
public static final Season FALL= new Season("Fall");
public static final Season WINTER= new Season("Winter");
// 4.1 对象属性的get()
public String getSeason(){
return this.season;
}
// 4.2 提供toString方法
@Override
public String toString() {
return "Season{" +
"season='" + season + '\'' +
'}';
}
}
2.2 使用enum关键字定义枚举类
- 使用enum关键字代替class关键字来定义一个类
- 使用enum关键字定义枚举类时,在类的语句块中,最先要枚举出所有的变量
- 多个枚举对象之间使用"逗号"来分隔开
- 使用enum修饰枚举类时,默认的toString方法为打印当前对象的对象名(这说明使用enum修饰的枚举类的父类并不是Object)
- 定义的枚举类默认继承于java.lang.Enum类
enum Week{
// 1. 提供当前枚举类的对象,多个对象之间用","隔开,末尾对象使用";"结束
MONDAY("星期一") ,
TUDEDAY("星期二"),
WEDNESAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
STAURDAY("星期六"),
SUNDAY("星期天");
// 2. 声明当前枚举类的属性,使用private final修饰
private final String weekInfo;
// 3. 提供带参构造方法
private Week(String str) {
this.weekInfo = str;
}
// 4. 提供使用的方法
public String getWeekInfo() {
return this.weekInfo;
}
}
- 定义枚举类时枚举的对象变量不在语句块首部,会报错:
2.3 自定义枚举类与使用enum定义枚举类的联系
- 自定义枚举类时,将枚举对象创建时,相同的部分去掉,并将枚举对象放在语句块首部,则就变成了使用enum关键字定义的枚举类
3. 枚举类常用方法
values()
:返回枚举类型的对象数组。该方法可以很方便第遍历所有的枚举值valueOf(String objName)
:返回枚举类中对象名是objName的对象;如果没有objName的枚举类对象,则抛出异常:IllegalArgumentExceptionpublic String toString()
:返回枚举对象的对象名(java的Enum类进行改写后的toString(),自定义枚举类可以进行重写)
常用方法测试:
- 使用上述enum关键字定义的枚举类来测试方法
@Test
public void testEnumMethdo(){
// 1. 返回枚举类所有成员的对象数据
Week[] values = Week.values();
for (Week value : values) {
// 2. 对枚举类对象进行输出,自动调用toString()方法
System.out.println(value);
}
// 3. 调用valueOf方法返回指定对象名的枚举类对象
// Week monday = Week.valueOf("Monday"); // 对象名称严格区分大小写
Week monday = Week.valueOf("MONDAY");
System.out.println("monday = " + monday);
}
4. 使用enum关键字定义的枚举类实现接口
- 情况一:实现接口,在enum类中实现抽象方法
- 情况二:让枚举类的对象分别是实现接口中的抽象方法
- 定义接口
enum EnumClass implements Host{
QITIYUANLIU("气体源流"){
@Override
public String showHost() {
return "张楚岚";
}
},
JULINGQIANJIANG("拘灵遣将"){
@Override
public String showHost() {
return "风星瞳";
}
},
TONGTIANLU("通天箓"){
@Override
public String showHost() {
return "张灵玉";
}
},
FENGHOUQIMEN("风后奇门"){
@Override
public String showHost() {
return "王也";
}
},
SHENJIBAILIAN("神级百炼"){
@Override
public String showHost() {
return "马仙洪";
}
},
DALUODONGGUAN("大罗洞观"){
@Override
public String showHost() {
return "谷畸亭";
}
},
LIUKUXIANZEI("六库仙贼"){
@Override
public String showHost() {
return "巴伦";
}
},
SHUANGQUANSHOU("双全手"){
@Override
public String showHost() {
return "吕良";
}
};
private String name ;
EnumClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "EnumClass{" +
"name='" + name + '\'' +
'}';
}
// 使用这种方式,所有的枚举类对象调用产生的都是同一个结果
// @Override
// public void showHost() {
// System.out.println("这是一个把奇技拥有者");
// }
}
- 对实现了接口的枚举类进行测试:
@Test
public void testMyEnumClass(){
EnumClass[] values = EnumClass.values();
for (EnumClass value : values) {
System.out.println("绝技:"+value);
System.out.println("拥有者:"+value.showHost());
}
EnumClass daluodongguan = EnumClass.valueOf("DALUODONGGUAN");
System.out.println("daluodongguan = " + daluodongguan);
}
// 结果:
绝技:EnumClass{name='炁体源流'}
拥有者:张楚岚
绝技:EnumClass{name='拘灵遣将'}
拥有者:风星瞳
绝技:EnumClass{name='通天箓'}
拥有者:张灵玉
绝技:EnumClass{name='风后奇门'}
拥有者:王也
绝技:EnumClass{name='神级百炼'}
拥有者:马仙洪
绝技:EnumClass{name='大罗洞观'}
拥有者:谷畸亭
绝技:EnumClass{name='六库仙贼'}
拥有者:巴伦
绝技:EnumClass{name='双全手'}
拥有者:吕良
daluodongguan = EnumClass{name='大罗洞观'}
二、注解
1. 概述
- 从JDK5.0开始,java增加了对元数据(MetaData)的支持,也就是Annotation(注解)
- 元数据即为注解
- 注解其实就是代码里的特殊标记,这些标记可以在编译,类加载、运行时内读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息,代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证
- 注解可以像"="像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的"value=value"对中
- 在javaSE中,注解的使用目的比较简单。例如标记过时的功能,忽略警告等。在javaEEAndroid中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替javaEE旧版中所遗留的冗杂代码和XML配置等
- 未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,Struts2有一部分也是基于注解的,
- 注解是一种趋势,一定程度上:可以说:框架 = 注解 + 反射 + 设计模式
2. 注解示例
- 生成文档相关的注解
- 在编译时进行格式检查(JDK内置的三个基本注解)
@Override
: 限定重写父类方法,该注解只能用于方法@Deprecated
:用以表示所修饰的元素已经过时。通常是因为所修饰的机构危险或存在更好的选择SuppressWarnings
:抑制编译器警告
- 跟踪代码依赖性,实现代替配置文件功能
3. JDK提供的4种元注解
元注解是用于修饰其他注解的定义:即对现有的注解进行修饰说明的注解
4个元注解:
@Retention
:指定该注解的生命周期,SOURCE\CLASS(默认行为)\RUNTIME
(只有声明为RUNTIME生命周期的注解,才能通过反射获取)
// Retention注解的源码定义
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value(); // 成员为一个枚举类型的变量
}
// 枚举类RetentionPolicy的源码定义
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE, // 表示被修饰的注解的有效期只存在于源代码中,编译后的.class文件通过反编译后无法看到该注解
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS, // 表示被修饰的注解的有效期保留至.class文件,.class文件可见,但对运行无影响
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME // 表示被修饰的注解保留到运行期,被JVM读取,可以通过反射被调用
}
@Target
:用于修饰注解定义,用于指定被修饰的注解能用于修饰那些程序元素,@Target也包含一个名为value的成员变量
// target注解的源码定义:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value(); // 成员:枚举数组
}
// ElementType的定义:枚举类,可修饰的元素类型;
ElementType的枚举成员:
1. TYPE:表示类,接口(包含注解类型),枚举类型
2. FIELD: 属性/成员变量(包括枚举常量)
3. METHOD:方法
4. PARAMETER: 参数
5. CONSTRUCTOR:构造器
6. LOCAL_VARIABLE:局部变量
7. ANNOTATION_TYPE:注解类型
8. PACKAGE:包
9. TYPE_PARAMETER:类型参数
10.TYPE_USE:所有使用类型的地方
/*
以上成员表示,如果一个注解被target修饰,且target的值为上述成员的某几
个,则表示被修饰的注解可以使用在上述没u成员所规定的地方
*/
@target注解测试:
- 自定义一个注解
// 使用Target注解修饰自定义注解表明该注解可作用的范围:
//类,接口,枚举类,注解,方法,类型参数(泛型)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.TYPE_PARAMETER})
public @interface TestTargetAnnotation {
}
- 定义一个类,使用自定义的注解进行标注:
- 在target的值中未给出的范围,使用@TestTargetAnnotation进行修饰全部报错
- 在target的值中未给出的范围,使用@TestTargetAnnotation进行修饰全部报错
@Documented
:用于指定被该Annotation修饰的Annotation类将被javadoc工具提取成文档。默认情况下,javadoc是不包括注释的- 定义为Documented的注解必须设置Retention值为RUNTIME
@Inherited
:被他修饰的Annotation将具有继承性。如果某个类使用了被Inherited修饰的注解,则其子类将自动具有该注解- 比如:如果把标有Inherited注解的自定义的注解标注在类级别上,子类则可以继承父类类级别的注解;
- 实际应用中,很少使用
inherited测试:
使用Inherited
修饰自定义的注解,来展现注解的继承性
- 自定义注解
//
@Inherited
@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
// 需要对Inherited的继承性进行验证,就需要使用反射,则需要将注解声明周期设置到为运行期
public @interface TestInheritedAnnotation {
String value();
}
- 自定义父类和子类,并用自定义的注解对父类进行标注
// 自定义父类
@SuppressWarnings("unchecked")
@Resource
@TestInheritedAnnotation("对父类进行标记")
public class SuperClass {
}
// 自定义子类
public class SubClass extends SuperClass{
}
- 进行测试,查看结果:
@Test
public void client(){
System.out.println("父类的注解:");
Class clazz = SuperClass.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType());
}
System.out.println("子类的注解:");
clazz = SubClass.class;
annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType());
}
}
- 结果
父类的注解:
interface javax.annotation.Resource
interface 测试inherited注解.TestInheritedAnnotation
子类的注解:
interface 测试inherited注解.TestInheritedAnnotation
- 这表明,使用了
@Inherited
修饰的注解,具有继承性,其可以随着子类继承父类,从而标记在子类之上
4. 自定义注解
可参考注解@SuppressWarnings
进行定义
自定义注解通常都会指定两个元注解:Retention,Target
- 注解声明为 @interface。
- 内部定义成员,通常使用value表示
- 可以指定成员的默认值,使用default定义
如果自定义注解没有成员,标明是一个标识作用
如果自定义注解有成员,在使用注解是,需要指明成员的值
@SuppressWarnings的定义
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
// 省略
/**
* The string "unchecked" is used to suppress unchecked warnings.
*/
// 字符串“unchecked”用于抑制未检查的警告。
// 省略
String[] value();
}
自定义的注解:
- 使用
@interface
进行注解的定义 - 可以添加一个成员变量/不添加成员变量
- 可以使用元注解对自定义注解进行修饰
@Target({ElementType.TYPE_USE,})
@SuppressWarnings("unchecked") // 抑制编译器警告
public @interface MyAnnotation {
// 注解的变量后带括号
String name() default "乾之三爻"; // name变量的设有默认值
String[] otherName();
}
// 自定义注解的测试:
@MyAnnotation(otherName = {"初九","风雨飘摇"}) // 对注解数组的赋值
public class TestMyAnnotation {
}
5. 注意事项
- 自定义类型注解自动继承了
java.lang.annotation.Annotation
接口 - Annotation的成员变量在Annotation定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称之为配置参数
注解成员变量类型:只能是八种基本数据类型以及String类型、Class类型、enum类型、Annotation类型以及这些类型的数组 - 可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可以使用default关键字
- 如果只有一个参数成员,建议使用参数名为value
- 如果定义的注解含有配置参数,那么使用是必须指定参数;除非它有默认值。格式是 “参数名” = “参数值”。如果只有一个而参数成员,且名称为value,那么可以省略 value=
- 没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation
- 自定义注解必须配上注解的信息处理流程才有意义
6. JDK8新特性
1. 重复注解
重复注解:对同一个目标使用多个相同的注解,在JDK8之前,这是不允许的。
直接进行重复注解会报错
- JDK8之前实现重复注解的方式
自定义注解
// 定义注解1:定义一个实现功能的注解
public @interface MyAnnotation {
String value();
}
// 定义注解2:定义一个注解,负责将上述定义的注解包装为数组变量
public @interface MyAnnotations {
MyAnnotation[] values();
}
使用重复注解:
@MyAnnotations(values = {@MyAnnotation("helloworld"),@MyAnnotation("乾之三爻")}) // JDK8之前的重复注解
public class Client {
}
- JDK8之后实现重复注解的方式
- 使用新注解
@Repeatable
对要进行重复注解的注解进行修饰,在本文中,即对@MyAnnotation
进行修饰,并在赋值时,通过反射MyAnnotations.class
与@MyAnnotations
进行绑定
依旧定义两个注解,其中一个注解的成员是注解数组,从而对另一个注解进行关联
// 定义注解1:
// 要求两个注解的变量必须是相同的名字
@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
// 定义注解2:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
使用重复注解
// JDK8之后进行重复注解:
@MyAnnotation("HelloWorld")
@MyAnnotation("QianZhiSanyao")
public class Client {
}
2. 类型注解
JDK8之后给@Target
注解新增加了两个作用范围,给枚举类ElementType
添加了两个成员
TYPE_PARAMETER
:类型参数,与泛型结合使用TYPE_USE
:使用类型
public <T> void show(@MyAnnotation("TYPE_PARAMETER") T t){
System.out.println(t);
}
public void count(@MyAnnotation("TYPE_USE") int i){
int count = 0 ;
for(@MyAnnotation("TYPE_USE") int j = 0 ; j < i ; j++,i--){
count += j ;
}
System.out.println(count);
}