文章目录
- Ⅰ. 枚举类定义与使用
- Ⅱ. 枚举类的构造方法默认就是 `private`
- 总结
- Ⅲ. 什么是 `Lambda` 表达式
- Ⅳ. `Lambda` 表达式的使用
- 列表排序的常用写法
- 1. **基本比较(数值)**
- `Integer.compare(a, b)`
- 2. **方法引用 +** **`Comparator.comparing`**
- 3. **多条件排序**
- 4. **倒序排序**
- 5. **处理 null 值**
- 6. **字符串比较(忽略大小写)**
- 7. **完全自定义规则**
Ⅰ. 枚举类定义与使用
publicenumColor{RED,GREEN,BLUE,YELLOW,ORANGE,PURPLE,PINK,BROWN,BLACK,WHITE;}每个我们写的enum类都会默认继承于一个抽象类Enum,如下图所示:
而这个Enum抽象类中有挺多方法,如下所示:
下面是使用举例:
publicstaticvoidmain(String[]args){// 使用Color.RED直接访问枚举元素,或者直接RED都行,但不能使用new来生成枚举类对象Colorcolor=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(inti=0;i<colors.length;++i){System.out.println(colors[i]+" "+colors[i].ordinal());}Colorc2=newColor();// ❌不能实例化枚举类,只能通过枚举值访问其属性和方法。}// 运行结果:RED0REDOptional[EnumDesc[Color.RED]]BLACK----------------------输出所有枚举元素: RED0GREEN1BLUE2YELLOW3ORANGE4PURPLE5PINK6BROWN7BLACK8WHITE9这里要注意values()是由Java编译器为每个枚举类自动生成的静态方法,所以在Enum类里是找不到它的源码的,但每个具体的枚举类都有它。
还有就是枚举类内也可以有方法,并且这些方法只能通过枚举元素来调用,而不能直接通过枚举类名来调用,如下图所示:
Ⅱ. 枚举类的构造方法默认就是private
枚举类的构造方法是隐式private的,是为了:
- 保证枚举常量的唯一性
- 避免外部创建新的实例(防止破坏
enum的封闭性) - 在某些场景下,天然实现单例模式,并且是线程安全的、防反射、防反序列化
首先枚举类也是可以写构造函数,然后枚举元素可以进行构造,如下所示:
publicenumColor{RED("red",1),GREEN("green",2),BLUE("blue",3);privateStringname;privateintvalue;// 构造函数默认就是private,不写也可以,但是不能写其它的访问权限privateColor(Stringname,intvalue){this.name=name;this.value=value;}publicstaticvoidmain(String[]args){Colorc=Color.RED;System.out.println(c.name);System.out.println(c.value);}}// 运行结果:red1然后测试一下不同对象引用同一个枚举元素,是不是同一地址:
publicstaticvoidmain(String[]args){Colorc1=Color.RED;Colorc2=Color.RED;System.out.println(c1==c2);// true}很明显不同对象引用同一个枚举元素,都是同一个对象。
然后看一下是不是能够在类内调用构造方法:
publicstaticvoidmain(String[]args){Colorc=newColor();// ❌报错:无法实例化枚举类型}那么就想,能不能用反射来设置访问权限,强制调用构造方法,如下所示:
publicstaticvoidmain(String[]args){try{// 先获取构造函数,再设置Accessible为trueClass<?>cs=Class.forName("EnumDemo.Color");Constructor<?>ctor=cs.getDeclaredConstructor(String.class,int.class);ctor.setAccessible(true);// 调用构造函数创建对象Colorc=(Color)ctor.newInstance("yellow",4);System.out.println(c.name+" "+c.value);}catch(ClassNotFoundExceptione){thrownewRuntimeException(e);}catch(InvocationTargetExceptione){thrownewRuntimeException(e);}catch(NoSuchMethodExceptione){thrownewRuntimeException(e);}catch(InstantiationExceptione){thrownewRuntimeException(e);}catch(IllegalAccessExceptione){thrownewRuntimeException(e);}}然后报了如下错误:
其实这个参数错误,是因为每个枚举类都默认继承了Enum,而Enum类的构造方法又有两个参数,如下所示:
这时就需要一起放在我们的枚举类的参数列表中传入才行:
Constructor<?>ctor=cs.getDeclaredConstructor(String.class,int.class,String.class,int.class);解决上述问题之后,此时又有一个错误如下所示:
点击第二行跳转过去看看源码:
总结
Enum类同样有构造方法,但一定得是private修饰。Enum类的每一个枚举元素都是【单例】。Enum类型的类不能通过调用【构造方法】来创建实例。Enum类型的类不能通过【反射】来创建新的实例,因为Enum的实例在编译时就已经确定,且JVM保证了这些实例的唯一性。
Ⅲ. 什么是Lambda表达式
Lambda表达式是JavaSE8中一个重要的新特性,它允许通过表达式来代替功能接口。
Lambda表达式和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(可以是一个表达式或一个代码块)。基本语法如下所示:
(parameters)->expression 或者(parameters)->{statements;}举一些例子:
// 1. 不需要参数,返回值为 2()->2// 2. 接收⼀个参数,返回其2倍的值x->2*x// 3. 接受2个参数,并返回他们的和(x,y)->x+y// 4. 接收2个int型整数,返回他们的乘积(intx,inty)->x*y// 5. 接受⼀个 string 对象,并在控制台打印,不返回任何值(Strings)->System.out.print(s)函数式接口定义:一个接口,且其中只有一个抽象方法(可以包含普通方法)。
注意:如果我们在某个接口上声明了
@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。举个例子:
@FunctionalInterfaceinterfaceNoParameterNoReturn{voidtest();// 合法!可以有普通方法defaultvoidtest2(){System.out.println("JDK1.8新特性,default默认⽅法可以有具体的实现");}}
Ⅳ.Lambda表达式的使用
@FunctionalInterfaceinterfaceMyInterface1{voidmyMethod();}@FunctionalInterfaceinterfaceMyInterface2{intmyMethod(inta,intb);}publicclassdemo2{publicstaticvoidmain(String[]args){MyInterface1myif1=()->System.out.println("无参无返回值方法");myif1.myMethod();MyInterface2myif2=(a,b)->a+b;System.out.println("有参数有返回值:"+myif2.myMethod(2,3));}}☠ 注意事项:
参数类型可以省略。如果需要省略,每个参数的类型都要省略。
参数的小括号里面只有一个参数,那么小括号可以省略
如果方法体当中只有一句代码,那么大括号可以省略
如果方法体中只有一条语句,且是
return语句,那么大括号可以省略,且去掉return关键字。lambda表达式捕获变量的规则,和匿名内部类是一样的,具体可以参考匿名内部类笔记lambda表达式是“函数式接口” 的一个匿名实现对象,区分开以下两者的区别:// Runnable是一个函数式接口,需要重写里面的run()方法!Runnabler=()->System.out.println("Hello, Lambda!");// Thread只是一个类,但是可以Thread(Runnable r)来构造,所以才需要new Thread,然后回到上面的问题!Threadt=newThread(()->System.out.println("work"));
列表排序的常用写法
1.基本比较(数值)
Integer.compare(a, b)
安全的数值比较,避免溢出:
list.sort((p1,p2)->Integer.compare(p1.getAge(),p2.getAge()));适用于:
int,double,long等基本类型- 数值比较安全性优先(避免
a - b的溢出问题)
2.方法引用 +Comparator.comparing
最简洁可读的写法:
list.sort(Comparator.comparing(Person::getAge));优点:
- 语义明确,别人一看就知道是按年龄排序
- 支持链式调用
.thenComparing(...)
3.多条件排序
list.sort(Comparator.comparing(Person::getAge).thenComparing(Person::getName));场景:
- 先按主条件排(年龄)
- 如果相同,再按次条件(名字)
4.倒序排序
两种常见方式:
// 方式1:reversed()list.sort(Comparator.comparing(Person::getAge).reversed());// 方式2:负数技巧(不推荐大数)list.sort((p1,p2)->Integer.compare(p2.getAge(),p1.getAge()));5.处理 null 值
list.sort(Comparator.comparing(Person::getAge,Comparator.nullsLast(Integer::compareTo)));说明:
nullsFirst→ null 在最前面nullsLast→ null 在最后面
6.字符串比较(忽略大小写)
list.sort(Comparator.comparing(Person::getName,String.CASE_INSENSITIVE_ORDER));7.完全自定义规则
当规则复杂(比如特殊业务逻辑)时:
list.sort((p1,p2)->{if(p1.getAge()==p2.getAge()){returnp1.getName().length()-p2.getName().length();}returnp1.getAge()-p2.getAge();});