目录
熟悉枚举
全文重点:枚举在单例模式中为什么是安全的?
Lambda 表达式
概念:
函数式接口
lambda表达式的根本使用:
lambda表达式的语法精简:
lambda表达式的变量捕获
Lambda在聚集当中的使用
在 Collection接口中的使用:
在List中的使用
lambda表达式的总结:
熟悉枚举
枚举(Enumeration)是一种数据范例,它由一组预界说的常量组成,这些常量通常被称为枚举的成员或枚举值。
枚举的作用,把我们想要描述的东西举例出来,例如颜色。因为在Java中并没有一个特殊的数据范例是表示颜色的。
下列直接列出枚举的简单使用的方法:
- enum TestEnum {
- RED,BLACK,GREEN,WHITE;
- public static void main(String[] args) {
- TestEnum testEnum2 = TestEnum.BLACK;
- switch (testEnum2) {
- case RED:
- System.out.println("red");
- break;
- case BLACK:
- System.out.println("black");
- break;
- case WHITE:
- System.out.println("WHITE");
- break;
- case GREEN:
- System.out.println("black");
- break;
- default:
- break;
- }
- }
- }
复制代码 上述场景就是:一个简单使用枚举范例的场景。
从上面代码可以看出枚举:enum 其实有点像类,里面也可以有属性和方法。不同的是enum的第一行必须是你要枚举的常量。否则编译器会直接报错。
带参数的使用案例:
这是带有构造方法的枚举,以是在第一行的枚举常量中我们就必须传参。
留意:枚举的构造方法默认是私有的(重要)
- enum TestEnum1 {
- RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
- private String name;
- private int key;
- /**
- * 1、当枚举对象有参数后,需要提供相应的构造函数
- * 2、枚举的构造函数默认是私有的 这个一定要记住
- * @param name
- * @param key
- */
- private TestEnum1 (String name,int key) {
- this.name = name;
- this.key = key;
- }
- public static TestEnum1 getEnumKey (int key) {
- for (TestEnum1 t: TestEnum1.values()) {
- if(t.key== key) {
- return t;
- }
- }
- return null;
- }
- public static void main(String[] args) {
- System.out.println(getEnumKey(2));
- }
- }
复制代码
枚举另有属于自己的的方法:
values() 以数组情势返回枚举范例的全部成员
ordinal() 获取枚举成员的索引位置
valueOf() 将平凡字符串转换为枚举实例
compareTo() 比力两个枚举成员在界说时的顺序
- enum TestEnum2 {
- RED,BLACK,GREEN,WHITE;
- public static void main(String[] args) {
- TestEnum2[] testEnum2 = TestEnum2.values();
- for (int i = 0; i < testEnum2.length; i++) {
- System.out.println(testEnum2[i] + " " + testEnum2[i].ordinal());
- }
- System.out.println("=========================");
- System.out.println(TestEnum2.valueOf("GREEN"));
- }
- }
复制代码
compareTo方法使用案例:
- enum TestEnum {
- RED,BLACK,GREEN,WHITE;
- public static void main(String[] args) {
- //拿到枚举实例BLACK
- TestEnum testEnum = TestEnum.BLACK;
- //拿到枚举实例RED
- TestEnum testEnum21 = TestEnum.RED;
- System.out.println(testEnum.compareTo(testEnum21));
- System.out.println(BLACK.compareTo(RED));
- System.out.println(RED.compareTo(BLACK));
- }
- }
复制代码
枚举的优缺点:
长处:
简单安全,枚举具有内置方法
缺点:无法继续,不可扩展
全文重点:枚举在单例模式中为什么是安全的?
枚举实现单例的安全性基于以下几个缘故原由:
- JVM包管的唯一性:枚举范例的每个元素都是静态的,JVM在类加载时会包管每个枚举值只被实例化一次。
- 防止反射攻击:尽管理论上可以通过反射创建枚举的额外实例,但是Java语言规范特殊指出,尝试通过反射来创建枚举实例的举动是非法的,并且会抛出IllegalArgumentException非常。这意味着即使有人尝试使用反射来粉碎单例模式,JVM也会制止这种举动,从而保持枚举实现的单例模式的线程安全性。
- 序列化安全:枚举范例是不可变的,并且JVM在反序列化时会包管枚举实例的唯一性,这使得枚举实现的单例在序列化和反序列化过程中也是安全的。
那么接下来我们就重点分析一下第二点:枚举是怎么防止反射攻击?
下面我们尝试一下反射获取构造方法,并尝试实例化出一个对象。
- enum TestEnum1 {
- RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
- private String name;
- private int key;
- /**
- * 1、当枚举对象有参数后,需要提供相应的构造函数
- * 2、枚举的构造函数默认是私有的 这个一定要记住
- * @param name
- * @param key
- */
- private TestEnum1 (String name,int key) {
- this.name = name;
- this.key = key;
- }
- public static TestEnum1 getEnumKey (int key) {
- for (TestEnum1 t: TestEnum1.values()) {
- if(t.key== key) {
- return t;
- }
- }
- return null;
- }
- public static void reflectPrivateConstructor() {
- try {
- Class<?> classStudent = Class.forName("TestEnum");
- //注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
- Constructor<?> declaredConstructorStudent= classStudent.getDeclaredConstructor(String.class, int.class);
- //设置为true后可修改访问权限
- declaredConstructorStudent.setAccessible(true);
- Object objectStudent = declaredConstructorStudent.newInstance("绿色",666);
- TestEnum testEnum = (TestEnum) objectStudent;
- System.out.println("获得枚举的私有构造函数:"+testEnum);
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
复制代码
显然编译器已经报错,并直接给出提示,不能通过反射,创建出一个枚举实例。
那么我们就看一下源码,来看看代码是怎样抛出非常,制止反射枚举获取实例。
跳转到源码当中就发现了,(clazz.getModifiers() & Modifier.ENUM) != 0,的第二个条件直接忽略了枚举,当Constructor中发现反射对象为枚举时,就直接抛出非常了。这就是枚举实现单例模式时安全的缘故原由之一。
Lambda 表达式
概念:
Lambda表达式在Java中是一种重要的高级特性,它答应开发者以更简洁的方式界说和使用函数,特殊是在处理聚集和多线程编程时提供了更为简洁和高效的办理方案。
- 汗青配景:Lambda表达式作为一种函数式编程的概念,最早由Alonzo Church在其λ演算中提出。Java在2014年发布的Java 8中正式引入了Lambda表达式,标志着Java向函数式编程迈出了重要一步。
- 引入缘故原由:在Java 8之前,Java主要是一种面向对象的编程语言。在处理特定场景,如事件监听器或聚集的排序时,必要创建大量的匿名内部类,这增长了代码的复杂性,降。
- 语法格式:Lambda表达式的语法格式非常简洁,通常由参数列表、箭头符号(->)和表达式体三部分组成。参数列表界说了传递给Lambda函数的参数,箭头符号表示参数列表的竣事,表达式体则是Lambda函数实行的代码。例如:
(parameters) -> expression(表达式)
(parameters) ->{ statements; } (代码块)
这里把expression和statements统称为方法体
方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值大概什么都不反 回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值大概什么都不反回。
lambda表达式代码示例:
- // 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 对象,并在控制台打印,不返回任何值(看起来像是返回void)
- (String s) -> System.out.print(s)
- @FunctionalInterface
- interface NoParameterNoReturn {
- //注意:只能有一个方法
- void test();
- }
复制代码
那么lambda表达式在哪使用,怎么使用呢?那么我们就必要先熟悉函数式接口!!!
函数式接口
- 如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
- 如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的界说来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。以是,从某种意义上来说,只要你包管你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。
界说方式(两种):
- interface NoParameterNoReturn {
- //注意:只能有一个方法
- void test();
- }
复制代码 或:
- @FunctionalInterface
- interface NoParameterNoReturn {
- void test();
- default void test2() {
- System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
- }
- }
复制代码 可以简单理解为:lambda就是用来简化函数式接口的使用。
lambda表达式的根本使用:
不使用lambda表达式的情势:
- @FunctionalInterface
- interface NoParameterNoReturn {
- void test();
- }
- public class T{
- public static void main(String[] args) {
- NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
- @Override
- public void test() {
- System.out.println("hello");
- }
- };
- noParameterNoReturn.test();
- }
- }
复制代码 向上面的代码,就略显麻烦。下面就展示各种接口的lambda表达式的使用方法:
以下还不是最精简的lambda表达式:
- //无返回值无参数
- @FunctionalInterface
- interface NoParameterNoReturn {
- void test();
- }
- //无返回值一个参数
- @FunctionalInterface
- interface OneParameterNoReturn {
- void test(int a);
- }
- //无返回值多个参数
- @FunctionalInterface
- interface MoreParameterNoReturn {
- void test(int a,int b);
- }
- //有返回值无参数
- @FunctionalInterface
- interface NoParameterReturn {
- int test();
- }
- //有返回值一个参数
- @FunctionalInterface
- interface OneParameterReturn {
- int test(int a);
- }
- //有返回值多参数
- @FunctionalInterface
- interface MoreParameterReturn {
- int test(int a,int b);
- }
- public class T{
- public class TestDemo {
- public static void main(String[] args) {
- NoParameterNoReturn noParameterNoReturn = ()->{
- System.out.println("无参数无返回值");
- };
- noParameterNoReturn.test();
- OneParameterNoReturn oneParameterNoReturn = (int a)->{
- System.out.println("一个参数无返回值:"+ a);
- };
- oneParameterNoReturn.test(10);
- MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->{
- System.out.println("多个参数无返回值:"+a+" "+b);
- };
- moreParameterNoReturn.test(20,30);
- NoParameterReturn noParameterReturn = ()->{
- System.out.println("有返回值无参数!");
- return 40;
- };
- //接收函数的返回值
- int ret = noParameterReturn.test();
- System.out.println(ret);
- OneParameterReturn oneParameterReturn = (int a)->{
- System.out.println("有返回值有一个参数!");
- return a;
- };
- ret = oneParameterReturn.test(50);
- System.out.println(ret);
- MoreParameterReturn moreParameterReturn = (int a,int b)->{
- System.out.println("有返回值多个参数!");
- return a+b;
- };
- ret = moreParameterReturn.test(60,70);
- System.out.println(ret);
- }
- }
- }
复制代码
lambda表达式的语法精简:
- 参数范例可以省略,如果必要省略,每个参数的范例都要省略。
- 参数的小括号里面只有一个参数,那么小括号可以省略
- 如果方法体当中只有一句代码,那么大括号可以省略
- 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。
- public static void main(String[] args) {
- MoreParameterNoReturn moreParameterNoReturn = ( a, b)->{
- System.out.println("无返回值多个参数,省略参数类型:"+a+" "+b);
- };
- moreParameterNoReturn.test(20,30);
-
- OneParameterNoReturn oneParameterNoReturn = a ->{
- System.out.println("一个参数无返回值,小括号可以胜率:"+ a);
- };
- oneParameterNoReturn.test(10);
-
- NoParameterNoReturn noParameterNoReturn = ()->System.out.println("无参数无返回值,方法体中只有一行代码");
- noParameterNoReturn.test();
-
- //方法体中只有一条语句,且是return语句
- NoParameterReturn noParameterReturn = ()-> 40;
- int ret = noParameterReturn.test();
- System.out.println(ret);
- }
复制代码 以上就是精简过后的lambda表达式。
lambda表达式的变量捕获
在下面代码当中的变量a就是,捕获的变量。这个变量要么是被final修饰,如果不是被final修饰的 你要包管在使用 之前,没有修改。如下代码就是错误的代码 :
Lambda在聚集当中的使用
在 Collection接口中的使用:
正常用法:
- public static void main(String[] args) {
- ArrayList<String> list = new ArrayList<>();
- list.add("Hello");
- list.add("bit");
- list.add("hello");
- list.add("lambda");
- list.forEach(new Consumer<String>(){
- @Override
- public void accept(String str){
- //简单遍历集合中的元素。
- System.out.print(str+" ");
- }
- });
- }
复制代码
使用lambda表达式:
相当于实现了Consumer<String>中的 void accept(String str)方法。
- public static void main(String[] args) {
- ArrayList<String> list = new ArrayList<>();
- list.add("Hello");
- list.add("bit");
- list.add("hello");
- list.add("lambda");
- //表示调用一个,不带有参数的方法,其执行花括号内的语句,为原来的函数体内容。
- list.forEach(s -> {
- System.out.println(s);
- });
- }
复制代码
在List中的使用
正常使用方法:
- public static void main(String[] args) {
- ArrayList<String> list = new ArrayList<>();
- list.add("Hello");
- list.add("bit");
- list.add("hello");
- list.add("lambda");
- list.sort(new Comparator<String>() {
- @Override
- public int compare(String str1, String str2) {
- //注意这里比较长度
- return str1.length() - str2.length();
- }
- });
- System.out.println(list);
- }
复制代码
lambda表示式用法:
相当于实现了Comparator<String>中的int compare(String str1, String str2)方法
- public static void main(String[] args) {
- ArrayList<String> list = new ArrayList<>();
- list.add("Hello");
- list.add("bit");
- list.add("hello");
- list.add("lambda");
- //调用带有2个参数的方法,且返回长度的差值
- list.sort((str1, str2) -> str1.length() - str2.length());
- System.out.println(list);
- }
复制代码
lambda表达式的总结:
Lambda表达式的长处很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
长处:
- 代码简洁,开发迅速
- 方便函数式编程
- 非常容易进行并行盘算
- Java 引入 Lambda,改善了聚集操作
缺点:
- 代码可读性变差
- 在非并行盘算中,很多盘算未必有传统的 for 性能要高
- 不容易进行调试
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |