Java中哪些很容易出现的坑

news2024/11/15 10:33:54

文章目录

    • 1空指针
    • 2小数的计算
    • 3包装类型
    • 4Java8 Stream
    • 5日期格式化

先来一个简单一点,就从空指针开始吧

1空指针

//多级调用空指针
 userService.getUser("张三").getUserInfo().getUserName();
//例如getUser("张三")、getUserInfo()都可能出现null

//equals空指针,当我们的str通过某个方法获取(或者传参),而这个方法可能返回null,其代码等价于如下
String str = null; //xxxService.doSomeThing();
if(str.equals("张三")){
    System.out.println("我是张三");
}

//集合遍历,当我们的list通过某个方法获取(或者传参),而这个方法可能返回null,其代码等价于如下
List<User> list = null; //xxxService.doSomeThing();
for (User user : list) {
	System.out.println(user);
}

//包装类型的空指针,如果使用包装类型,包装类型是可能为null,对其进行算术操作,会出控制
Integer num = null;
System.out.println(num + 1);

2小数的计算

如下图所示,我们挑选了几个数字,做了加减乘除的操作,代码很简单,结果很意外。
在这里插入图片描述
这样的代码很容易就不经意间写出来了,然后BUG就产生了。因此,如果你的代码中有用到包含小数进行加减乘除的操作,及其容易出现意想不到的结果,得到的数会很接近你的目标小数,但是不是你的目标数,如果此时做大小等于判断以及高精度,就很容易出现BUG了。

其主要原因主要有如下

  1. 二进制表示不精确:计算机使用二进制表示浮点数,而十进制的小数无法精确表示为有限长度的二进制小数。某些十进制小数在二进制表示中是无限循环的,因此在转换过程中会有截断或舍入误差。
  2. 浮点数运算规则:浮点数的加法、减法、乘法和除法运算都遵循IEEE 754标准的规则。这些规则对于某些十进制小数可能会产生舍入误差,尤其是在进行多次运算时。
  3. 存储空间限制:浮点数通常使用有限的位数来表示,例如float类型使用32位,double类型使用64位。这种有限的存储空间限制了浮点数的精度,因此在进行计算时可能会丢失一些细微的精度。

解决办法

  1. 需要精确的计算:提高精度,使用高精度的计算,可以使用BigDecimal类,它提供了高精度的十进制计算。使用BigDecimal可以增加浮点数的精度(不能完全消除),但同时也需要更多的计算和内存开销。因此,根据具体需求,需要权衡精度和性能之间的取舍。一般适用于比较严谨或者数值比较大的场景,例如金钱的计算,例如某公司流上千亿流水,某银行的存款有百万亿,这种情况下,即使是亿分之1的精度,每次计算那也是会丢失几百上千,甚至上万的丢失。
  2. 不需要精确的计算:降低精度,保留低精度的结果进行比较,忽略细小的进度差距。例如格式化输出、四舍五入等
//格式化
double result = 0.1 * 0.2;
System.out.println(String.format("%.2f", result)); // 输出:0.02,保留两位小数

//四舍五入  乘法结果乘以100后使用Math.round()进行四舍五入,然后除以100.0来保留两位小数
double result = 0.1 * 0.2;
System.out.println(String.format("%.2f", result)); // 输出:0.02,保留两位小数

//四舍五入,setScale()方法设置保留小数的位数,并指定舍入模式为RoundingMode.HALF_UP进行四舍五入
BigDecimal num1 = new BigDecimal("0.1");
BigDecimal num2 = new BigDecimal("0.2");
BigDecimal result = num1.multiply(num2).setScale(2, RoundingMode.HALF_UP);
System.out.println(result); // 输出:0.02,保留两位小数

备注:BigDecimal的正确使用,如下的三个例子中,可以发现第一种实现方式一样存在精度问题,这是为什么勒?
在这里插入图片描述
我们查看valueOf的源码发现,其首先将浮点数转换成字符串,也就是上面我们的第三种方式
在这里插入图片描述
究其原因,浮点数类型(如 double)是基于二进制表示的,而某些十进制数无法精确表示为有限长度的二进制小数,new BigDecimal(0.1)实际上是将 double 类型的值转换为 BigDecimal,而new BigDecimal(“0.1”)会将传入的参数作为字符串进行精确表示。
当我们使用浮点数参数,代码扫描工具会提示,使用valueOf替换,而使用字符串参数则不会提示。
在这里插入图片描述

