Java开发中如何正确踩坑

为什么说一个好的员工能顶 100 个普通员工

我们的做法是,要用最好的人。我一直都认为研发本身是很有创造性的,如果人不放松,或不够聪明,都很难做得好。你要找到最好的人,一个好的工程师不是顶10个,是顶100个。所以,在核心工程师上面,大家一定要不惜血本去找,千万不要想偷懒只用培养大学生的方法去做。最好的人本身有很强的驱动力,你只要把他放到他喜欢的事情上,让他自己有玩的心态,他才能真正做出一些事情,打动他自己,才能打动别人。所以你今天看到我们很多的工程师,他自己在边玩边创新。所以,找最好的人,要给他做他喜欢和擅长的事情。研发人员千万不要去管太严,一管就“死”了。工程师很讨厌跟规章制度打交道,作汇报他都很烦,大家不要管他,让用户去管他。他做好了一个产品,用户表扬他,这个大神多牛逼。他做不好了,用户骂他,他自己赶紧去改。

再谈阿里巴巴 Java 开发手册

之前在这个手册刚发布的时候看过一遍,当时感觉真是每个开发者都应该必读的一本手册,期间还写过一篇关于日志规约的文章:《下一个项目为什么要用 SLF4J》,最近由于在总结一些我们日常开发中容易忽略的问题,可能是最低级的编码常见问题,往往这也是最最容易忽略的,所以,又重新看了一遍这个手册,好像最近它也更新到了 1.2 版本。

这个手册目的就是让我们尽可能少踩坑,杜绝踩重复的坑。我接下来就打算试着写一些“坑”出来,来看看我们如何一不留神踩坑的,以及如何用正确的姿势跳出坑。

随随便便写出 NPE

首先声明一个 User 对象,接下来所有代码可能都会用到这个对象做演示,在下面将不在赘述。很简单,不上代码,上图片:

1.自动解箱抛 NPE 代码只有一行,再简单不过了:int method() { return new User().getId(); }

踩坑姿势:包装类型为 null 时,进行自动转换为基本数据类型报错。 解决方案:返回之前进行判断与处理或者改为相同类型。

2.级联调用易产生 NPE 这段代码有点容易迷惑人,因为它进行了集合元素的 isEmpty 判断,按说不会出问题了吧。看代码:

static void method1() {    List<User> list = new ArrayList<User>();    list.add(new User());
    if (!CollectionUtils.isEmpty(list)) {        for (User user : list) {            System.out.println("userid:" + user.getId().toString());        }    }}

不废话,看运行结果:

没错,还是报错了。

踩坑姿势:其实就是尽管你在之前做了对象不为空的判断,但你并不能保证对象中的值不为空,而且这时候去级联调用就会抛 NPE 。

手册中关于 NPE 的描述:

防止 NPE 是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败、序列化失败、运行时异常等场景返回 null 的情况。 集合里的元素即使 isEmpty,取出的数据元素也可能为 null。 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE

3.关于 Equals 这是日常开发中用于相等比较使用最多的方法了吧,因为当年谁没被 == 坑过阿。现在一般我们都会这么写:user.getName().equals("mafly");

踩坑姿势: 一不小心使用了 null 值调用了 Equals 方法。 解决方案: 很简单咯,这么写:"mafly".equals(user.getName());

equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。

4.Map 下的 NPE Map 应该是我们开发中使用最频繁的了,最常用的可能有 HashMapConcurrentHashMap 这俩了,可能会一不留神写出这样的代码:

踩坑姿势: 可能我们知道 ConcurrentHashMap 的 K/V 都不能为空,但我们有时候并不知道传进来的值是否为空。 解决方案: 设置时做下检验,对它的特性正确理解及使用。

由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值,而事实上, 存储 null 值时会抛出 NPE 异常

Map 类集合 K/V 能不能存储 null 值的情况,如下表格:

集合类

Key

Value

Super

说明

Hashtable

不允许为 null

不允许为 null

Dictionary

线程安全

ConcurrentHashMap

不允许为 null

不允许为 null

AbstractMap

分段锁技术

TreeMap

不允许为 null

允许为 null

AbstractMap

线程不安全

HashMap

允许为 null

允许为 null

AbstractMap

线程不安全

简单聊聊常用的集合

5.foreach 遍历集合删除元素

大家应该都知道,在遍历集合时对元素进行 add/remove 操作要使用 Iterator,使用 for 循环时会报错,一定会报错吗?看代码:

public static void main(String[] args) {    List<String> a = new ArrayList<>();    a.add("1");    a.add("2");    a.add("3");
    for (String temp : a) {        if ("2".equals(temp)) {            a.remove(temp);        }    }
    Iterator<String> it = a.iterator();    while (it.hasNext()) {        String temp = it.next();        if ("2".equals(temp)) {            it.remove();        }    }}

应该会报错的吧?因为在 for 循环中移出了元素,如果你运行了就会惊讶的,输出如下:

不解释其中原因了,不管是不是倒数第二个元素才没问题,我们依然要注意不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式(代码第二种),如果并发操作,需要对 Iterator 对象加锁。

6.Arrays.asList() 数组转换集合 这个工具类应该都用过,可以很方便的把数组转换为集合,直接看结果吧:

踩坑姿势: Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。 asList() 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。 解决方案: 在转换之前操作咯。还需要注意一点,在你转换后,再对数组的值进行修改时,集合也会跟着变哦(注释掉的代码)。

7. toArray() 集合转换数组

当我们需要把一个集合转换为数组时,往往会调用 toArray() 方法,如果你用的是无参的这个可以吗?

当然不可以啦!会报 ClassCastException 异常。 踩坑姿势: 直接使用 toArray() 无参方法返回值只能是 Object[]类,若强转其它类型数组将会抛异常。 解决方案: 使用 <T> T[] toArray(T[] a); 有参数这个方法,代码如下:

String[] array = new String[list.size()];
array = list.toArray(array);

8. subList 的使用 集合中的 subList 是用于来返回某一部分的视图内容的,可能我们不是很常用,但是其中有好多坑的,直接看代码:

这次我们从输出来看上面的所有关于 subList 的代码。

  • 18行: 当你原始集合大小没有那么大时,毫无疑问抛异常。
  • 20-21行:得到一个新的集合,我们往新集合中增加一条数据。
  • 23-26行:遍历原始集合,竟然 size=2 了,而且往新集合中增加的数据存在与原始集合。
  • 28-31行:移除新集合中一条数据,遍历新集合。
  • 33-37行:原始集合增加一条数据并遍历。
  • 40-42行:遍历新集合,抛出 ConcurrentModificationException 异常。

从上述代码中,我们应该可以得出如下结论:返回的新集合是靠原来的集合支持的,修改都会影响到彼此对方。在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生异常。

先总结一下

写到这只是其中关于异常部分的一些坑吧,还有另外一些令人异常惊讶的“我的天吶”的问题,由于篇幅太长了点,感觉不能再写下去了,过两天再接着写吧。

异常真的是一个有意思的问题。

原文发布于微信公众号 - Java团长(javatuanzhang)

原文发表时间:2017-12-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏分布式系统和大数据处理

C#中的泛型

.Net 1.1版本最受诟病的一个缺陷就是没有提供对泛型的支持。通过使用泛型,我们可以极大地提高代码的重用度,同时还可以获得强类型的支持,避免了隐式的装箱、拆箱...

987
来自专栏walterlv - 吕毅的博客

从 “x is null 和 x == null” 的区别看 C# 7 模式匹配中常量和 null 的匹配

发布于 2017-11-06 15:24 更新于 2018-02...

711
来自专栏AzMark

Python学习之面向对象

1213
来自专栏小詹同学

Leetcode打卡 | No.017 电话号码的字母组合

欢迎和小詹一起定期刷leetcode,每周一和周五更新一题,每一题都吃透,欢迎一题多解,寻找最优解!这个记录帖哪怕只有一个读者,小詹也会坚持刷下去的!

1483
来自专栏哲学驱动设计

绑定子类的泛型基类,反模式?

    这次总结一个个人认为的反模式:“绑定子类的泛型层基类”,这个模式在一些著名的框架中也见到过,如果CSLA、BlogEngine。我自己在原来的写的框架中...

1955
来自专栏开发 & 算法杂谈

LeetCode之Implement strStr()

Returns the index of the first occurrence of needle in haystack, or -1 if needl...

704
来自专栏Danny的专栏

数据脱敏——基于Java自定义注解实现日志字段脱敏

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

1.1K1
来自专栏web编程技术分享

JavaScript: 零基础轻松学闭包(1)

2015
来自专栏Java与Android技术栈

为了程序的健壮性,我们可以使用空对象模式

在写代码的时候我们经常会遇到空指针,为了避免空指针的发生需要做一些判断。如果是复杂对象的话,还需要一层层地去判断。这个时候我就无比怀念groovy、kotlin...

1072
来自专栏加米谷大数据

多面编程语言Scala

如Scala官网宣称的:“Object-OrientedMeetsFunctional”,这一句当属对Scala最抽象的精准描述,它把近二十年间大行其道的面向对...

2694

扫码关注云+社区

领取腾讯云代金券