1.Java Stream流与Optional流浅析
2.JDK源码解析之Optional源码解析
3.Clean Code(1):参数校验
4.Java 8 开始新增的源码 Optional 类 - 检查 Optional 的值
5.Python花式导包的八种方法
6.Java8新特性-Optional类
Java Stream流与Optional流浅析
Stream流
1. 操作类型
Stream API中的操作类型主要分为两大类:中间操作和终止操作。中间操作仅作为标记,解析实际计算会在触发终止操作时进行。源码
2. Stream的解析操作过程
首先,我们准备了一些示例代码。源码在TestStream类中,解析java考勤管理源码我们定义了一些测试lambda函数的源码方法。在main方法中,解析我们执行了一个相关的源码流操作,在控制台中并没有看到任何输出。解析这说明Stream并没有真正执行到对应的源码方法中,因为我们没有写入终止操作。解析由此可见,源码在终止操作之前,解析Stream并没有真正去执行每个中间操作,源码而是将中间操作记录了下来。在执行终止操作这一行代码时,再去执行中间操作。
2.1 记录过程
进入源码后,可以看到Collection的Stream方法调用了StreamSupport.stream()方法。在该方法中,返回了一个ReferencePipeline.Head对象,这是记录管道操作的头节点对象。这个Head对象继承了ReferencePipeline对象,所以后续的map、filter等方法实际上是ReferencePipeline对象的方法。在构造方法中,也调用了父类AbstractPipeline类的构造方法。
在Stream中,每一步操作都被定义为一个Stage。在构造方法中,定义了previousStage和sourceStage,即上一个节点和头节点。在类中还有一个nextStage对象。
Stream实际上构建了一个双向链表来记录每一步操作。接下来,我们看一下list.map()方法。
在该方法中,创建了一个StatelessOp对象,它代表无状态的中间操作。这个对象同样继承了ReferencePipeline。视频源码解密在该对象的构造方法中,将调用该初始化方法的节点定义为上一个节点,并且对应的深度depth也进行了+1操作。
我们总结一下,stream()方法得到的是HeadStage,之后每一个操作(Operation)都会创建一个新的Stage,并以双向链表的形式结合在一起。每个Stage都记录了本身的操作。Stream就以此方式实现了对操作的记录。注意,结束操作不算depth的深度,它也不属于stage。但是我们的示例语句中没有写结束操作的代码,所以在这里提一下Stream的Lazy机制。它的特点是:Stream直到调用终止操作时才会开始计算,没有终止操作的Stream将是一个静默的无操作指令。
Stage相关类如下
2.2 执行过程
在了解执行过程之前,我们应该先了解另一个接口Sink,它继承了Consumer接口。在调用map、filter等无状态操作中返回的StatelessOp对象中,覆盖了opWrapSink方法,返回了一个Sink对象,并且将参数中的Sink对象作为构造方法中的参数传入进去。
走进构造方法后,可以看到在该对象中定义了一个downstream,该对象也是一个Sink类型的对象,并且在定义Sink对象时,覆盖了Consumer接口中的accept方法。
不难看出,在执行accept方法时,就是将当前节点的操作结果传入给downstream继续执行,而这个downstream则是通过onWrapSink方法中传入过来的。
了解了以上这些概念,我们可以走进结束操作.collect(Collectors.toList());方法。在该方法中,通过Collectors定义了一个另一个ArrayList收集器,并且传入了collect方法中。
我们暂时只看非并行的部分。在这一行通过ReduceOps定义了一个ReduceOp对象。istio bookinfo源码
在makeRef方法中,返回了一个ReduceOp对象,该对象覆盖了makeSink()方法,返回了一个ReducingSink对象。我们继续往下走,走进evaluate方法中。
可以看出,wrapsink方法中,是查找链表的头节点,并且调用每个节点的onWrapSink方法,在该方法中传入当前节点的sink对象,并且将传入的对象定义成自己的下游,形成一个从头节点到尾部节点的Sink单向链表。
在wrapSink中,通过一层层的前置包装,返回头节点的Sink类传入copyInto方法中。
在该方法中,先调用了wrappedSink.begin()方法,该方法默认实现为调用downstream的begin方法。相当于触发全部Sink的begin方法,做好运行前的准备。
具体循环的执行则是在spliterator.forEachRemaining(wrappedSink);方法中,操作如下
在forEachRemaining方法中,调用了accept方法,也就是在定义onWrapSink方法中初始化Sink对象后定义的accept方法,将自己的执行结果传入downstream继续执行,也就是说,在调用结束操作后才实际执行每个方法。在实际执行过后,在执行end方法进行结束操作。Stream整体的流操作大概就是如此。了解了大概过程后可以找一些常用的case来分析一下。
2.3 具体分析
一般情况下都会选择list作为排序容器,大部分情况下都是不知道容器大小的,于是采用RefSortingSink类作为当前节点处理类,该类代码如下。
可以看到该Sink中的accept方法中,并没有执行下游的accept方法,而是将所有的数据装入了一个ArrayList,在end方法利用arrayList进行排序,机战游戏源码并且继续开启后续的循环操作。
3. 代码建议
JDK源码解析之Optional源码解析
在开发过程中,空指针异常(NullPointerException)是常见的运行时异常。为了解决这个问题,除了常见的判空操作外,本文将介绍一种更为优雅的方法——使用Optional类来避免空指针问题。
Optional本质上是一个容器类,它可能包含非空值或null值,但只能保存一个元素。需要注意的是,Optional没有实现序化接口,因此不适宜作为类中的字段使用。
一、使用方法
首先,创建一个静态内部类User。传统上,我们直接使用判空操作来判断对象是否为null。然而,这种方法有时会忽略判空,例如在接收方法返回值时,未考虑到方法返回值可能是null,从而引发空指针异常。
使用Optional类可以带来哪些改变呢?首先,我们来了解如何构造Optional对象。主要有两个方法:ofNullable()静态方法和of()静态方法。这两个方法的主要区别在于,当传入的对象为null时,of()方法会直接抛出空指针异常,而ofNullable()方法则允许传入null值。
之后,可以通过isPresent()方法判断容器内部对象是否为空。如果不为空,则返回true,否则返回false。除此之外,Optional还提供了一些其他实用的方法,如ifPresent()、orElse()、orElseThrow()、orElseGet()和map()等。芥子社区源码
二、Optional结构
Optional类是不可继承的final类,内部包含一个静态变量EMPTY表示一个空的Optional对象,以及一个value成员变量表示保存的元素。
Optional类有两个私有的构造函数,不允许外部直接通过构造函数创建Optional对象。无参构造函数会将value设置为null,而第二个构造函数需要传递value值,如果为null,则抛出异常。
三、创建Optional对象的方法
在上文中,已经提到创建Optional对象的两个方法:of()和ofNullable()。当value为空时,of()方法会抛出异常,因为Optional类的构造函数中进行了校验。
ofNullable()方法会根据value是否为null,决定是返回一个保存null的Optional对象还是创建一个包含value值的Optional对象。
四、Optional主要方法
Optional类的主要方法包括get()、isPresent()、ifPresent()、orElse()、orElseGet()、orElseThrow()和map()等。这些方法帮助我们更好地处理Optional对象,减少模板代码的编写。
五、总结
Optional类作为容器类,主要帮助我们判断对象是否为空,从而避免空指针问题。通过了解使用方法和分析源码,我们可以发现它在内部进行了很多判断和处理,减少了模板代码的编写。此外,使用Optional可以提醒使用者注意返回值可能为null,从而最大程度避免空指针异常。
Clean Code(1):参数校验
编写清晰代码涉及多个方面,《Clean Code》与《重构》等书籍提供了清晰代码的定义以及重构方法,帮助维护代码质量。
尽管了解重构重要性以及常见演示,实际编写清晰代码时,人们常感到困难。原因主要在于接触高质量代码不足与关注影响效能的实践不够。计划分享一些简短代码片段,期待能为读者带来启发。
本文聚焦于方法参数校验,探讨入参与出参处理。
为了防止方法调用时返回结果为null导致空指针异常,通常使用Optional或NullValueObject显式处理可能的null值。然而,入参不宜使用Optional。
避免多层嵌套判断语句,降低代码可读性。推荐使用卫语句或Assert工具类进行参数校验。
卫语句能简化代码结构,避免深层嵌套。而Assert工具类在Spring源码中常见,提供参数校验方法,帮助确保参数符合预期。
条件语句频繁使用容易导致复杂嵌套,卫语句或Assert能有效解决参数校验问题。Optional用于线性化null值处理,但不建议直接作为入参,确保方法实现与外部调用解耦。
使用Assert在方法执行前进行参数校验,如下:
执行时进行参数判断,不符合预期则抛出IllegalArgumentException异常。在Web服务中,需捕获全局异常并转化成特定错误响应。
通过上述手段,提升代码可读性与健壮性,开发过程中更容易定位问题。
Java 8 开始新增的 Optional 类 - 检查 Optional 的值
在Java 8引入的Optional类中,isPresent()方法用于检查Optional对象中是否存在值。无论是自定义创建的Optional对象,还是从其他方法返回的Optional对象,我们都能使用此方法。如果Optional对象内的值非null,isPresent()方法将返回真值。
而Java 中,我们则可以使用与isPresent相反的方法,即isEmpty()。当Optional对象中的值为null时,isEmpty方法将返回真值。
在JDK源代码中,这两个方法的实现都简单地通过判断是否等于null来完成。isPresent()和isEmpty()方法的使用原则相反。在实际应用中,我们可根据需求选择合适的检查方法。
Python花式导包的八种方法
大家好,今天这篇文章,准备跟大家分享 8 种导入模块的方法。
与此类似的还有
一般情况下,使用 import 语句导入模块已经够用的。
但是在一些特殊场景中,可能还需要其他的导入方式。
参数介绍:
name (required): 被加载 module 的名称
globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 global()
locals (optional): 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local()
fromlist (Optional): 被导入的 submodule 名称
level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 类似于 '.',2 类似于 '..'。 使用示例如下:
如果要实现 import xx as yy 的效果,只要修改左值即可
如下示例,等价于 import os as myos:
它的简单示例:
如果要实现 import xx as yy效果,可以这样
从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib 模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib 模块的合集。
语法如下:
参数有这么几个:
filename:文件名。
globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
但是原理值得借鉴,你可以使用 open … read 读取文件内容,然后再用 exec 去执行模块。
示例如下:
这个包使用了PEP 中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。
pip 要保证是较新版本,如果不是请执行如下命令进行升级。
确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com
示例如下
看了 import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 pip 来安装那些没有安装的包,然后使用Python的import()函数来引入新安装的模块。
并且在远程服务器上开启 http 服务,并且手动编辑一个名为 my_info 的 python 文件,如果后面导入成功会打印 ok。
一切准备好,验证开始。
好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import 这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习import 以及 importlib 是非常有必要的。
Java8新特性-Optional类
在Java应用开发中,避免NPE问题一直是开发者面临的一大挑战。Guava项目通过引入Optional类,为解决这一问题提供了全新的思路。Optional类作为Java 8的一部分,旨在优雅地解决NPE问题,促进代码简洁性和可读性。
Optional类是Java中用于表示可能不存在的值的容器类,它用`value`变量存储实际值,或仅存储`null`,以表示值不存在。相比使用`null`来表示无值状态,Optional更精确地描述了值的有无,有效避免了空指针异常,并鼓励了函数式编程风格的实现。
基本使用示例展示了如何获取用户所在地方的编号。引入Optional后,只需在最后执行一次空值判断,极大简化了代码结构,同时提供了`orElse`、`orElseGet`、`orElseThrow`等方法,为处理空值提供了灵活的解决方案。调用Optional的`toString()`方法时,若值为空,则返回`"Optional.empty"`,避免了直接抛出空指针异常。
Optional类提供了丰富的API以进行数据操作。通过`map`、`filter`、`flatMap`等方法,开发者可以对包装对象进行转换和过滤,确保操作的安全性。这些方法在处理值存在性的同时,保持了代码的简洁性和功能性。
获取值时,Optional提供了多种方法,每种方法依据需求不同而设计,确保了在确保代码安全的同时,提供了灵活的访问方式。
深入Optional的源码分析,探究了构造方法、实例方法、空值判断、数据处理和数据获取等关键部分,展示了Optional如何在内部结构和功能上实现其独特设计,从而在Java生态系统中扮演了关键角色。通过其高效的API和清晰的设计,Optional类不仅简化了代码实现,还提升了开发者的编程体验,是现代Java应用开发中不可或缺的工具。
Java8之Optional中orElse和orElseGet的区别
在探讨Java8的Optional类中orElse和orElseGet的区别时,许多文章常会提出类似的疑问,例如以下例子所示:
初见此场景,可能感到疑惑:明明已有值,为何还要执行?这似乎违背了orElse的初衷。带着疑问,我们深入查看了orElse的源码。
初时,对于传入类调用与接收对象间的关联,感到困惑,直到豁然开朗:在执行orElse之前,参数值的获取是必不可少的。因此,执行传入的方法是必须的步骤。
实际跟踪代码,我们可以发现,执行orElse之前,已调用了getDefault方法。进一步对比orElseGet的源码,更清晰地理解了两者间的关键差异。
通过思考,我渐渐领悟到了orElse和orElseGet语义的本质区别。