3包装类型

在java中,我们看一下基本数据类型有8种。

整数类型:
byte8位有符号整数,取值范围为-128127short16位有符号整数,取值范围为-32,76832,767int32位有符号整数,取值范围为约-21亿到21亿。
long64位有符号整数,取值范围为约-922万亿亿到922万亿亿。

浮点数类型:
float32IEEE 754单精度浮点数,取值范围约为±1.4e-45到±3.4e+38,精度约为6-7位小数。
double64IEEE 754双精度浮点数,取值范围约为±4.9e-324到±1.8e+308,精度约为15位小数。

字符类型:
char16Unicode字符,表示单个字符。

布尔类型:
boolean:表示真或假的值,只有两个取值:truefalse
每个基本数据类型都有对应的包装类,用于提供额外的功能和操作。以下是基本数据类型及其对应的包装类:
byte:对应的包装类是 Byte
short:对应的包装类是 Short
int:对应的包装类是 Integer
long:对应的包装类是 Long
float:对应的包装类是 Float
double:对应的包装类是 Double
char:对应的包装类是 Character
boolean:对应的包装类是 Boolean

在这里,我们以Integer为例说明,先看下面的代码,如果你以前不知道这个坑,那么铁定会有点无语
在这里插入图片描述
1基本类型有默认值

现象:num0_1和num0_2都没有进行赋值,直接打印值时,基本类型的num0_1为0,包装类型的num0_2为null,
原因:基本类型会有一个默认值,而包装类型则没有默认值。
建议:使用基本类型时,避免使用默认值。使用包装类型时,赋予初始值,防止空指针。
例如:使用int表示状态:0表示成功, 1表示失败,此时有个问题,不管是设置成功状态,还是状态没有设置值,其值都是0, 因此可以改为,用1表示成功,2表示失败

2==比较

现象:num1和num2都是127,使用==比较,结果为true,而num5和num6都是128,结果却为false
原因:使用 == 运算符比较两个对象时,会比较它们的引用是否指向同一个对象。这意味着如果两个包装类型对象的引用指向相同的对象,则 == 操作符返回 true,否则返回 false。

对于整数类型的包装类 Byte、Short、Integer 和 Long,Java提供了一个对象缓存池(Object Pool),范围是 -128 到 127。
这意味着当创建一个值在这个范围内的整数类型包装类对象时,会从缓存池中获取已存在的对象,而不是创建新的对象。
当 num1 和 num2 的值都为 127 时,它们在范围内,并且 Byte、Short、Integer 和 Long 类型的对象在范围内的情况下会被缓存。因此,使用 == 比较时,它们引用的是相同的对象,所以结果为 true。

当 num5 和 num6 的值都为 128 时,它们超出了缓存池的范围。在这种情况下,每次创建包装类型对象时,都会创建一个新的对象。因此,虽然它们的值相同,但它们引用的是不同的对象,所以使用 == 比较时结果为 false。

建议:使用包装类提供的 equals() 方法,它会比较对象的值而不仅仅是引用

3构造方法
现象:num3和num4也是127,也在缓存范围值内,==比较结果是false
原因:Integer的构造方法中,并没有使用缓存,缓存是在valueOf方法中使用的,因此是用new Integer(127) 并不会用缓存
在这里插入图片描述
建议:使用包装类提供的 equals() 方法,它会比较对象的值而不仅仅是引用
我的IDE环境中,安装了一些代码扫描插件,我们可以看到,此时提示我们,String和包装类型的比较应该使用equals()
在这里插入图片描述

4Java8 Stream

Java8中的Stream可以简化集合数据的处理和操作,最常见的排序,过滤,去重等,这里我们以排序为例进行操作

filter(Predicate):根据指定条件过滤流中的元素。
map(Function):将流中的每个元素映射到另一个元素。
sorted():对流中的元素进行排序。
distinct():去除流中的重复元素。
limit(n):限制流中元素的数量为 n。
skip(n):跳过流中的前 n 个元素。

