Java 开发者最容易犯的10个错误——你有犯过吗?

这 10 个错误是我综合 GitHub 上的项目、StackOverflow 上的问答和 Google 搜索关键词的趋势而分析得出的。

1 将 Array 转换成 ArrayList 时出错

一些开发者经常用这样的代码将 Array 转换成 ArrayList

List list = Arrays.asList(arr);复制代码

Arrays.asList() 的返回值是一个 ArrayList 类的对象,这个 ArrayList 类是 Arrays 类里的一个私有静态类(java.util.Arrays.ArrayList),并不是 java.util.ArrayList 类。

java.util.Arrays.ArrayList 有 set() / get() / contains() 方法,但是并不提供任何添加元素的方法,因此它的长度是固定的。如果你希望得到一个 java.util.ArrayList 类的实例,你应该这么做:

ArrayList arrayList = new ArrayList(Arrays.asList(arr));复制代码

ArrayList 的构造函数可以接受一个 Collection 实例,而 Collection 是 java.util.Arrays.ArrayList 的超类。

2 检查 array 里是否含有某个值时出错

一些开发者会这么写:

Setset= new HashSet(Arrays.asList(arr));returnset.contains(targetValue);复制代码

上面的代码可以工作,但是其实没必要把 list 转为 set,这有些浪费时间,简单的写法是这样的:

Arrays.asList(arr).contains(targetValue);复制代码

或者这样的

for(String s: arr){if(s.equals(targetValue))returntrue;}returnfalse;复制代码

这两种写法中,前者可读性更好。

3 遍历 list 移除元素时出错

下面的代码在迭代时移除了元素:

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]复制代码

这种代码的问题在于,当元素被移除时,list 的长度也随之变小了,index 也同时发生了变化。所以,如果你想要在循环中使用 index 移除多个元素,它可能不能正常工作。

你可能认为在循环中删除元素的正确方法是迭代器,比如 foreach 循环看起来就是一个迭代器,其实这样还是有问题。

考虑以下代码(代码 1):

ArrayList list = new ArrayList(Arrays.asList("a","b","c","d"));for(String s : list) {if(s.equals("a"))list.remove(s);}复制代码

会抛出 ConcurrentModificationException 异常。

要正确地在遍历时删除元素,应该这么写(代码 2):

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()。

在代码 1 中的 foreach 循环中,编译器会在元素的删除操作之后调用 .next(),导致 ConcurrentModificationException 异常,如果你想深入了解,可以看看 ArrayList.iterator() 的源码

4 用 Hashtable 还是用 HashMap

一般来说,算法中的 Hashtable 是一种常见的数据结构的名字。但是在 Java 中,这种数据结构的名字却是 HashMap,不是 Hashtable。Java 中 Hashtable 和 HashMap 的最重要的区别之一是 Hashtable 是同步的(synchronized)。因此大部分时候你不需要用 Hashtable,应该用 HashMap。

5 直接使用 Collection 的原始类型时出错

在 Java 中,「原始类型」和「无限制通配符类型」很容易被搞混。举例来说,Set 是一个原始类型,而 Set<?> 是一个无限制通配符类型。

下面的代码中的 add 接受原始类型 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);}复制代码

这个代码会在运行时才抛出异常:

Exceptioninthread"main"java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stringat ...复制代码

使用原始类型的 collection 是很危险的,因为原始类型没有泛型检查。Set / Set<?> / Set<Object> 之间有非常大的差异,详情可以看看《Set vs. Set<?>》和《Java Type Erasure Mechanism》。

6 访问级别设置过高

很多开发者为了省事,把类字段标记为 public,这不是个好习惯。好习惯应该是将访问级别设置得越低越好。

详见《public, default, protected, and private》。

7 ArrayList 和 LinkedList 选用错误

如果不了解 ArrayList 和 LinkedList 的区别,你很容易会倾向于使用 ArrayList,因为它看起来更常见。

但是,ArrayList 和 LinkedList 有巨大的性能差异。简单来说,如果 add/remove 操作较多,则应该使用 LinkedList;如果随机访问操作较多,则应该使用 ArrayList。

如果你想深入了解这些性能差异,可以看看《ArrayList vs. LinkedList vs. Vector》。

8 可变还是不可变?

不可变对象有很多好处,比如简单、安全等。但是不可变对了要求每次改动都生成新的对象,对象一多就容易对垃圾回收造成压力。我们应该在可变对象和不可变对象上找到一个平衡点。

一般来说,可变对象可以避免产生太多中间对象。一个经典的例子就是连接大量字符串。如果你使用不可变字符串,你就会造出许多用完即弃的中间对象。这既浪费时间又消耗 CPU,所以这种情况下你应该使用可变对象,如 StringBuilder:

String result="";for(String s: arr){result = result + s;}复制代码

还有一些情况值得使用可变对象。比如你可以通过将可变对象传入方法来收集多个结果,从而绕开语法的限制。再比如排序和过滤操作,虽然你可以返回新的被排序之后的对象,但是如果元素数量众多,这就会浪费不少内存

扩展阅读《为什么字符串是不可变的》。

9 超类和子类的构造函数

