Java8实战-总结36

news2025/1/4 18:52:43

Java8实战-总结36

  • 重构、测试和调试
    • 调试
      • 查看栈跟踪
      • 使用日志调试
    • 小结

重构、测试和调试

调试

调试有问题的代码时,程序员的兵器库里有两大老式武器,分别是:

  • 查看栈跟踪
  • 输出日志

查看栈跟踪

程序突然停止运行(比如突然抛出一个异常),这时首先要调查程序在什么地方发生了异常以及为什么会发生该异常。这时栈帧就非常有用。程序的每次方法调用都会产生相应的调用信息,包括程序中方法调用的位置、该方法调用使用的参数、被调用方法的本地变量。这些信息被保存在栈帧上。

程序失败时,会得到它的栈跟踪,通过一个又一个栈帧,可以了解程序失败时的概略信息。换句话说,通过这些能得到程序失败时的方法调用列表。这些方法调用列表最终会帮助发现问题出现的原因。

Lambda表达式和栈跟踪
不幸的是,由于Lambda表达式没有名字,它的栈跟踪可能很难分析。在下面这段简单的代码中,刻意地引入了一些错误:

import java.util.*; 
public class Debugging { 
	public static void main(String[] args) { 
		List<Point> points = Arrays.asList(new Point(12, 2), null); 
		points.stream().map(p -> p.getX()).forEach(System.out::println); 
	} 
} 

运行这段代码会产生下面的栈跟踪:

Exception in thread "main" java.lang.NullPointerException 
	at Debugging.lambda$main$0(Debugging.java:6)
	at Debugging$$Lambda$5/284720968.apply(Unknown Source) 
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) 
 	at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)

这段程序当然会失败,因为Points列表的第二个元素是空(null)。

这时的程序实际是在试图处理一个空引用。由于Stream流水线发生了错误,构成Stream流水线的整个方法调用序列都暴露在你面前了。不过,你留意到了吗?栈跟踪中还包含下面这样类似加密的内容:

at Debugging.lambda$main$0(Debugging.java:6) 
 at Debugging$$Lambda$5/284720968.apply(Unknown Source) 

这些表示错误发生在Lambda表达式内部。由于Lambda表达式没有名字,所以编译器只能为它们指定一个名字。这个例子中,它的名字是lambda$main$0,看起来非常不直观。如果使用了大量的类,其中又包含多个Lambda表达式,这就成了一个非常头痛的问题。

即使使用了方法引用,还是有可能出现栈无法显示你使用的方法名的情况。将之前的Lambda表达式p-> p.getX()替换为方法引用reference Point::getX也会产生难于分析的栈跟踪:

points.stream().map(Point::getX).forEach(System.out::println); 
Exception in thread "main" java.lang.NullPointerException 
 at Debugging$$Lambda$5/284720968.apply(Unknown Source)
 at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)

注意,如果方法引用指向的是同一个类中声明的方法,那么它的名称是可以在栈跟踪中显示的。比如,下面这个例子:

import java.util.*; 
public class Debugging{ 
	public static void main(String[] args) { 
		List<Integer> numbers = Arrays.asList(1, 2, 3); 
		numbers.stream().map(Debugging::divideByZero).forEach(System.out::println); 
 	} 
 	
 	public static int divideByZero(int n) { 
 		return n / 0; 
 	} 
} 

方法divideByZero在栈跟踪中就正确地显示了:

Exception in thread "main" java.lang.ArithmeticException: / by zero 
	at Debugging.divideByZero(Debugging.java:10) 
	at Debugging$$Lambda$1/999966131.apply(Unknown Source) 
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)

总的来说,需要特别注意,涉及Lambda表达式的栈跟踪可能非常难理解。

使用日志调试

假设试图对流操作中的流水线进行调试,该从何入手呢?可以像下面的例子那样,使用forEach将流操作的结果日志输出到屏幕上或者记录到日志文件中:

List<Integer> numbers = Arrays.asList(2, 3, 4, 5); 
numbers.stream()
	.map(x -> x + 17) 
 	.filter(x -> x % 2 == 0) 
 	.limit(3) 
 	.forEach(System.out::println); 

这段代码的输出如下:

20 
22 

一旦调用forEach,整个流就会恢复运行。到底哪种方式能更有效地帮助我们理解Stream流水线中的每个操作(比如mapfilterlimit)产生的输出?

这就是流操作方法peek大显身手的时候。peek的设计初衷就是在流的每个元素恢复运行之前,插入执行一个动作。但是它不像forEach那样恢复整个流的运行,而是在一个元素上完成操作之后,它只会将操作顺承到流水线中的下一个操作。下图解释了peek的操作流程。下面的这段代码中,使用peek输出了Stream流水线操作之前和操作之后的中间值:

List<Integer> result = numbers.stream()
	.peek(x -> System.out.println("from stream: " + x))
	.map(x -> x + 17)
	.peek(x -> System.out.println("after map: " + x))
	.filter(x -> x % 2 == 0)
	.peek(x -> System.out.println("after filter: " + x))
	.limit(3)
	.peek(x -> System.out.println("after limit: " + x))
	.collect(toList()); 

在这里插入图片描述
通过peek操作能清楚地了解流水线操作中每一步的输出结果:

from stream: 2 
after map: 19
from stream: 3 
after map: 20 
after filter: 20 
after limit: 20 
from stream: 4 
after map: 21 
from stream: 5 
after map: 22 
after filter: 22 
after limit: 22 

小结

  • Lambda表达式能提升代码的可读性和灵活性。

  • 如果你的代码中使用了匿名类,尽量用Lambda表达式替换它们,但是要注意二者间语义的微妙差别,比如关键字this,以及变量隐藏。

  • Lambda表达式比起来,方法引用的可读性更好 。

  • 尽量使用Stream API替换迭代式的集合处理。

  • Lambda表达式有助于避免使用面向对象设计模式时容易出现的僵化的模板代码,典型的比如策略模式、模板方法、观察者模式、责任链模式,以及工厂模式。

  • 即使采用了Lambda表达式,也同样可以进行单元测试,但是通常应该关注使用了 Lambda表达式的方法的行为。

  • 尽量将复杂的Lambda表达式抽象到普通方法中。

  • Lambda表达式会让栈跟踪的分析变得更为复杂。

  • 流提供的peek方法在分析Stream流水线时,能将中间变量的值输出到日志中,是非常有用的工具。

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

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

相关文章

Win10 cmd如何试用tar命令压缩和解压文件夹

环境&#xff1a; Win10 专业版 Microsoft Windows [版本 10.0.19041.208] 问题描述&#xff1a; Win10 cmd如何试用tar命令压缩和解压文件夹 C:\Users\Administrator>tar --help tar(bsdtar): manipulate archive files First option must be a mode specifier:-c Cre…

ElementUI之首页导航+左侧菜单->mockjs,总线

mockjs总线 1.mockjs 什么是Mock.js 前后端分离开发开发过程当中&#xff0c;经常会遇到以下几个尴尬的场景&#xff1a; - 老大&#xff0c;接口文档还没输出&#xff0c;我的好多活干不下去啊&#xff01; - 后端小哥&#xff0c;接口写好了没&#xff0c;我要测试啊&#x…

知识图谱(6)基于KG构建问答系统

问答系统概述 问答系统是人类从机器中获取数据与知识的主要形式&#xff0c;问答系统包括NLP的多种应用&#xff1a;语义理解&#xff0c;知识图谱&#xff0c;推理&#xff0c;文本生成。问答系统是检验机器智能的一种方式&#xff08;图灵测试&#xff09;。 图灵测试&#…

C++ 继承详解

目录 C 继承介绍 继承中的特点 public 继承 protected 继承 private 继承 在类里面不写是什么类型&#xff0c;默认是 private 的 如果继承时不显示声明是 private&#xff0c;protected&#xff0c;public 继承&#xff0c;则默认是 private 继承&#xff0c;在 struct …

【每日一题】递枕头

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;模拟方法二&#xff1a; O ( 1 ) O(1) O(1) 解法 写在最后 Tag 【模拟】【 O ( 1 ) O(1) O(1) 公式】【2023-09-26】 题目来源 2582. 递枕头 题目解读 编号从 1 到 n 的 n 个人站成一排传递枕头。最初&#xff0c;排…

知识工程---neo4j 5.12.0+GDS2.4.6安装

&#xff08;已安装好neo4j community 5.12.0&#xff09; 一. GDS下载 jar包下载地址&#xff1a;https://neo4j.com/graph-data-science-software/ 下载得到一个zip压缩包&#xff0c;解压后得到jar包。 二. GDS安装及配置 将解压得到的jar包放入neo4j安装目录下的plugi…

thinkphp5 如何模拟在apifox里面 post数据接收

tp5里面控制器写的方法想直接apifox里面请求接受 必须带上这个参数 header里面 X-Requested-With&#xff1a;XMLHttpRequest

ThreeJS-3D教学一:基础场景创建

Three.js 是一个开源的 JS 3D 图形库&#xff0c;用于创建和展示高性能、交互式的 3D 图形场景。它建立在 WebGL 技术之上&#xff0c;并提供了丰富的功能和工具&#xff0c;使开发者可以轻松地构建令人惊叹的 3D 可视化效果。 Three.js 提供了一套完整的工具和 API&#xff0…