我们按照年龄进行排序后,然后取第一个元素,将第一个用户的年龄修改为100,我们发现,原始数据也被修改了
在这里插入图片描述
原因:过滤后的集合中,保存的是对象的引用,该引用只有一份数据。也就是说,只要有一个地方,把该引用对象的成员变量的值,做修改了,其他地方也会同步修改。

5日期格式化

正确的日期格式化:yyyy-MM-dd
错误的日期格式化:YYYY-MM-dd
我们分别使用SimpleDateFormat和java8中的DateTimeFormatter来格式化,有些时间格式化是正确的,有些时间格式化又是错误的。
先看一个时间,2023年12月30日(格式正确的时间)
在这里插入图片描述

在看一个时间,2023年12月31日(格式错误的时间)
在这里插入图片描述
因此,如果你使用的日期格式如果为YYYY-MM-dd,那就是有时时间对,有时时间不对,够你排查喝一壶了

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

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

相关文章

基于SpringBoot的景区旅游管理系统

项目介绍 本期给大家介绍一个 景区旅游管理 系统.。主要模块有首页&#xff0c;旅游路线&#xff0c;旅行攻略&#xff0c;在线预定。管理员可以登录管理后台对用户进行管理&#xff0c;可以添加酒店&#xff0c;景区&#xff0c;攻略&#xff0c;路线等信息。整体完成度比较高…

【Java面试系列】JDK 1.8 新特性之 Stream API

目录 一、Stream 简介二、Stream 特点&#xff1a;Stream 注意点&#xff1a;1、什么是聚合操作2、Stream 流1、什么是流2、流的构成3、stream 流的两种操作4、惰性求值和及早求值方法5、Stream 流的并行 三、Stream操作的三个步骤1、创建流第一种&#xff1a;通过集合第二种&a…

2024全球网络安全展望|构建协同生态,护航数字经济

2024年1月&#xff0c;世界经济论坛发布《2024全球网络安全展望》报告&#xff0c;指出在科技快速发展的背景下&#xff0c;网络安全不均衡问题加剧&#xff0c;需加强公共部门、企业组织和个人的合作。 报告强调&#xff0c;面对地缘政治动荡、技术不确定性和全球经济波动&am…

Vue知识学习

Vue 是什么&#xff1f; 概念&#xff1a;Vue 是一个用于构建用户界面的渐进式框架 Vue 的两种使用方式: ① Vue 核心包开发 场景:局部 模块改造 ② Vue 核心包& Vue插件工程化开发 场景:整站开发 创建Vue 实例&#xff0c;初始化渲染的核心步骤: 1.准备容器 2.引包(官…

哪些软件可以把试卷照片转换成电子版?试试这些软件

哪些软件可以把试卷照片转换成电子版&#xff1f;在数字化时代&#xff0c;纸质试卷的保存和传输都显得不太方便。为了解决这个问题&#xff0c;我们可以将试卷照片转换成电子版。下面&#xff0c;我将为大家介绍5款可以轻松实现这一功能的软件&#xff0c;让你轻松应对各种试卷…

吸虫塔的工作原理是什么?

吸虫塔虫情智能测报分析系统是一款专门用于长期动态监测蚜虫等小型迁飞性害虫的大型植保设备&#xff0c;由装置上方的空气动力装置、上下两层远红外虫体处理装置、高清图像采集装置、虫体收集装置等部分组成。昆虫在经由设备上方的吸风装置后会被吸入设备内部&#xff0c;上下…

【超实用!游戏主程必须掌握的必杀技!】

超实用&#xff01;游戏主程必须掌握的必杀技&#xff01; 大家有没有发现&#xff1f;以上问题都存在共性&#xff1a;那就是跨部门的沟通与协作&#xff0c;这是一个必须高度重视的问题。正是因为这些问题的存在&#xff0c;造成初入职场的焦虑和不适应。 那么产生这些问题的…

【大厂AI课学习笔记】【2.2机器学习开发任务实例】(1)搭建一个机器学习模型

今天学习的是&#xff0c;如何搭建一个机器学习模型。 主要有以上的步骤&#xff1a; 原始数据采集特征工程 数据预处理特征提取特征转换&#xff08;构造&#xff09;预测识别&#xff08;模型训练和测试&#xff09; 在实际工作中&#xff0c;特征比模型更重要。 数据和特征…

