文章目录
- [Ⅰ. 枚举类定义与使用](#Ⅰ. 枚举类定义与使用)
- [Ⅱ. 枚举类的构造方法默认就是 `private`](#Ⅱ. 枚举类的构造方法默认就是
private) - 总结
- [Ⅲ. 什么是 `Lambda` 表达式](#Ⅲ. 什么是
Lambda表达式) - [Ⅳ. `Lambda` 表达式的使用](#Ⅳ.
Lambda表达式的使用) - 列表排序的常用写法
-
- [1. **基本比较(数值)**](#1. 基本比较(数值))
-
- [`Integer.compare(a, b)`](#
Integer.compare(a, b))
- [`Integer.compare(a, b)`](#
- [2. **方法引用 +** **`Comparator.comparing`**](#2. 方法引用 +
Comparator.comparing) - [3. **多条件排序**](#3. 多条件排序)
- [4. **倒序排序**](#4. 倒序排序)
- [5. **处理 null 值**](#5. 处理 null 值)
- [6. **字符串比较(忽略大小写)**](#6. 字符串比较(忽略大小写))
- [7. **完全自定义规则**](#7. 完全自定义规则)

Ⅰ. 枚举类定义与使用
typescript
public enum Color {
RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, PINK, BROWN, BLACK, WHITE;
}
每个我们写的 enum 类都会默认继承于一个抽象类 Enum,如下图所示:

而这个 Enum 抽象类中有挺多方法,如下所示:

下面是使用举例:
java
public static void main(String[] args) {
// 使用Color.RED直接访问枚举元素,或者直接RED都行,但不能使用new来生成枚举类对象
Color color = Color.RED;
System.out.println(color.name()); // 输出颜色名称
System.out.println(color.ordinal()); // 输出颜色的序号
System.out.println(color); // 直接输出颜色对象
System.out.println(color.describeConstable()); // 输出颜色的描述信息
System.out.println(Color.valueOf("BLACK")); // 通过名称获取枚举值
// 有个比较特殊的是values()方法,它可以返回所有枚举值数组,它不存在Enum中,而是在编译时由编译器生成的。
// 但是如果枚举值太多,可能导致内存溢出,所以一般不用这个方法。
System.out.println("----------------------输出所有枚举元素:");
Color[] colors = Color.values();
for(int i = 0; i < colors.length; ++i) {
System.out.println(colors[i] + " " + colors[i].ordinal());
}
Color c2 = new Color(); // ❌不能实例化枚举类,只能通过枚举值访问其属性和方法。
}
// 运行结果:
RED
0
RED
Optional[EnumDesc[Color.RED]]
BLACK
----------------------输出所有枚举元素:
RED 0
GREEN 1
BLUE 2
YELLOW 3
ORANGE 4
PURPLE 5
PINK 6
BROWN 7
BLACK 8
WHITE 9
这里要注意 values() 是由 Java 编译器为每个枚举类自动生成的静态方法 ,所以在 Enum 类里是找不到它的源码的,但每个具体的枚举类都有它。
还有就是枚举类内也可以有方法 ,并且这些方法只能通过枚举元素来调用,而不能直接通过枚举类名来调用,如下图所示:

Ⅱ. 枚举类的构造方法默认就是 private
枚举类的构造方法是隐式 private 的,是为了:
- 保证枚举常量的唯一性
- 避免外部创建新的实例(防止破坏
enum的封闭性) - 在某些场景下,天然实现单例模式,并且是线程安全的、防反射、防反序列化

首先枚举类也是可以写构造函数,然后枚举元素可以进行构造,如下所示:
java
public enum Color {
RED("red", 1), GREEN("green", 2), BLUE("blue", 3);
private String name;
private int value;
// 构造函数默认就是private,不写也可以,但是不能写其它的访问权限
private Color(String name, int value) {
this.name = name;
this.value = value;
}
public static void main(String[] args) {
Color c = Color.RED;
System.out.println(c.name);
System.out.println(c.value);
}
}
// 运行结果:
red
1
然后测试一下不同对象引用同一个枚举元素,是不是同一地址:
java
public static void main(String[] args) {
Color c1 = Color.RED;
Color c2 = Color.RED;
System.out.println(c1 == c2); // true
}
很明显不同对象引用同一个枚举元素,都是同一个对象。
然后看一下是不是能够在类内调用构造方法:
java
public static void main(String[] args) {
Color c = new Color(); // ❌报错:无法实例化枚举类型
}
那么就想,能不能用反射来设置访问权限,强制调用构造方法,如下所示:
java
public static void main(String[] args) {
try {
// 先获取构造函数,再设置Accessible为true
Class<?> cs = Class.forName("EnumDemo.Color");
Constructor<?> ctor = cs.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true);
// 调用构造函数创建对象
Color c = (Color)ctor.newInstance("yellow", 4);
System.out.println(c.name + " " + c.value);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
然后报了如下错误:

其实这个参数错误,是因为每个枚举类都默认继承了 Enum,而 Enum 类的构造方法又有两个参数,如下所示:

这时就需要一起放在我们的枚举类的参数列表中传入才行:
java
Constructor<?> ctor = cs.getDeclaredConstructor(String.class, int.class, String.class, int.class);
解决上述问题之后,此时又有一个错误如下所示:

点击第二行跳转过去看看源码:

总结
Enum类同样有构造方法,但一定得是private修饰。Enum类的每一个枚举元素都是【单例】。Enum类型的类不能通过调用【构造方法】来创建实例。Enum类型的类不能通过【反射】来创建新的实例,因为Enum的实例在编译时就已经确定 ,且JVM保证了这些实例的唯一性。
Ⅲ. 什么是 Lambda 表达式
Lambda 表达式是 JavaSE8 中一个重要的新特性,它允许通过表达式来代替功能接口。
Lambda 表达式和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(可以是一个表达式或一个代码块)。基本语法如下所示:
java
(parameters) -> expression
或者
(parameters) -> { statements; }
举一些例子:
java
// 1. 不需要参数,返回值为 2
() -> 2
// 2. 接收⼀个参数,返回其2倍的值
x -> 2 * x
// 3. 接受2个参数,并返回他们的和
(x, y) -> x + y
// 4. 接收2个int型整数,返回他们的乘积
(int x, int y) -> x * y
// 5. 接受⼀个 string 对象,并在控制台打印,不返回任何值
(String s) -> System.out.print(s)
函数式接口定义:一个接口,且其中只有一个抽象方法(可以包含普通方法)。
注意:如果我们在某个接口上声明了
@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。举个例子:
java@FunctionalInterface interface NoParameterNoReturn { void test(); // 合法!可以有普通方法 default void test2() { System.out.println("JDK1.8新特性,default默认⽅法可以有具体的实现"); } }
Ⅳ. Lambda 表达式的使用
java
@FunctionalInterface
interface MyInterface1 {
void myMethod();
}
@FunctionalInterface
interface MyInterface2 {
int myMethod(int a, int b);
}
public class demo2 {
public static void main(String[] args) {
MyInterface1 myif1 = () -> System.out.println("无参无返回值方法");
myif1.myMethod();
MyInterface2 myif2 = (a, b) -> a + b;
System.out.println("有参数有返回值:" + myif2.myMethod(2, 3));
}
}
☠ 注意事项:
-
参数类型可以省略。如果需要省略,每个参数的类型都要省略。
-
参数的小括号里面只有一个参数,那么小括号可以省略
-
如果方法体当中只有一句代码,那么大括号可以省略
-
如果方法体中只有一条语句,且是
return语句,那么大括号可以省略,且去掉return关键字。 -
lambda表达式捕获变量的规则,和匿名内部类是一样的,具体可以参考匿名内部类笔记 -
lambda表达式是 "函数式接口" 的一个匿名实现对象,区分开以下两者的区别:java// Runnable是一个函数式接口,需要重写里面的run()方法! Runnable r = () -> System.out.println("Hello, Lambda!"); // Thread只是一个类,但是可以Thread(Runnable r)来构造,所以才需要new Thread,然后回到上面的问题! Thread t = new Thread(() -> System.out.println("work"));
列表排序的常用写法

1. 基本比较(数值)
Integer.compare(a, b)
安全的数值比较,避免溢出:
java
list.sort((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
适用于:
int,double,long等基本类型- 数值比较安全性优先(避免
a - b的溢出问题)
2. 方法引用 + Comparator.comparing
最简洁可读的写法:
java
list.sort(Comparator.comparing(Person::getAge));
优点:
- 语义明确,别人一看就知道是按年龄排序
- 支持链式调用
.thenComparing(...)
3. 多条件排序
java
list.sort(
Comparator.comparing(Person::getAge)
.thenComparing(Person::getName)
);
场景:
- 先按主条件排(年龄)
- 如果相同,再按次条件(名字)
4. 倒序排序
两种常见方式:
java
// 方式1:reversed()
list.sort(Comparator.comparing(Person::getAge).reversed());
// 方式2:负数技巧(不推荐大数)
list.sort((p1, p2) -> Integer.compare(p2.getAge(), p1.getAge()));
5. 处理 null 值
java
list.sort(
Comparator.comparing(Person::getAge, Comparator.nullsLast(Integer::compareTo))
);
说明:
nullsFirst→ null 在最前面nullsLast→ null 在最后面
6. 字符串比较(忽略大小写)
java
list.sort(Comparator.comparing(Person::getName, String.CASE_INSENSITIVE_ORDER));
7. 完全自定义规则
当规则复杂(比如特殊业务逻辑)时:
java
list.sort((p1, p2) -> {
if (p1.getAge() == p2.getAge()) {
return p1.getName().length() - p2.getName().length();
}
return p1.getAge() - p2.getAge();
});
