Java开发中遇到的那些坑!

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

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

再谈阿里巴巴 Java 开发手册

之前在这个手册刚发布的时候看过一遍,当时感觉真是每个开发者都应该必读的一本手册,最近由于在总结一些我们日常开发中容易忽略的问题,可能是最低级的编码常见问题,往往这也是最最容易忽略的,所以,又重新看了一遍这个手册,好像最近它也更新到了 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 应该是我们开发中使用最频繁的了,最常用的可能有 HashMap、ConcurrentHashMap 这俩了,可能会一不留神写出这样的代码:

踩坑姿势: 可能我们知道 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 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生异常。

先总结一下

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

异常真的是一个有意思的问题。小伙伴们要是你遇到什么坑,欢迎在下方留言!

原文发布于微信公众号 - 互扯程序(chat_routine)

原文发表时间:2018-11-26

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏加米谷大数据

多面编程语言Scala

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

2534
来自专栏拂晓风起

java HashMap 极限容量 大小限制 占用内存大小

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

C#中的泛型

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

977
来自专栏小古哥的博客园

读书笔记-JavaScript面向对象编程(一)

前前后后大概花了两周的时间,终于把这本书大致看完了,对之前javascript高级程序设计中模糊不清的概念,有了一些新的看法和角度,整体上来说,本书还是一本比较...

3347
来自专栏Java与Android技术栈

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

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

1062
来自专栏猿人谷

[你必须知道的.NET] 第四回:后来居上:class和struct

本文将介绍以下内容: • 面向对象基本概念 • 类和结构体简介 • 引用类型和值类型区别 1. 引言 提起class和struct,我们首先的感觉是语法几乎相...

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

LeetCode之Implement strStr()

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

684
来自专栏微信公众号:Java团长

Java开发中如何正确踩坑

之前在这个手册刚发布的时候看过一遍,当时感觉真是每个开发者都应该必读的一本手册,期间还写过一篇关于日志规约的文章:《下一个项目为什么要用 SLF4J》,最近由于...

1054
来自专栏写代码的海盗

准备冲锋 golang入坑系列

史前摘要: 本来想写读前必读,但连续几篇博文都写读前必读,感觉就没有了新意。 所以换成史前摘要,反正是一个意思。 此摘要的目的仍然是提醒点击而来的同学,本系列最...

3565
来自专栏带你撸出一手好代码

浅谈 var 关键字

提起 var关键子,程序员的第一反应就是JavaScript, 事实上这个关键子在其他语言中也有被采用。 比如说C#, 比如说kotlin, 用法和JavaSc...

2868

扫码关注云+社区

领取腾讯云代金券