Java 开发者的实操避坑指南

1、Java 空指针处理

使用 Optional 优雅规避空指针时,需要注意什么?

Optional:
在这里插入图片描述

在这里插入图片描述


Option 优雅判空示例:
在这里插入图片描述

2、Java 异常处理

推荐 - 统一异常的处理,避免满屏的 try - catch
在这里插入图片描述

1. 并发修改异常(遍历集合时,删除集合的元素)

建议使用 JDK8 的 Stream 过滤,不建议遍历删除。否则务必使用 Iterator 迭代器遍历删除集合元素。

2.类型转换异常

当转换一个类,却不能确定该类的具体类型时,有以下两种方法获取对象的类型

1.object.getClass().getName(); - 返回类的类型,如:pers.cjh.java.Worker

2.object instanceof Manager(类名) - 返回 bool 值

3.枚举异常

当执行 枚举类.valueOf(“xx”),如果 xx 不是枚举类的枚举值,那么就会抛出 java.lang.IllegalArgumentException 异常。而很多情况下,xx 是用户输入的值,我们并不能保证其是否合理。
在这里插入图片描述
在这里插入图片描述

解决方案:
使用 Google Guava Enums,需要相关的依赖。

Guava 的依赖

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>12</version>
</dependency>

在这里插入图片描述
在这里插入图片描述
当存在枚举值时,照常返回枚举值。如果不存在枚举值,不会抛出异常,而是返回 Optional.absent(),即 Optional 类的子类 Absent 类。

使用 try finally 资源泄露隐患

初级程序的资源关闭:

// 1. 单个资源关闭
InputStream in = new FileInputStream("");
try {
	// do
} finally {
	in.close();
}

// 2. 多个资源的关闭
InputStream in = new FileInputStream("");
try {
	OutputStream out = new FileOutputStream("");
	try {
		byte[] buf = new byte[100];
		int n;

		while((n = in.read(buf)) >= 0) {
			out.write(buf, 0, n);
		}
	} finally {
		out.close();
	}
} finally {
	in.close();
}

从以上程序可以看出由 try finally 方式实现资源的关闭有很大的冗余代码,而且如果 try 和 finally 同时抛出异常,那么 finally 中的异常会覆盖 try 产生的异常,即抛出的最终异常中并不会出现 try 抛出的异常,又因为 finally 中的 close 方法也有小概率抛出异常,所以 try finally 实现关闭异常不利于异常排查。

下面介绍 java7 引入的 try with resources 实现自动的资源关闭,这种方式不仅使用更简单而且有更好的容错性。我们只需要去申明他、使用他,关闭的事情不需要我们再去关注,即将原先的 try-catch-finally 简化为 try-catch,而且最终抛出的异常也是 try 代码块产生的异常,便于排查异常。

// 1. 单个资源的关闭
try (BufferedReader br = new BufferedReader(new FileReader(""))) {
	return br.readLine();
}

// 2. 多个资源的关闭
try (FileInputStream in = new FileInputStream("");
	 FileOutputStream out = new FileOutputStream("")
) {
	byte[] buffer = new byte[100];
	int n = 0;
	while((n = in.read(buffer)) != -1) {
		out.write(buffer, 0, n);
}
 

3、Java 计算异常

数值计算经常考虑的三个问题

用于精确计算的类 BigDecimal,BigDecimal 可以用来对超过 16 位有效位的数进行精确的运算,Double 可以处理 16 位的数字,但是在实际运用中,可能需要对更大或者更小的数字进行运算和处理。而 BigDecimal 的核心就是精度,他活跃于对精度敏感的业务中,因为 BigDecimal 创建的是对象,所以我们不能直接使用 ±*/ 等运算,而需要调用对象提供的 API,而方法参数通常也是 BigDecimal 对象。

精度经常需要考虑的三个问题:1.初始化设置精度;2.除法结果的精度匹配;3.数值比较的精度匹配。
在这里插入图片描述

初始化设置精度的匹配解决方案

// 1. 初始化设置精度
BigDecimal decimal = new BigDecimal("12.222");
// 设置精度以后,如果出现了精度丢失就会直接抛出精度丢失异常
BigDecimal result = decimal.setScale(2);
System.out.println(result);

因为上面设置的精度太小,产生了精度丢失,所以抛出了如下的算数异常(精度丢失)。而设置的精度比数字的精度大则没有关系,小数部分会以零补齐。
在这里插入图片描述

当然我们也可以设置如下四舍五入的精度丢失方式,这样就会把超出精度的数字舍弃,而不会抛出异常。
在这里插入图片描述
在这里插入图片描述

除法结果精度匹配的解决方案

BigDecimal 做除法时会出现除不尽的情况。

代码如下:

// 30 / 7 是除不尽的
System.out.println(new BigDecimal("30").divide(new BigDecimal(7 )));

在这里插入图片描述


在数学运算中除不尽是很正常的,我们通常的解决方案就是设置一个有效数位,即确定精度。

实现代码如下:
在这里插入图片描述
在这里插入图片描述

比较中,精度问题导致结果和预期不一致的解决方案

如下代码所言,运行结果的显示指 0 和 0.0 是不相等的。
在这里插入图片描述
所以在使用 BigDecimal 做数值比较时,不要使用 equals 方法,而应该使用 compareTo 方法,这个方法返回的是 -1、0、1 这三个数值来指代大小。

4、lombok 工具使用上的两个坑

变量名的第二个字母是大写

其实第二个字母不能是大写是 JavaBean 的编码规范,如果我们没有遵守这个规范,在使用 lombok 时,可能会出现给变量赋值不上去的情况。例如:有一个变量名为 aFiled 而且其对应的 json 名也是 aFiled,但是我们会发现在使用 Jackson 反序列化数据的时候 aFiled 变量是不能被正确赋值的,即打印输出的仍然是 null。又因为远程调用组件 Feign 默认是采用 Jackson 反序列化数据,所以第二个字母不能是大写的规范务必要遵守。

Jackson 默认是根据 JavaBean 规范找到对应属性后再赋值,也就是说 Jackson 并没有在这个对象找到 aFiled 属性。JavaBean 属性名称跟变量名称是不一定相同的。JavaBean 是通过对象的 get、set 方法来确定对象属性,其属性名称是由其对象变量来决定的,通常的逻辑是:将属性首字母转成大写。但也有例外,就是前两个字母有大写的情况,如下是整理出来的对于变量前两个字母出现大写时候的 JavaBean 规范:
在这里插入图片描述
从上图可以得出的结论就是:如果变量名称的前两个字母存在大写,那么依据 JavaBean 规范生成的 get、set 方法不用改变变量名称(即首字母不用再大写了)。我们项目上使用的是 lombok,其生成的 get、set 方法是不遵循 JavaBean 规范的,只是将变量名的首字母大写而已,所以它生成 aFiled 的方法还是会把首字母大写 getAFiled。所以,Jackson 在接收到 aFiled 属性值,它依据 JavaBean 规范会到对象找 setaFiled 方法,自然这个对象是没有这个方法的,所以就没映射到 aFiled 字段上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值