01_02_mysql06_(视图-存储过程-函数(变量、流程控制与游标)-触发器)

视图 使用 视图一方面可以帮我们使用表的一部分而不是所有的表&#xff0c;另一方面也可以针对不同的用户制定不同的查询视图。比如&#xff0c;针对一个公司的销售人员&#xff0c;我们只想给他看部分数据&#xff0c;而某些特殊的数据&#xff0c;比如采购的价格&#xff0…

flink 任务提交流程源码解析

flinkjob 提交流程 任务启动流程图1客户端的工作内容1.1解析命令1.2 执行用户代码 2集群工作内容2.2启动JobManager和 ResourceManager2.3 申请资源 启动 taskmanager 3分配任务3.1 资源计算3.2 分发任务 4 Task 任务调度执行图5 任务提交过程总结 任务启动流程图 可以先简单看…

【Vue3】toRefs和toRef在reactive中的一些应用

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

如何在debian上实现一键恢复操作系统?

在Debian或任何其他Linux发行版上实现一键恢复操作系统&#xff0c;需要创建一个系统镜像或快照&#xff0c;并设置一个简单的方法来从该镜像恢复。以下是创建和恢复系统的基本步骤&#xff1a; 1. 创建系统镜像&#xff1a; 使用像dd&#xff0c;rsync或专门的备份工具&#…

详细分析UML的10种图(全)

目录 前言1. 基本知识2. 结构图2.1 类图2.2 对象图2.3 组件图2.4 部署图2.5 包图 3. 行为图3.1 用例图3.2 活动图3.3 状态图 4. 行为图4.1 顺序图4.2 协作图 前言 在软考高级中常见的一种题型&#xff0c;对此补充这方面的知识&#xff0c;并将其归入软考的专栏 1. 基本知识 …

全网最最最详细DataEase源码Docker方式部署教程

1.源码获取 有条件的小伙伴可以使用GitHub方式获取&#xff0c;要是没有条件的小伙伴可以去码云上面获取也是一样的&#xff0c;或者可以联系博主&#xff0c;博主手把手教学~ GitHub地址 Gitee地址 2.配置源码信息 1.配置单机版的配置文件中的数据库信息 2.下载前端的依赖包…

顺序表经典算法及其相关思考

27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 思路一 利用顺序表中的SLDestroy函数的思想&#xff0c;遇到等于val值的就挪动 思路二 双指针法&#xff1a;不停的将和val不相等的数字往前放。此时的des更像一个空数组&#xff0c;里面存放的都是和val不相等、能够存…

java面试JVM虚拟机篇

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&a…

常见消息中间件

ActiveMQ 我们先看ActiveMQ。其实一般早些的项目需要引入消息中间件&#xff0c;都是使用的这个MQ&#xff0c;但是现在用的确实不多了&#xff0c;说白了就是有些过时了。我们去它的官网看一看&#xff0c;你会发现官网已经不活跃了&#xff0c;好久才会更新一次。 它的单机吞…

Unity Meta XR SDK 快捷配置开发工具【Building Block/Quick Action/OVRCameraRigInteraction】

文章目录 &#x1f4d5;教程说明&#x1f4d5;Building Block&#x1f4d5;Quick Action&#x1f4d5;OVRCameraRigInteraction 此教程相关的详细教案&#xff0c;文档&#xff0c;思维导图和工程文件会放入 Spatial XR 社区。这是一个高质量 XR 社区&#xff0c;博主目前在内…

redis在go语言中的使用

redis在go语言中的使用 以下说明以读者有redis基础的前提下进行 未学习redis的可以到b站1小时浅学redis了解大概&#xff0c;学会如何使用 【GeekHour】一小时Redis教程_哔哩哔哩_bilibili 以下开发环境以windows为测试环境&#xff0c;旨在练习redis在go语言中的使用 red…

Java 面向对象进阶 14 抽象类和抽象方法(黑马)

抽象类不能实例化&#xff08;创建对象&#xff09;&#xff1a; 抽象类中不一定有抽象方法&#xff1a; 有抽象方法的类一定是抽象类&#xff1a; 可以有构造方法&#xff1a;&#xff08;作用&#xff1a;在创建子类对象时&#xff0c;给属性进行赋值的&#xff09; Perso…