Java 开发最容易写的 10 个bug,你中招了没?

Arrays.asList() 将返回一个 ArrayList,它是 Arrays 中的私有静态类,它不是 java.util.ArrayList 类。如下图所示

Java 开发最容易写的 10 个bug

Arrays 内部的 ArrayList 只有 set、get、contains 等方法,但是没有能够像是 add 这种能够使其内部结构进行改变的方法,所以 Arrays 内部的 ArrayList 的大小是固定的。

Java 开发最容易写的 10 个bug

如果要创建一个能够添加元素的 ArrayList ,你可以使用下面这种创建方式:

ArrayList arrayList = new ArrayList(Arrays.asList(arr));

因为 ArrayList 的构造方法是可以接收一个 Collection 集合的,所以这种创建方式是可行的。

Java 开发最容易写的 10 个bug

错误二:检查数组是否包含某个值

===============

检查数组中是否包含某个值,部分程序员经常会这么做:

Set set = new HashSet(Arrays.asList(arr));

return set.contains(targetValue);

这段代码虽然没错,但是有额外的性能损耗,正常情况下,不用将其再转换为 set,直接这么做就好了:

return Arrays.asList(arr).contains(targetValue);

或者使用下面这种方式(穷举法,循环判断)

for(String s: arr){

if(s.equals(targetValue))

return true;

}

return false;

上面第一段代码比第二段更具有可读性。

错误三:在 List 中循环删除元素

==================

这个错误我相信很多小伙伴都知道了,在循环中删除元素是个禁忌,有段时间内我在审查代码的时候就喜欢看团队的其他小伙伴有没有犯这个错误。

Java 开发最容易写的 10 个bug

说到底,为什么不能这么做(集合内删除元素)呢?且看下面代码

ArrayList list = new ArrayList(Arrays.asList(“a”, “b”, “c”, “d”));

for (int i = 0; i < list.size(); i++) {

list.remove(i);

}

System.out.println(list);

这个输出结果你能想到么?是不是蠢蠢欲动想试一波了?

答案其实是 [b,d]

为什么只有两个值?我这不是循环输出的么?

其实,在列表内部,当你使用外部 remove 的时候,一旦 remove 一个元素后,其列表的内部结构会发生改变,一开始集合总容量是 4,remove 一个元素之后就会变为 3,然后再和 i 进行比较判断。。。。。。所以只能输出两个元素。

你可能知道使用迭代器是正确的 remove 元素的方式,你还可能知道 for-each 和 iterator 这种工作方式类似,所以你写下了如下代码

ArrayList list = new ArrayList(Arrays.asList(“a”, “b”, “c”, “d”));

for (String s : list) {

if (s.equals(“a”))

list.remove(s);

}

然后你充满自信的 run xxx.main() 方法,结果。。。。。。ConcurrentModificationException

为啥呢?

那是因为使用 ArrayList 中外部 remove 元素,会造成其内部结构和游标的改变。

在阿里开发规范上,也有不要在 for-each 循环内对元素进行 remove/add 操作的说明。

Java 开发最容易写的 10 个bug

所以大家要使用 List 进行元素的添加或者删除操作,一定要使用迭代器进行删除。也就是

ArrayList list = new ArrayList(Arrays.asList(“a”, “b”, “c”, “d”));

Iterator iter = list.iterator();

while (iter.hasNext()) {

String s = iter.next();

if (s.equals(“a”)) {

iter.remove();

}

}

.next() 必须在 .remove() 之前调用。在 foreach 循环中,编译器会在删除元素的操作后调用 .next(),导致ConcurrentModificationException。

错误四:Hashtable 和 HashMap

=======================

这是一条算法方面的规约:按照算法的约定,Hashtable 是数据结构的名称,但是在 Java 中,数据结构的名称是 HashMap,Hashtable 和 HashMap 的主要区别之一就是 Hashtable 是同步的,所以很多时候你不需要 Hashtable ,而是使用 HashMap。

错误五:使用原始类型的集合

=============

这是一条泛型方面的约束:

在 Java 中,原始类型和无界通配符类型很容易混合在一起。以 Set 为例,Set 是原始类型,而 Set<?> 是无界通配符类型。

比如下面使用原始类型 List 作为参数的代码:

public static void add(List list, Object o){

list.add(o);

}

public static void main(String[] args){

List list = new ArrayList();

add(list, 10);

String s = list.get(0);

}

这段代码会抛出 java.lang.ClassCastException 异常,为啥呢?

Java 开发最容易写的 10 个bug

使用原始类型集合是比较危险的,因为原始类型会跳过泛型检查而且不安全,Set、Set<?> 和 Set 存在巨大的差异,而且泛型在使用中很容易造成类型擦除。

大家都知道,Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java 的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除

如在代码中定义List和List等类型,在编译后都会变成List,JVM 看到的只是List,而由泛型附加的类型信息对 JVM 是看不到的。Java 编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法在运行时刻出现的类型转换异常的情况,类型擦除也是 Java 的泛型与 C++ 模板机制实现方式之间的重要区别。

比如下面这段示例

public class Test {

public static void main(String[] args) {

ArrayList list1 = new ArrayList();

list1.add(“abc”);

ArrayList list2 = new ArrayList();

list2.add(123);

System.out.println(list1.getClass() == list2.getClass());

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

复习的面试资料

这些面试全部出自大厂面试真题和面试合集当中,小编已经为大家整理完毕(PDF版)

  • 第一部分:Java基础-中级-高级

image

  • 第二部分:开源框架(SSM:Spring+SpringMVC+MyBatis)

image

  • 第三部分:性能调优(JVM+MySQL+Tomcat)

image

  • 第四部分:分布式(限流:ZK+Nginx;缓存:Redis+MongoDB+Memcached;通讯:MQ+kafka)

image

  • 第五部分:微服务(SpringBoot+SpringCloud+Dubbo)

image

  • 第六部分:其他:并发编程+设计模式+数据结构与算法+网络

image

进阶学习笔记pdf

  • Java架构进阶之架构筑基篇(Java基础+并发编程+JVM+MySQL+Tomcat+网络+数据结构与算法

image

  • Java架构进阶之开源框架篇(设计模式+Spring+SpringMVC+MyBatis

image

image

image

  • Java架构进阶之分布式架构篇 (限流(ZK/Nginx)+缓存(Redis/MongoDB/Memcached)+通讯(MQ/kafka)

image

image

image

  • Java架构进阶之微服务架构篇(RPC+SpringBoot+SpringCloud+Dubbo+K8s)

image

image

[外链图片转存中…(img-aCiLd9ti-1711947756548)]

  • Java架构进阶之分布式架构篇 (限流(ZK/Nginx)+缓存(Redis/MongoDB/Memcached)+通讯(MQ/kafka)

[外链图片转存中…(img-v8iBLV9U-1711947756548)]

[外链图片转存中…(img-J2LeRl1L-1711947756548)]

[外链图片转存中…(img-xU2c7yCn-1711947756548)]

  • Java架构进阶之微服务架构篇(RPC+SpringBoot+SpringCloud+Dubbo+K8s)

[外链图片转存中…(img-ZmxV4iZq-1711947756549)]

[外链图片转存中…(img-s6RKikjd-1711947756549)]

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值