枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~

目录

认识枚举

全文重点:枚举在单例模式中为什么是安全的?

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) {
//拿到枚举实例BLACKTestEnum testEnum = TestEnum.BLACK;
//拿到枚举实例REDTestEnum testEnum21 = TestEnum.RED;System.out.println(testEnum.compareTo(testEnum21));System.out.println(BLACK.compareTo(RED));System.out.println(RED.compareTo(BLACK));}
}

枚举的优缺点:

优点:

简单安全,枚举具有内置方法

缺点:无法继承,不可扩展


全文重点:枚举在单例模式中为什么是安全的?

枚举实现单例的安全性基于以下几个原因:

  1. JVM保证的唯一性:枚举类型的每个元素都是静态的,JVM在类加载时会保证每个枚举值只被实例化一次。
  2. 防止反射攻击:尽管理论上可以通过反射创建枚举的额外实例,但是Java语言规范特别指出,尝试通过反射来创建枚举实例的行为是非法的,并且会抛出IllegalArgumentException异常。这意味着即使有人尝试使用反射来破坏单例模式,JVM也会阻止这种行为,从而保持枚举实现的单例模式的线程安全性。
  3. 序列化安全:枚举类型是不可变的,并且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中是一种重要的高级特性,它允许开发者以更简洁的方式定义和使用函数,特别是在处理集合和多线程编程时提供了更为简洁和高效的解决方案。

  1. 历史背景:Lambda表达式作为一种函数式编程的概念,最早由Alonzo Church在其λ演算中提出。Java在2014年发布的Java 8中正式引入了Lambda表达式,标志着Java向函数式编程迈出了重要一步。
  2. 引入原因:在Java 8之前,Java主要是一种面向对象的编程语言。在处理特定场景,如事件监听器或集合的排序时,需要创建大量的匿名内部类,这增加了代码的复杂性,降。
  3. 语法格式: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表达式在哪使用,怎么使用呢?那么我们就需要先认识函数式接口!!!

