1 Java 注解
1.1 概念
- 定义:注解(Annotation)是在 Java 1.5 时引入的概念,同
class
和interface
一样,属于一种类型。 - 作用:注解提供了一系列数据用来装饰程序代码(类、方法、字段等),但注解并不是所装饰代码的一部分,它对代码的运行效果没有直接影响,由编译器决定该执行哪些操作。
1.2 注解的生命周期策略
- 定义:定义在
RetentionPolicy
枚举中。 - 类型:
- SOURCE:在源文件中有效,被编译器丢弃。
- CLASS:在编译器生成的字节码文件中有效,但在运行时会被处理类文件的 JVM 丢弃。
- RUNTIME:在运行时有效。这是注解生命周期中最常用的一种策略,它允许程序通过反射的方式访问注解,并根据注解的定义执行相应的代码。
1.3 注解的类型
- 定义:定义在
ElementType
枚举中。 - 类型:
- TYPE:用于类、接口、注解、枚举。
- FIELD:用于字段(类的成员变量),或者枚举常量。
- METHOD:用于方法。
- PARAMETER:用于普通方法或者构造方法的参数。
- CONSTRUCTOR:用于构造方法。
- LOCAL_VARIABLE:用于变量。
- ANNOTATION_TYPE:用于注解。
- PACKAGE:用于包。
- TYPE_PARAMETER:用于泛型参数。
- TYPE_USE:用于声明语句、泛型或者强制转换语句中的类型。
- MODULE:用于模块。
1.4 代码示例
1.4.1 编写JsonField
注解
/*** @package: com.yunyang.javabetter.oop.annotation* @description: 自定义JsonField 注解* @author: Yunyang* @date: 2024/10/16 14:18* @version:1.0**/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonField {public String value() default "";
}
分析:
- JsonField 注解的生命周期是 RUNTIME,也就是运行时有效。
- JsonField 注解装饰的目标是 FIELD,也就是针对字段的。
- 创建注解需要用到 @interface 关键字。
- JsonField 注解有一个参数,名字为 value,类型为 String,默认值为一个空字符串。
- value 允许注解的使用者提供一个无需指定名字的参数。举个例子,我们可以在一个字段上使用 @JsonField(value = “沉默王二”),也可以把 value = 省略,变成 @JsonField(“沉默王二”)。
default ""
允许我们在一个字段上直接使用 @JsonField,而无需指定参数的名和值。
1.4.2 编写Writer
类
假设有一个 Writer 类,他有 3 个字段,分别是 age、name 和 bookName,后 2 个是必须序列化的字段。就可以这样来用 @JsonField 注解。
/*** @package: com.yunyang.javabetter.oop.annotation* @description: Writer类* @author: Yunyang* @date: 2024/10/16 14:26* @version:1.0**/
public class Writer {private int age;@JsonField("writerName")private String name;@JsonFieldprivate String bookName;public Writer(int age, String name, String bookName) {this.age = age;this.name = name;this.bookName = bookName;}// getter / setter@Overridepublic String toString() {return "Writer{" +"age=" + age +", name='" + name + '\'' +", bookName='" + bookName + '\'' +'}';}
}
分析:
- name 上的 @JsonField 注解提供了显式的字符串值。
- bookName 上的 @JsonField 注解使用了缺省项。
1.4.3 编写序列化类 JsonSerializer
/*** @package: com.yunyang.javabetter.oop.annotation* @description: 序列化类 JsonSerializer* @author: Yunyang* @date: 2024/10/16 14:27* @version:1.0**/
public class JsonSerializer {/*** serialize() 方法是用来序列化对象的,它接收一个 Object 类型的参数* @param object* @return* @throws IllegalAccessException*/public static String serialize(Object object) throws IllegalAccessException {Class<?> objectClass = object.getClass();Map<String, String> jsonElements = new HashMap<>();// objectClass.getDeclaredFields()// 通过反射的方式获取对象声明的所有字段,然后进行 for 循环遍历for (Field field : objectClass.getDeclaredFields()) {// 通过 field.setAccessible(true) 将反射对象的可访问性设置为 true,供序列化使用field.setAccessible(true);// 通过 isAnnotationPresent() 判断字段是否装饰了 JsonField 注解if(field.isAnnotationPresent(JsonField.class)){// 如果是的话,调用 getSerializedKey() 方法,以及获取该对象上由此字段表示的值,并放入 jsonElements 中jsonElements.put(getSerializedKey(field), (String) field.get(object));}}return toJsonString(jsonElements);}/*** getSerializedKey() 方法用来获取字段上注解的值,如果注解的值是空的,则返回字段名* @param field* @return*/private static String getSerializedKey(Field field) {String annotationValue = field.getAnnotation(JsonField.class).value();if(annotationValue.isEmpty()){return field.getName();} else {return annotationValue;}}/*** toJsonString() 方法借助 Stream 流的方式返回格式化后的 JSON 字符串* @param jsonMap* @return*/private static String toJsonString(Map<String, String> jsonMap) {String elementsString = jsonMap.entrySet().stream().map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"").collect(Collectors.joining(","));return "{" + elementsString + "}";}}
1.4.4 测试类 JsonFieldTest
/*** @package: com.yunyang.javabetter.oop.annotation* @description: 测试类 JsonFieldTest* @author: Yunyang* @date: 2024/10/16 14:42* @version:1.0**/
public class JsonFieldTest {public static void main(String[] args) throws IllegalAccessException {Writer writer = new Writer(18, "zhangsan", "Java进阶之路");System.out.println(JsonSerializer.serialize(writer));}
}
运行结果:
{"bookName":"Java进阶之路","writerName":"zhangsan"}
分析:
Writer
类的age
字段没有装饰@JsonField
注解,所以没有序列化Writer
类的name
字段装饰了@JsonField
注解,并且显示指定了字符串“writerName”,所以序列化后变成了 writerNameWriter
类的bookName
字段装饰了@JsonField
注解,但没有显式指定值,所以序列化后仍然是 bookName
2 Java 枚举
2.1 概念
- 定义:枚举(enum)是 Java 1.5 时引入的关键字,表示一种特殊类型的类,继承自
java.lang.Enum
。
2.2 新建枚举 PlayerType
public enum PlayerType {TENNIS,FOOTBALL,BASKETBALL
}
2.2.1 反编译后的字节码
public final class PlayerType extends Enum
{public static PlayerType[] values(){return (PlayerType[])$VALUES.clone();}public static PlayerType valueOf(String name){return (PlayerType)Enum.valueOf(com/cmower/baeldung/enum1/PlayerType, name);}private PlayerType(String s, int i){super(s, i);}public static final PlayerType TENNIS;public static final PlayerType FOOTBALL;public static final PlayerType BASKETBALL;private static final PlayerType $VALUES[];static {TENNIS = new PlayerType("TENNIS", 0);FOOTBALL = new PlayerType("FOOTBALL", 1);BASKETBALL = new PlayerType("BASKETBALL", 2);$VALUES = (new PlayerType[] {TENNIS, FOOTBALL, BASKETBALL});}
}
2.2.2 Java 编译器对枚举的隐式工作
- 继承:枚举类隐式继承自
java.lang.Enum
。 - 构造方法:编译器会为枚举生成一个私有的构造方法。
- 静态变量和数组:编译器会为枚举生成静态变量和数组来存储枚举常量。
- 静态块:编译器会使用静态块来初始化静态变量和数组。
- 静态方法:编译器会提供
values()
和valueOf(String name)
等静态方法。
2.3 枚举的作用域
- 内部类:枚举可以定义在一个类的内部,作用域限定于外部类中。
public class Player {private PlayerType type;public enum PlayerType {TENNIS,FOOTBALL,BASKETBALL}public boolean isBasketballPlayer() {return getType() == PlayerType.BASKETBALL;}public PlayerType getType() {return type;}public void setType(PlayerType type) {this.type = type;}
}
分析:
PlayerType 就相当于 Player 的内部类
2.4 枚举的比较
- 使用
==
运算符:由于枚举是final
的,可以使用==
运算符比较两个枚举是否相等。 - 不使用
equals()
方法:==
运算符在比较时不会抛出NullPointerException
。==
运算符在编译时会检查类型匹配,而equals()
方法不会。
2.5 枚举与 switch
语句
- 用法:枚举可以用于
switch
语句,与基本数据类型的用法一致。 - 示例
switch (playerType) {case TENNIS:return "网球运动员费德勒";case FOOTBALL:return "足球运动员C罗";case BASKETBALL:return "篮球运动员詹姆斯";case UNKNOWN:throw new IllegalArgumentException("未知");default:throw new IllegalArgumentException("运动员类型: " + playerType);}
2.6 枚举与带参数的构造方法
如果枚举中需要包含更多信息的话,可以为其添加一些字段,比如下面示例中的 name,此时需要为枚举添加一个带参的构造方法,这样就可以在定义枚举时添加对应的名称了。
- 示例:
public enum PlayerType {TENNIS("Tennis"),FOOTBALL("Football"),BASKETBALL("Basketball");private String name;PlayerType(String name) {this.name = name;}public String getName() {return name;}
}
2.7 EnumSet
- 定义:
EnumSet
是专门针对枚举类型的Set
接口的实现类,非常高效。 - 创建:不能使用
new
关键字创建EnumSet
,可以使用静态工厂方法。 - 示例:
/*** @package: com.yunyang.javabetter.oop.enumdemo* @description: EnumSet* @author: Yunyang* @date: 2024/10/16 15:28* @version:1.0**/
public class EnumSetTest {public enum PlayerType{TENNIS,FOOTBALL,BASKETBALL}public static void main(String[] args) {EnumSet<PlayerType> enumSetNone = EnumSet.noneOf(PlayerType.class);System.out.println(enumSetNone);EnumSet<PlayerType> enumSetAll = EnumSet.allOf(PlayerType.class);System.out.println(enumSetAll);}
}
运行结果:
[]
[TENNIS, FOOTBALL, BASKETBALL]
分析:
使用 noneOf() 静态工厂方法创建了一个空的 PlayerType 类型的 EnumSet;使用 allOf() 静态工厂方法创建了一个包含所有 PlayerType 类型的 EnumSet。
2.8 EnumMap
- 定义:
EnumMap
是专门针对枚举类型的Map
接口的实现类,效率比HashMap
高。 - 创建:可以使用
new
关键字创建EnumMap
。 - 示例:
/*** @package: com.yunyang.javabetter.oop.enumdemo* @description: EnumMap* @author: Yunyang* @date: 2024/10/16 15:33* @version:1.0**/
public class EnumMapTest {public enum PlayerType {TENNIS("网球"),FOOTBALL("足球"),BASKETBALL("篮球");private String name;PlayerType(String name) {this.name = name;}}public static void main(String[] args) {EnumMap<PlayerType, String> enumMap = new EnumMap<>(PlayerType.class);enumMap.put(PlayerType.BASKETBALL,"篮球运动员");enumMap.put(PlayerType.FOOTBALL,"足球运动员");enumMap.put(PlayerType.TENNIS,"网球运动员");System.out.println(enumMap);System.out.println(enumMap.get(PlayerType.BASKETBALL));System.out.println(enumMap.get(PlayerType.FOOTBALL));System.out.println(enumMap.get(PlayerType.TENNIS));System.out.println(enumMap.containsKey(PlayerType.TENNIS));}
}
运行结果:
{TENNIS=网球运动员, FOOTBALL=足球运动员, BASKETBALL=篮球运动员}
篮球运动员
足球运动员
网球运动员
true
2.9 枚举实现单例
2.9.1 概念
- 单例模式:保证一个类仅有一个对象,并提供全局访问点。
- Java 标准库有一些类就是单例,比如说 Runtime 这个类
2.9.2 volatile、synchronized关键字实现单例
/*** @package: com.yunyang.javabetter.oop.enumdemo* @description: volatile、synchronized关键字实现单例* @author: Yunyang* @date: 2024/10/16 15:38* @version:1.0**/
public class Singleton {private volatile static Singleton singleton;private Singleton() {}public static Singleton getSingleton() {if(singleton == null){synchronized (Singleton.class){if(singleton == null){singleton = new Singleton();}}}return singleton;}
}
2.9.3 枚举实现单例
/*** @package: com.yunyang.javabetter.oop.enumdemo* @description: 枚举实现单例* @author: Yunyang* @date: 2024/10/16 15:40* @version:1.0**/
public enum EasySingleton {INSTANCE;
}
- 优点:枚举默认实现了
Serializable
接口,Java 虚拟机保证该类为单例。
3 思维导图
4 参考链接
- Java注解,请别小看我
- Java枚举:小小enum,优雅而干净