DAMO-YOLO训练KITTI数据集

1.KITTI数据集准备 DAMO-YOLO支持COCO格式的数据集&#xff0c;在训练KITTI之前&#xff0c;需要将KITTI的标注转换为KITTI格式。KITTI的采取逐个文件标注的方式确定的&#xff0c;即一张图片对应一个label文件。下面是KITTI 3D目标检测训练集的第一个标注文件&#xff1a;000…

基于springboot的小说阅读网站设计与实现【附源码】

基于以下技术实现&#xff1a;springbootmybatisplusjsoupmysql 媛麻&#xff1a;可代xie lun文,ding制作网站 在这里插入图片描述

图像处理与计算机视觉--第四章-图像滤波与增强-第二部分

目录 1.图像噪声化处理与卷积平滑 2.图像傅里叶快速变换处理 3.图像腐蚀和膨胀处理 4 图像灰度调整处理 5.图像抖动处理算法 学习计算机视觉方向的几条经验: 1.学习计算机视觉一定不能操之过急&#xff0c;不然往往事倍功半&#xff01; 2.静下心来&#xff0c;理解每一个…

前后端分离的计算机毕设之基于springboot+vue的课程设计选题管理系统(内含源码+文档+教程)

博主介绍&#xff1a;✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久&#xff0c;选择我们就是选择放心、选择安心毕业✌ &#x1f345;由于篇幅限制&#xff0c;想要获取完整文章或者源码&#xff0c;或者代做&am…

vue_Delete `␍`eslint(prettier/prettier)

Delete ␍eslint(prettier/prettier) 错误的解决方案 问题背景 在Windows笔记本上新拉完代码&#xff0c;在执行pre-commit时&#xff0c;出现如下错误&#xff1a; Delete ␍eslint(prettier/prettier)问题根源 罪魁祸首是git的一个配置属性&#xff1a;core.autocrlf 由于…

Kafka数据可靠性保证

1.生产者发送数据到Topic partition的可靠性保证 为保证producer发送的数据&#xff0c;能可靠的发送到指定的topic&#xff0c;topic的每个partition收到producer发送的数据后&#xff0c;都需要向producer发送ack&#xff08;acknowledgement确认收到&#xff09;&#xff0c…

构建卓越语言模型应用的利器:LangChain | 开源日报 No.39

langchain-ai/langchain Stars: 61.3k License: MIT LangChain 是一个用于通过组合性构建 LLMs 应用程序的库。 LLMs 和 Prompts&#xff1a;包括 prompt 管理、prompt 优化、所有 LLM 的通用接口以及与 LLMs 一起使用的常见工具。Chains&#xff1a;超越单个 LLM 调用&…

nodejs+vue大学食堂订餐系统elementui

可以查看会员信息&#xff0c;录入新的会员信息&#xff0c;对会员的信息进行管理。 网站管理模块对整个网站中的信息进行管理&#xff0c;可以查看会员留在留言栏中的信息&#xff0c;设置网站中的参数等。用户管理模块主要实现用户添加、用户修改、用户删除等功能。 近年来&…

C++与数据结构面经(重中之重)

多线程 互斥锁 原子变量 自旋锁 C11新特性 智能指针 首先智能指针是一个类&#xff0c;超过类的作用域会进行析构&#xff0c;所以不用担心内存泄漏。Unique_ptr(独占指针):规定一个智能指针独占一块内存资源。当两个智能指针同时指向一块内存&#xff0c;编译报错。 不允…

华南理工大学电子与信息学院23年预推免复试面试经验贴

运气较好&#xff0c;复试分数90.24&#xff0c;电科学硕分数线84、信通83、专硕电子与信息74. 面试流程&#xff1a; 1&#xff1a;5min ppt的介绍。其中前2min用英语简要介绍基本信息&#xff0c;后3min可用英语也可用中文 介绍具体项目信息如大创、科研、竞赛等&#xff08…

ThrowableError in Arr.php line 380

欢迎关注我的公众号&#xff1a;夜说猫&#xff0c;每周新闻点评~ 前言 今天重装了宝塔之后重装php&#xff0c;遇到了一个问题&#xff0c;如下 ThrowableError in Arr.php line 380 Parse error: syntax error, unexpected 提示我语法错误。 报错原因 主要是thinkphp5.1…

【新版】系统架构设计师 - 软件架构的演化与维护

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 软件架构的演化与维护考点摘要软件架构演化和定义面向对象软件架构演化对象演化消息演化复合片段演化约束演化 软件架构演化方式静态演化动态演化 软件架构演化原则软件架构演化评估方法大型网站架…