当前位置: 代码迷 >> 综合 >> java编码常见踩坑
  详细解决方案

java编码常见踩坑

热度:43   发布时间:2023-10-15 06:51:00.0

本博客中的内容参考了阿里的《java开发手册-泰山版》并结合自身的工作经历而撰写,若涉及到版权问题,请联系作者删除,谢谢!

1、pojo类中的任何布尔类型的变量,都不要加is前辍,否则部分框架解析会引起序列化错误。在数据库建表SQL中,布尔类型变量采用is_xxx的命名方式,所以,需要在<resultMap>设置从is_xxx到xxx的映射关系。

2、Object的equals方法容易抛出空指针异常,应使用常量或确定有值的对象来调用equals。推荐使用java.util.Objects#equals(JDK7以后)。

3、任何货币金额/时间单位,均以最小货币单位/时间单位,且整形类型来进行存储。

4、浮点数之间的等值判断,基本数据类型不能用“==”来比较,包装数据类型不能用equals类判断。因为,浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数。

		float f1 = 0.9f - 0.8f;float f2 = 1.9f - 1.8f;// 输出结果:float == float :falseSystem.out.println("float == float :" + (f1 == f2));Float f3 = Float.valueOf(f1);Float f4 = Float.valueOf(f2);// 输出结果:Float equals Float :falseSystem.out.println("Float equals Float :" + (f3.equals(f4)));// 原因如下:// 输出结果:f1 = 0.099999964System.out.println("f1 = " + f1);// 输出结果:f2 = 0.100000024System.out.println("f2 = " + f2);// 输出结果:f3 = 0.099999964System.out.println("f3 = " + f3);// 输出结果:f4 = 0.100000024System.out.println("f4 = " + f4);

解决方法:

1)指定一个误差范围,两个浮点数的差值在此范围内,则认为是相等的。

		float f1 = 0.9f - 0.8f;float f2 = 1.9f - 1.8f;if (Math.abs(f1 - f2) < 0.00001) {System.out.println("true");}

2) 使用BigDecimal来定义两个浮点数的值,再进行运算或比较

		// float f1 = 0.9f - 0.8f;// float f2 = 1.9f - 1.8f;BigDecimal b1 = new BigDecimal("0.9");BigDecimal b2 = new BigDecimal("0.8");BigDecimal b3 = new BigDecimal("1.9");BigDecimal b4 = new BigDecimal("1.8");BigDecimal v1 = b1.subtract(b2);BigDecimal v2 = b3.subtract(b4);if (v1.equals(v2)) {System.out.println("true");}

5、禁止使用构造方法 BigDecimal(double)的方式把 double 值转化为 BigDecimal 对象

说明: BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0.10000000149
正例: 优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了
Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal ( "0.1" );
BigDecimal recommend2 = BigDecimal . valueOf ( 0.1 );
6、关于基本数据类型和包装类:除了方法内部的局部变量可以使用基本数据类型外,其他地方推荐使用包装类型。
1)POJO类的属性强制使用包装类型;
2)RPC的入参和返回值强制使用包装类型;
7、构造方法中严禁加入任何业务逻辑,如果有业务逻辑,可以放置在init()方法中;
8、POJO类必须重写toString()方法,在方法抛出异常时,可以打印POJO的toString()方法打印属性值,便于排查问题。
9、在setter/getter方法中禁止添加业务逻辑,增加问题的排查难度。 
10、日期格式化时,yyyy表示当天所在的年,而YYYY表示当天所在的周所在的年(JDK7之后),一周从周日开始,周六结束,只要本周跨年,返回的YYYY就是下一年。
11、不要在程序中写死一年是365天,避免在闰年时出现日期转换错误。正确的做法如下:
		int lengthOfYear = LocalDate.now().lengthOfYear();lengthOfYear = LocalDate.of(2020, 1, 1).lengthOfYear();

如果使用数字来代表月份,注意Date\Calendar等类中month(月份)的取值是0-11。

12、只要重写equals,就必须重写hashcode方法。特别是set存储的对象是不允许重复的,就是根据hashcode和equals来进行比较判断的。如果使用自定义的对象做为Map的key,则必须重写hashcode和equals方法;String已经重写了hashcode和equals方法,所以我们可以直接使用。

13、判断集合是否为空,推荐使用isEmpty()方法(不需要遍历整个集合),而不是size()>0(需要遍历整个集合); 

14、Map的keyset()、values()、entrySet()返回的集合,不可对其添加元素操作(可以删除元素),否则会抛出java.lang.UnsupportedOperationException异常!

Collections类返回的对象都是不可变的集合,如Collections.singletonList("L1")或Collections.EMPTY_LIST,不可以对其执行添加或删除元素操作,否则会抛出java.lang.UnsupportedOperationException异常。

15、使用集合转数组的方法,必须使用集合的toArray(T[] array),必须传入类型一致长度为0的数组,此时toArray方法会动态创建与集合size大小一致的数组,性能最好。

		List<String> list = new ArrayList<>();list.add("L1");list.add("L2");list.add("L3");String[] array = list.toArray(new String[0]);for (String str : array) {System.out.println(str);}

16、使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合的相关方法,比如add/remove/clear,否则会抛出java.lang.UnsupportedOperationException异常

		String[] strs = new String[] {"1","2","3"};List<String> list = Arrays.asList(strs);// 这里会抛出异常java.lang.UnsupportedOperationExceptionlist.add("4");System.out.println(list.toString());

17、不要在foreach循环里进行元素的remove/add操作.remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

		List<String> list = new ArrayList<>();list.add("1");list.add("2");// 抛出java.util.ConcurrentModificationException异常for (String item : list) {if ("2".equals(item)) {list.remove(item);}}//		Iterator<String> iterator = list.iterator();
//		while (iterator.hasNext()) {
//			String item = iterator.next();
//			if ("2".equals(item)) {
//				iterator.remove();
//			}
//		}System.out.println(list.toString());

未完待续......