函数式接口

  1. 如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
  2. 如果我们在某个接口上声明了 @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(){@Overridepublic 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表达式的语法精简:

  1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
  2. 参数的小括号里面只有一个参数,那么小括号可以省略
  3. 如果方法体当中只有一句代码,那么大括号可以省略
  4. 如果方法体中只有一条语句,且是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>(){@Overridepublic 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>() {@Overridepublic 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表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
优点:

  1. 代码简洁,开发迅速
  2. 方便函数式编程
  3. 非常容易进行并行计算
  4. Java 引入 Lambda,改善了集合操作
    缺点:
  5. 代码可读性变差
  6. 在非并行计算中,很多计算未必有传统的 for 性能要高
  7. 不容易进行调试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/20240.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【JAVA】一次操蛋的nginx镜像之旅

一、前言 由于我们的项目中使用到了nginx&#xff0c;同时我们的nginx是通过docker镜像进行安装的&#xff0c;由于nginx出现了问题&#xff0c;需要重新安装。于是。。。 二、通过docker进行安装 docker pull nginx:latest 1.5.2 脚本文件 在/home/docker/script路径下创…

高并发场景下的热点key问题探析与应对策略

目录 一、问题描述 二、发现机制 三、解决策略分析 &#xff08;一&#xff09;解决策略一&#xff1a;多级缓存策略 客户端本地缓存 代理节点本地缓存 &#xff08;二&#xff09;解决策略二&#xff1a;多副本策略 &#xff08;三&#xff09;解决策略三&#xff1a;热点…

.NET 9 的新增功能

文章目录 前言一、.NET 运行时二、序列化三、缩进选项四、默认 Web 选项五、LINQ六、集合七、PriorityQueue.Remove() 方法八、密码九、CryptographicOperations.HashData() 方法十、KMAC 算法十一、反射十二、性能十三、循环优化十四、本机 AOT 的内联改进十五、PGO 改进&…

11.19.2024刷华为OD

文章目录 HJ51HJ53 杨辉三角HJ56HJ57 高精度整数加法HJ58HJ60 简单题HJ63 DNA序列&#xff08;简单题&#xff09;语法知识记录 HJ51 https://www.nowcoder.com/practice/54404a78aec1435a81150f15f899417d?tpId37&tags&title&difficulty0&judgeStatus0&…

小米表盘自定义工具支持最新小米9pro

app下载(v5.2.28) 点击下载 介绍 米坛小米表盘自定义工具是专为小米手环用户设计的软件&#xff0c;它具备以下特点和功能&#xff1a; 兼容性广泛&#xff1a;支持包括小米手环7、7Pro、8、8Pro、9、9Pro以及小米手表S3、S4在内的多款设备。 持续更新&#xff1a;软件不断…

算法-二叉树(从理论知识到力扣题,递归、迭代。)

二叉树 一、二叉树理论知识1、种类a.满二叉树b.完全二叉树c.二叉搜索树d.平衡二叉搜索树 2、java对于树的理解3、存储a.链式存储&#xff08;常用&#xff09;b.数组存储 4、遍历方式a.深度优先搜索b.广度优先搜索 5、树的定义&#xff08;链式&#xff09; 二、力扣题解写题思…

青训营刷题笔记10

问题描述 小C拿到了一个排列&#xff0c;她想知道在这个排列中&#xff0c;元素 xx 和 yy 是否是相邻的。排列是一个长度为 nn 的数组&#xff0c;其中每个数字从 11 到 nn 恰好出现一次。 你的任务是判断在给定的排列中&#xff0c;xx 和 yy 是否是相邻的。 测试样例 样例1…

时间类的实现

在现实生活中&#xff0c;我们常常需要计算某一天的前/后xx天是哪一天&#xff0c;算起来十分麻烦&#xff0c;为此我们不妨写一个程序&#xff0c;来减少我们的思考时间。 1.基本实现过程 为了实现时间类&#xff0c;我们需要将代码写在3个文件中&#xff0c;以增强可读性&a…

Leetcode 路径总和

使用递归算法 class Solution {public boolean hasPathSum(TreeNode root, int targetSum) {// 如果节点为空&#xff0c;返回falseif (root null) {return false;}// 如果是叶子节点&#xff0c;检查路径和是否等于目标值if (root.left null && root.right null) …

Linux开发讲课50--- epoll、poll、select的原理和区别

一、什么是epoll&#xff1f; epoll是一种I/O事件通知机制&#xff0c;是linux 内核实现IO多路复用的一个实现。IO多路复用是指&#xff0c;在一个操作里同时监听多个输入输出源&#xff0c;在其中一个或多个输入输出源可用的时候返回&#xff0c;然后对其的进行读写操作。 ep…

MySQL datetime不同长度的影响

MySQL datetime长度的影响 1.背景 MySQL数据库中某张表的某个字段类型设置datetime, 长度为0&#xff0c;在进行插入数据时&#xff0c;MySQL会对该字段进行舍入操作。 2.测试 1.创建一张测试表&#xff0c;里面有两个时间字段都是datetime&#xff0c;但其中一个长度为3 …

数据灾备方案学习

1. 数据灾备 1.1 备份 将数据由一份数据转存为多份数据的过程&#xff0c;即为备份&#xff0c;通常指将数据通过某些手段&#xff0c;将数据存放到其他不同设备中&#xff0c;防止数据丢失。指用户为应用系统产生的重要数据&#xff08;或者原有的重要数据信息&#xff09;制…

在centos7中安装SqlDeveloper的Oracle可视化工具

1.下载安装包 &#xff08;1&#xff09;在SqlDeveloper官网下载&#xff08;Oracle SQL Developer Release 19.2 - Get Started&#xff09;对应版本的安装包即可&#xff08;安装包和安装命令如下&#xff09;&#xff1a; &#xff08;2&#xff09;执行完上述命令后&#x…

矩阵论在深度学习中的应用

摘要&#xff1a; 本文深入探讨了矩阵论在深度学习领域的广泛应用。首先介绍了深度学习中数据表示和模型结构与矩阵的紧密联系&#xff0c;接着详细阐述了矩阵论在神经网络训练算法优化、卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;及其…

AlphaFold 3开源,谷歌DeepMind诺奖AI项目,革新蛋白质结构预测,加速新药和疫苗研发

AlphaFold 3是什么&#xff1f; MeoAI了解到这个模型在2024年因其在蛋白质结构预测方面的贡献获得了诺贝尔化学奖。AlphaFold 3 是由 DeepMind 开发的一款人工智能&#xff08;AI&#xff09;软件&#xff0c;它能够以前所未有的精确度预测几乎所有生命大分子&#xff08;蛋白…

Excel如何把两列数据合并成一列,4种方法

Excel如何把两列数据合并成一列,4种方法 参考链接:https://baijiahao.baidu.com/s?id=1786337572531105925&wfr=spider&for=pc 在Excel中,有时候需要把两列或者多列数据合并到一列中,下面介绍4种常见方法,并且提示一些使用注意事项,总有一种方法符合你的要求:…

甲骨文云服务器 (Oracle Cloud) 终极防封、防回收的教程!

1.WindTerm 远程终端连接器&#xff1a;【官方下载】、【备用下载 】 2.AA面板&#xff1a;【安装脚本】 3.开启端口&#xff1a; sudo iptables -P INPUT ACCEPT sudo iptables -P FORWARD ACCEPT sudo iptables -P OUTPUT ACCEPT sudo iptables -F 4.WordPress&#xf…

c++源码阅读__ThreadPool__正文阅读

一. 简介 本章我们开始阅读c git 高星开源项目ThreadPool, 这是一个纯c的线程池项目, 并且代码量极小, 非常适合新手阅读 git地址: progschj / ThreadPool 二. 前提知识 为了面对不同读者对c掌握情况不同的情况, 这里我会将基本上稍微值得一说的前提知识点, 全部专门写成一篇…

环形子数组的最大和

题目 给定一个长度为 n 的环形整数数组 nums &#xff0c;返回 nums 的非空 子数组 的最大可能和 。 环形数组 意味着数组的末端将会与开头相连呈环状。形式上&#xff0c; nums[i] 的下一个元素是 nums[(i 1) % n] &#xff0c; nums[i] 的前一个元素是 nums[(i - 1 n) % …

二叉搜索树的基本操作(最全面)

目录 二叉搜索的定义: 节点类: 查找关键词对应的值: 非递归 递归: 查找最小关键词对应的值: 方法一: 方法二: 查找最大关键词对应的值: 方法一: 方法二: 存贮关键词对应的值: 查找关键词的前驱值: 查找关键词对应的后继值: 删除节点: 非递归: 递归: 范围 1.…