线上问题年年有,今年特别多。记几次线上惨痛的踩坑记录,希望大家以史为鉴。


1. 包装类型自动解箱导致空指针异常

public int getId() {Integer id = null;return id;
}

如果调用上面的方**发生什么?id是Integer类型,而方法的返回值int类型,会自动拆箱转换,由于id是null,转换成int类型的时候,就会报NullPointerException异常。

无论是《阿里Java开发手册》、《代码整洁之道》还是《Effective Java》都建议方法返回值类型尽量写成包装类型,类似Integer。还有实体类、接收前端传参类、给前端的响应类中的属性都要写成包装类型,避免拆箱出错。

2. 包装类型用==判断相等,导致判断不正确

先看一段代码运行结果:

public class IntegerTest {public static void main(String[] args) {Integer a = 100;Integer b = 100;Integer c = 200;Integer d = 200;System.out.println(a == b); // 输出 trueSystem.out.println(c == d); // 输出 false}
}

很多人会很疑惑,为什么输出的两个结果会不一样?

当给Integer类型赋值时,会调用Integer.valueOf()方法

static final int low = -128;
static final int high = 127;public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
}

当value值在-128到127之间时,会复用缓存。当不在这个区间时,才会创建对象。

而==比较的是内存地址,不同的对象的内存地址不相同,所以就出现上述的结果。

Integer重写了equals()方法:

public boolean equals(Object obj) {if (obj instanceof Integer) {return value == ((Integer)obj).intValue();}return false;
}

当使用equals()方法时,比较的是int值是否相等。

所以,包装类判断是否相等的时候,绝不能用==判断,一定要用equals()方法判断。

3. Switch传参是null导致空指针异常

猜一下下面代码的运行结果:

public class Test {public static void main(String[] args) {String name = null;switch (name) {case "yideng":System.out.println("一灯");break;default:System.out.println("default");}}
}

你是不是认为会输出default,其实代码会抛出NullPointerException异常。

当switch比较两个对象是否相等的时候,会调用name.hashCode()方法和name.equals()方法,因为name是null,结果就抛出了NullPointerException异常。

所以调用switch方法前,一定要对传参进行判空。

4. 创建BigDecimal类型时精度丢失

猜一下下面代码的运行结果:

public class Test {public static void main(String[] args) {BigDecimal bigDecimal = new BigDecimal(0.1);System.out.println(bigDecimal);}
}

你以为会输出0.1,其实输出结果是:

0.1000000000000000055511151231257827021181583404541015625

What?这么一大串是什么东西?

为什么会出现这种情况呢?原因是,当我们用new BigDecimal(0.1)创建对象是,会调用BigDecimal的这个构造方法:

public BigDecimal(double val) {this(val,MathContext.UNLIMITED);
}

把传参0.1当成了double类型,double计算的时候会把数值转换成二进制,而0.1转换成二进制是无法除尽的,所以就带了一大串小数位。

当需要创建BigDecimal类型时,应该怎么做呢?

可以先把数值转换成字符串类型,再创建BigDecimal对象,类似这样:

BigDecimal bigDecimal = new BigDecimal(String.valueOf(0.1));

又来一个问题,BigDecimal是怎么解决精度丢失问题?

答案是BigDecimal会先把数值乘以10的整数倍,去除小数位,转换成long类型,然后进行运算,最后把运算结果除以10的整数倍。

5. group分组时主键重复,导致异常

下面代码的分组能成功吗?

public class SteamTest {static class User {// 用户IDprivate Integer id;// 用户名private String name;}public static void main(String[] args) {List<User> users = Arrays.asList(new User(1, "Tom"),new User(1, "Tony"),new User(2, "Jerry"));// 用户集合按id进行分组Map<Integer, User> userMap = users.stream().collect(Collectors.toMap(User::getId, user -> user));System.out.println(userMap);}
}

结果报异常了,Exception in thread "main" java.lang.IllegalStateException: Duplicate key SteamTest.User(id=1, name=Tom)

原因是主键冲突,有两个id=1的数据,按id进行分组时程序就不知道怎么处理了。

可以这样做

public class SteamTest {static class User {// 用户IDprivate Integer id;// 用户名private String name;}public static void main(String[] args) {List<User> users = Arrays.asList(new User(1, "Tom"),new User(1, "Tony"),new User(2, "Jerry"));// 用户集合按id进行分组,主键冲突的时候,取第一个userMap<Integer, User> userMap = users.stream().collect(Collectors.toMap(User::getId, user -> user, (user1, user2) -> user1));System.out.println(userMap); // 输出 {1:{"id":1,"name":"Tom"},2:{"id":2,"name":"Jerry"}}}
}

6. 真假ArrayList导致添加异常

下面的add()方法能添加成功吗?

public class Test {public static void main(String[] args) {List<Integer> list = Arrays.asList(1, 2);list.add(3);}
}

结果是抛异常了,Exception in thread "main" java.lang.UnsupportedOperationException

抛出了不支持这个方法的异常,为什么呢?我们看一下Arrays.asList()方法的源码:

public static <T> List<T> asList(T... a) {return new ArrayList<>(a);
}

返回了一个ArrayList,为什么还不能添加成功了?

真相是此ArrayList非彼ArrayList,跟我们常用的ArrayList只是重名,这个ArrayList只是Arrays对象一个内部类,内部并没有实现add()方法,所以添加的时候会报错。

这不是明摆着坑人吗?实现了list接口,为啥不实现add()方法?

其实作者是故意这样设计的,除了没有实现add()方法,还没有实现addAll()、remove()、clear()等修改方法,目的就是创建后再不让用户修改,这样的集合有什么用呢?

其实在某些不可变场景还是很实用的,比如已结束的订单状态集合:

List<String> list = Arrays.asList("Failure", "Cancelled","Completed");

这种集合一般不会变的,使用过程中也不允许修改,避免出错。

7. 总结

每一次踩坑,背后都有至少一次的线上问题记录,这些总结都是用教训换来的,不只是自己,其他人肯定也遇到过。我们如何才能避免在以后的开发中再出现类似的问题呢?

  • 站在使用者的角度,编写详细的单元测试,打印必要日志,追踪代码执行结果
  • 站在创造者的角度,探究框架的架构设计和源码实现,理解作者的意图

你在线上还踩过那些坑?

来源: http://www.shaoqun.com/a/1930525.html

Java线上惨痛踩坑记录,你也一定遇到过相关推荐

  1. Java 线上惨痛踩坑记录,你也一定遇到过

    线上问题年年有,今年特别多.记几次线上惨痛的踩坑记录,希望大家以史为鉴. 1. 包装类型自动解箱导致空指针异常 public int getId() { Integer id = null; retu ...

  2. postman+nestjs文件上传踩坑记录

    记录一下最近使用nestjs上传文件踩的坑. file.controller.ts @Post('upload')//注意:这里的'excel'名称一定要和使用postman上传文件时使用的key一样 ...

  3. uc浏览器请求被拦截报跨域踩坑记录

    记录下开发时uc浏览器请求被拦截时遇到的问题 请求在uc浏览器出现跨域问题 app使用uniapp开发,使用plus.runtime.launchApplication来打开并跳转指定页面,并在Xco ...

  4. sonar覆盖率怎么统计的_实战|Java 测试覆盖率 Jacoco插桩的不同形式总结和踩坑记录(上)...

    本文为霍格沃兹测试学院优秀学员关于 Jacoco 的小结和踩坑记录.测试开发进阶学习,文末加群. 一.概述 测试覆盖率是老生常谈的话题.因为我测试理论基础不是很好,这里就不提需求.覆盖率等内容,直奔主 ...

  5. mvn exec: java_实战|Java 测试覆盖率 Jacoco插桩的不同形式总结和踩坑记录(下)

    本文为霍格沃兹测试学院优秀学员关于 Jacoco 的小结和踩坑记录.测试开发进阶学习,文末加群. 六.注意事项汇总 修改 JAVA_OPTS 参数时,如果位置不对,可能造成代理无法启动. java - ...

  6. 微信退款 java工具类,微信支付中退款踩坑记录

    首先附上微信支付的开发者文档 其实这里所说的踩坑记录,无非就是微信在开发者文档上的写不太明确,也没有比较官方的demo,在此列出一个可行的demo,供大家下载使用. 主要问题就是在这几步解密上 微信的 ...

  7. 使用Java读取 “Python写入redis” 的数据踩坑记录

    https://my.oschina.net/u/2338224/blog/3061507 使用Java读取 "Python写入redis" 的数据踩坑记录 https://seg ...

  8. 新手可直接复现:Duan版本CenterNet在2080Ti上训练自己的数据集——踩坑记录

    实验室换了GPU,记录下centernet在新电脑上的配置过程,如果对下面很多不理解先阅读我以前的这篇博客即可初学者复现CornerNet:详细指导零基础在Ubuntu系统运行该代码并完全理解论文思路 ...

  9. 日常踩坑记录-汇总版

    开发踩坑记录,不定时更新 心得 RTFM 严谨的去思考问题,处理问题 严格要求自己的代码编写习惯与风格 注意 单词拼写 20200207 mybatis plus 自带insert插入异常 sql i ...

最新文章

  1. 当网站遇到黑链时该如何进行处理?
  2. TensorFlow安装【2018/12更新】+文档查询以及栏目配置(Windows)
  3. IntelliJ IDEA 导入新项目
  4. ubuntu 使用root用户登录
  5. 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器,sqlserver
  6. php架在底部页面,页脚始终保持在页面底部的网页布局方法
  7. 零基础带你学习MySQL—流程控制函数(十七)
  8. 帆软报表-通过代码来创建一个模板文件
  9. Velocity-模板引擎(代码生成等)
  10. DSOframer 的简单介绍和资源整理(2015-09-02重新整理)
  11. MATLAB人体行为检测与识别
  12. IMDB数据看影响电影票房的因素分析
  13. 【LeetCode】347. Top K Frequent Elements 前 K 个高频元素(Medium)(JAVA)
  14. Hides for Mac v5.6一键隐藏所有应用 支持 M1
  15. 小米手机4S超简单刷成开发版获得ROOT超级权限的流程
  16. (PHP)程序中如何判断当前用户终端是手机等移动终端
  17. Ubuntu 安装MySQL 并设置其他主机可访问
  18. 最优化方法期末考试复习
  19. 转载一篇文章,纪念我的童年~ 炮竹
  20. word转html java pect,THE PROPECT OF COMPUTERISATION IN THE BANKING 在银行计算机化的前景.doc...

热门文章

  1. 如何在网页中时行QQ交谈
  2. JAVA毕业设计可视化工器具信息管理系统计算机源码+lw文档+系统+调试部署+数据库
  3. 茅台1499哪里能抢?说一下茅台1499能卖多少钱以及抢购渠道!
  4. 外滩十八号的长笛女孩[转]
  5. 自己关于学习js的一些经历
  6. DockerToolBox window 下载 与初步使用
  7. sleep()的线程是什么状态
  8. 程序员:这10种糟糕的程序命名,你遇到过几个?
  9. Tabbar:底部导航栏
  10. 【深度学习】Softmax 函数