class Super {  String s;  public Super(String s){    this.s = s;  }}public class Sub extend Super{  int x = 200;  public Sub(String s){ // 编译错误  }  publicSub(){  // 编译错误    System.out.println("Sub");  }  public static void main(String[] args){    Sub s = new Sub();  }}复制代码

上述代码会有编译错误,因为没有实现 Super() 构造函数。Java 中,如果一个类没有定义构造函数,编译器将会插入一个默认的没有参数的构造函数。但是如果 Super 类已经有了一个构造函数 Super(String s),那么编译器就不会插入这个默认的无参数的构造函数。这就是上述代码的遇到的情况。

Sub 类的两个构造函数,一个有参数一个没有参数,都会调用 Super 类的无参数构造函数。因为编译器会尝试在 Sub 类的两个构造函数里插入 super(),由于 Super 类没有无参数构造函数,所以编译器就报错了。

解决这个问题,有三种方法:

给 Super 类添加一个 Super() 无参数构造函数

删掉 Super 类里的有参数的构造函数

在 Sub 类的构造函数里添加 super(value)

想了解更多详情,可以看《Constructors of Sub and Super Classes in Java?》。

10 用 "" 还是用构造函数

字符串可以通过两种途径来构造:

// 1. 使用双引号String x ="abd";// 2. 使用构造函数String y = new String("abc");复制代码

有什么区别呢?

下面的代码可以很快地告诉你区别:

String a ="abcd";String b ="abcd";System.out.println(a == b);  // TrueSystem.out.println(a.equals(b)); // True String c = new String("abcd");String d = new String("abcd");System.out.println(c == d);  // FalseSystem.out.println(c.equals(d)); // True复制代码

想了解这两种方式生成的字符串在内存中是如何存在的,可以看看《Create Java String Using ” ” or Constructor?

总结

这 10 个错误是我综合 GitHub 上的项目、StackOverflow 上的问答和 Google 搜索关键词的趋势而分析得出的。它们可能并不是真正的 10 个最多的错误,但还是挺普遍的。如果你有异议,可以给我留言。如果你能告诉我其他常见的错误,我会非常感谢你。当然除了注意这些容易放的错误,还是要不断的一直提升自己。在这里小编分享一份自己工作中提升自己的学习经验,希望能帮助到大家。

从事java十余年,现在把架构师必须具备的一些技术总结出来一套思维导图和录制了一些相关视频,分享给大家,供大家参考。

需要相关资料可以加群:810589193,点击链接加入群聊【Java架构学习交流群】:https://jq.qq.com/?_wv=1027&k=5deQUBl

我把它分为六个点

1. 高性能架构

1.1. 分布式架构思维

1.2. Zookeeper分布式环境指挥官

1.3. Nginx高并发分流进阶实战

1.4. ActiveMq消息中间件

1.5. RabbitMq消息中间件

1.6. Kafka百万级吞实战

1.7. Memcached进阶实战

1.8. Redis高性能缓存数据库

1.9. MongoDB进阶实战

1.10. 高性能缓存开发实战

1.11. Mysql高性能存储实战

1.12. FastDFS分布式文件存储实战

1.13. 高并发场景分布式解决方案实战

15535205-67349de963a2b10b.jpg

2. 微服务架构

2.1. 服务的前世今生

2.2. 基于分布式思想下的RPC解决方案

2.3. Dubbo应用及源码解读

2.4. SpringBoot

2.5. SpringCloud应用及源码解读

2.6. Docker虚拟化技术

15535205-4b52c60bf0bd55b2.jpg

3. 开源框架

3.1. spring5概述

3.2. Spring5 Framework体系结构

3.3. Spring5环境搭建

3.4. IOC源码解析

3.5. AOP源码解析

3.6. Spring MVC

3.7. Mybatis

15535205-a68f69a4240c080f.jpg

4. 架构师基础

4.1. JVM性能调优

4.2. Java程序性能优化

4.3. Tomcat

4.4. 并发编程进阶

4.5. Mysql

4.6. 高性能Netty框架

4.7. Linux基础与进阶

15535205-edf4afa2e7ac12b0.jpg

5. 团队协作开发

5.1. Git

5.2. Maven

5.3. Jenkins

5.4. Sonar

15535205-f2f7cd32b6792d97.jpg

6. B2C商城项目

6.1. 系统设计

6.2. 用户管理子系统

6.3. 商品管理子系统

6.4. 搜索子系统

6.5. 订单子系统

6.6. 支付系统

6.7. 分布式调度系统

6.8. 后台系统

15535205-143652e54f6ad5fd.jpg
15535205-342c8f579a7ccc8f.jpg

不是每个人都能成为高手,但是不努力,就算有再高的天分,也白痴一个!如果你想学习 Java 工程化、高性能及分布式、高性能、深入浅出。性能调优、Spring,MyBatis,Netty 源码分析和大数据等知识点可以来找我。

工作一到五年的程序员朋友面对目前的技术无从下手,感到很迷茫可以加群:810589193,点击链接加入群聊【Java架构学习交流群】:https://jq.qq.com/?_wv=1027&k=5deQUBl里面有阿里Java高级大牛直播讲解知识点,分享知识,课程内容都是各位老师多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

猜你喜欢

转载自blog.csdn.net/weixin_33795833/article/details/87070178