05 JAVA枚举类、注解与比较器
5.1 枚举类
类的对象只有有限个,确定的。当需要定义一组常量时,强烈建议使用枚举类。
- JDK1.5之前需要自定义枚举类
- JDK 1.5 新增的 enum 关键字用于定义枚举类
5.1.1 属性
-
枚举类对象的属性不应允许被改动,所以应该使用 private final 修饰
-
枚举类的使用 private final 修饰的属性应该在构造器中为其赋值
-
若枚举类显式的定义了带参数的构造器,则在列出枚举值时也必须对应的传入参数
5.1.2 自定义枚举类
- 私有化类的构造器,保证不能在类的外部创建其对象
- 在类的内部创建枚举类的实例。声明为: public static final
- 对象如果有实例变量,应该声明为private final,并在构造器中初始化
class Season{
private final String SEASONNAME;//季节的名称
private final String SEASONDESC;//季节的描述
// 定义私有化的构造器,对final的常量进行初始化
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("冬天", "白雪皑皑");
}
5.1.3 enum关键字
使用enum关键字定义枚举类。定义的枚举类默认继承于 java.lang.Enum 类。
- 在枚举类的最开始要提供当前枚举类的对象
- 在对象之间使用逗号隔开,最后一个对象使用分号结束
- 其他定义与自定义实现枚举类相同
interface Info{
void show();
}
//使用enum关键字枚举类并实现接口
enum Season1 implements Info{
//1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
SPRING("春天","春暖花开"){
@Override
public void show() {
System.out.println("春天在哪里?");
}
},
SUMMER("夏天","夏日炎炎"){
@Override
public void show() {
System.out.println("宁夏");
}
},
AUTUMN("秋天","秋高气爽"){
@Override
public void show() {
System.out.println("秋天不回来");
}
},
WINTER("冬天","冰天雪地"){
@Override
public void show() {
System.out.println("大约在冬季");
}
};
//2.声明Season对象的属性:private final修饰
private final String seasonName;
private final String seasonDesc;
//3.私有化类的构造器,并给对象属性赋值
private Season1(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//4.其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
5.2 注解
5.2.1 概述
从 JDK 5.0 开始,Java 增加了对元数据(MetaData) 的支持,也就是Annotation(注解)
-
Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。 代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署
-
Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在 Annotation的 “name=value” 对中
-
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面, 代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
-
未来的开发模式都是基于注解的, JPA是基于注解的, Spring2.5以上都是基于注解的, Hibernate3.x以后也是基于注解的,现在的Struts2有一部分也是基于注解的了,注解是一种趋势,一定程度上可以说: 框架 = 注解 + 反射 + 设计模式。
5.2.2 自定义注解
-
定义新的 Annotation 类型使用 @interface 关键字
-
自定义注解自动继承了
java.lang.annotation.Annotation
接口 -
Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明。 其方法名和返回值定义了该成员的名字和类型。 我们称为配置参数。 类型只能是八种基本数据类型、 String类型、 Class类型、 enum类型、 Annotation类型、以上所有类型的数组。
-
可以在定义 Annotation 的成员变量时为其指定初始值, 指定成员变量的初始值可使用 default 关键字
-
如果只有一个参数成员, 建议使用参数名为value
-
如果定义的注解含有配置参数, 那么使用时必须指定参数值, 除非它有默认值。 格式是“参数名 = 参数值” , 如果只有一个参数成员, 且名称为value,可以省略“value=”
-
没有成员定义的 Annotation 称为标记; 包含成员变量的 Annotation 称为元数据 Annotation
注意: 自定义注解必须配上注解的信息处理流程才有意义
5.3 比较器
在 Java 中经常会涉及到对象数组的排序问题,那么就会涉及到对象之间的比较问题。正常情况下,会使用 == 或 != 进行比较,而不能使用大于或小于号进行比较。在实际的开发场景中,我们需要对多个对象进行排序,此时就需要对多个对象的大小进行比较,此时就需要使用比较器:
- 自然排序:Comparable【默认实现】
- 定制排序:Comparator
5.3.1 自然排序
Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序叫做类的自然排序。
(1)比较规则
实现 Comparable 的类必须重写 compareTo(obj) 方法,两个对象通过 compareTo(obj) 方法的返回值来比较大小:
- 如果当前对象this大于形参对象obj,则返回正整数
- 如果当前对象this小于形参对象obj,则返回负整数
- 如果当前对象this等于形参对象obj, 则返回零
(2)具体实现类
Comparable 的典型实现: (默认都是从小到大排列的)
- String:按照字符串中字符的Unicode值进行比较
- Character:按照字符的Unicode值来进行比较
- 数值类型对应的包装类以及BigInteger、 BigDecimal:按照它们对应的数值大小进行比较
- Boolean: true 对应的包装类实例大于 false 对应的包装类实例
- Date、 Time等:后面的日期时间比前面的日期时间大
像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,就可以实现对两个对象的从小到大的排序。
// 具体实现类——String类
@Test
public void test1(){
String[] arr = new String[]{"AA","RR","FF","GG","BB"};
// 排序sort()方法会调用String类中的比较器
Arrays.sort(arr);
// 输出:[AA, BB, FF, GG, RR]
System.out.println(Arrays.toString(arr));
}
(3)自定义实现类
自定义商品类,但是未实现Comparable接口,此时去对商品对象进行排序时,将会报类型转换异常:
因为在调用sort()方法时,系统将会默认去调用自然排序比较器,但是由于未对其进行重写,所以会报异常转换错误。对于自定义类而言,若需要排序,则需要让自定义类实现Comparable接口,并重写其中的compareTo()方法,在compareTo()方法中指明如何排序。
// 重写:指明商品按照商品价格的大小进行排序
@Override
public int compareTo(Object o) {
// 判断对象的类型【此时与重写equals()方法相似】
if (o instanceof Goods){
Goods goods = (Goods) o;
if (this.price>goods.price) return 1;
else if (this.price<goods.price) return -1;
else {
// 当商品的价格相同时,再按照name进行排序【直接调用String类的compareTo()方法】
return this.name.compareTo(goods.name);
}
}
// 抛出运行时异常,此时就不需要处理异常
throw new RuntimeException("对象类型不一致!");
}
5.3.2 定制排序
当元素的类型没有实现 Comparable 接口而且又不方便修改代码时,或者实现了 Comparable 接口的排序规则又不适合当前的操作,那么可以考虑使用定制排序 Comparator 的对象来实现,强行对多个对象进行整体排序。
(1)比较规则
重写compare(o1,o2)方法,比较o1和o2的大小:
- 返回正整数,表示o1大于o2
- 返回0,表示相等
- 返回负整数,表示o1小于o2
(2)具体实现类
定制排序的参数有两个,而自然排序的参数只有一个。对String具体实现类的定制排序如下:
// 定制排序:使用匿名内部类实现
@Test
public void test3(){
String[] arr = new String[]{"AA", "RR", "FF", "GG", "BB"};
// 使用匿名内部类进行重写Comparator
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 实现从大到小排序,调用String内已经实现的Comparable接口的方法compareTo()
return -o1.compareTo(o2);
}
});
// 输出:[RR, GG, FF, BB, AA]
System.out.println(Arrays.toString(arr));
}
// 定制排序:lambda表达式
@Test
public void test3(){
String[] arr = new String[]{"AA", "RR", "FF", "GG", "BB"};
// 使用lambda表达式进行重写Comparator
Arrays.sort(arr, (o1, o2) -> -o1.compareTo(o2));
System.out.println(Arrays.toString(arr));
}
(3)自定义实现类
@Test
public void test4() {
Goods[] arr = new Goods[5];
arr[0] = new Goods("z11", 511);
arr[1] = new Goods("z21", 211);
arr[2] = new Goods("z31", 311);
arr[3] = new Goods("z41", 411);
arr[4] = new Goods("z41", 311);
// 定制排序:价格从高到低,name从低到高
Arrays.sort(arr, new Comparator<Goods>() {
@Override
public int compare(Goods o1, Goods o2) {
if (o1.getPrice() > o2.getPrice()) return -1;
else if (o1.getPrice() < o2.getPrice()) return 1;
else return o1.getName().compareTo(o2.getName()); //compareTo是String类的
}
});
// lambda表达式
//Arrays.sort(arr, (o1, o2) -> {
// if (o1.getPrice() > o2.getPrice()) return -1;
// else if (o1.getPrice() < o2.getPrice()) return 1;
// else return o1.getName().compareTo(o2.getName());
//});
System.out.println(Arrays.toString(arr));
}
5.3.3 总结
- 左大右小返回正数【正数为几都可以】;左小右大返回负数;左右相等返回0
Comparable | Comparator | |
---|---|---|
使用范围 | 由某一个具体的类去实现Comparable方法,并重写其compareTo(obj)方法 | 只在实际运用比较器时,临时去实现一个定制比较器Comparator,并重写其中的compare(obj1,obj2)方法 |