文章列出了Java开发者最常犯的是个错误。

1.将数组转换为ArrayList

为了将数组转换为ArrayList,开发者经常会这样做:

1
List<String> list = Arrays.asList(arr);

Arrays.asList()会返回一个ArrayList,但这个ArrayListArrays的私有静态类,不是java.util.ArrayListjava.util.Arrays.ArrayListset(), get(), contains()方法,但没有任何能增加元素的方法,所以它的大小是确定的。 为了创建一个真正的ArrayList,应该这样做:

1
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

ArrayList的构造函数能够接收一个Collection类型,而它也是java.util.Arrays.ArrayList的一个祖先类。

2.检查一个数组是否包含某个值

开发者经常这样做:

1
2
Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);

这个的代码可以工作,但没必要首先把数组转换为集合,把数组转换为集合需要额外的时间。可以这样做:

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

或者

1
2
3
4
5
for(String s: arr){
    if(s.equals(targetValue))
        return true;
}
return false;

第一个比第二个的可读性更好。

3.在循环里边删除列表的元素

思考下面的代码,该代码在循环里边删除元素

1
2
3
4
5
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (int i = 0; i < list.size(); i++) {
    list.remove(i);
}
System.out.println(list);

输出如下:

1
[b, d]

上面的方法有一个严重的问题。当一个元素被移除后,列表的大小减小了,索引也就变了。所以希望利用索引在一个循环里边删除多个元素是做不到的。 你可能知道利用迭代器在一个循环里边删除元素是正确的方法,并且知道Java的foreach循环很像一个迭代器,但事实上不是。思考下面的代码:

1
2
3
4
5
6
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
 
for (String s : list) {
    if (s.equals("a"))
        list.remove(s);
}

它将会抛出异常ConcurrentModificationException。 下面的代码是可以的:

1
2
3
4
5
6
7
8
9
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String s = iter.next();
 
    if (s.equals("a")) {
        iter.remove();
    }
}

.next()方法必须在调用.remove()方法之前调用。在foreach循环里边,编译器会先调用.remove(),再调用.next(),从而导致异常ConcurrentModificationException。你可能想知道ArrayList.iterator()的源代码。

4.HashTable vs HashMap

根据算法的约定,HashTable是这个数据结构的名字,但在Java里边,HashMap是这个数据结构的名字。Hashtable和 HashMap的一个关键性的不同是,HashTable是同步的,而HashMap不是。所以通常不需要HashTable,HashMap用的更多。 HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap Top 9 questions about Java Maps

5.使用原始集合类型

在Java里边,原始类型和无界通配符类型很容易混合在一起。以Set为例,Set是一个原始类型,而Set< ? >是一个无界通配符类型。 考虑下面使用原始类型List作为参数的代码:

1
2
3
4
5
6
7
8
public static void add(List list, Object o){
    list.add(o);
}
public static void main(String[] args){
    List<String> list = new ArrayList<String>();
    add(list, 10);
    String s = list.get(0);
}

上面的代码将会抛出异常:

1
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at ...

使用原始集合类型是很危险的,因为原始集合类型跳过了泛型类型检查,是不安全的。SetSet< ? >Set< Object >之间有很大差别。请参考Raw type vs. Unbounded wildcardType Erasure

6.访问级别

开发者经常使用Public作为类的修饰符,这样可以很简单的通过直接引用得到值,但这是一个非常糟糕的设计。根据经验,分配给成员的访问级别应尽可能的低。 public, default, protected, and private

7.ArrayList vs LinkedList

当开发者不知道ArrayList和LinkedList的区别的时候,通常会使用ArrayList,因为它看起来更熟悉。然而,两者之间有很大 的性能差异。简单地说,当有大量的插入/删除操作并且没有太多的随机访问操作的时候,应该使用LinkedList。如果对此不太了解,可参考ArrayList vs. LinkedList

8.可变与不可变

不可变对象有许多的优点,比如简单,安全等等。但是对于每一个不同的值都要有一个独立的对象,过多的对象导致垃圾回收的高消耗。当选择可变与不可变时应该有一个权衡。 通常情况下,可变对象可以用来避免产生过多的中间对象。一个经典的实例就是连接大量的字符串,如果使用不可变的字符串,将会产生大量的需要进行垃圾回收的对象。这会浪费CPU大量的时间,使用可变对象才是正确的方案(比如StringBuilder)。

1
2
3
4
String result="";
for(String s: arr){
    result = result + s;
}

在其它的一些情况下也是需要使用可变对象的,比如将可变对象作为参数传入方法可以使你不需要使用很多语句便可以得到多个结果。另外一个例子是排序和 过滤:当然,你可以写一个方法来接收原始的集合,并且返回一个排好序的集合,但是那样对于大的集合就太浪费了。(来自StackOverFlow的dasblinkenlight的答案)。 Why String is Immutable?

9.父类和子类的构造函数

因为没有定义父类的默认构造函数,在编译的时候会产生错误。在Java里边,如果一个类没有定义构造函数,编译器将会插入一个无参数的默认构造函数。如果在父类里边定义了一个构造函数,在此例中即Super(String s),编译器将不会插入默认的无参数构造函数。这就是上面示例中父类的情形。 子类的构造函数,不管是没有参数还有有参数,都会调用父类的无参构造函数。因为编译器试图把super()插入到子类的两个构造函数中。但是父类默认的构造函数未定义,编译器就会报出这个错误信息。 要修复这个问题,可以简单的通过1)在父类中添加一个Super()构造方法,就像这样:

1
2
3
public Super(){
    System.out.println("Super");
}

或者2)移除自定义的父类构造函数,或者3)在子类的构造函数中调用父类的super(value)。 Constructor of Super and Sub

10.”“还是构造函数?

有两种方式构造字符串:

1
2
3
4
//1. 利用双引号
String x = "abc";
//2. 利用构造函数
String y = new String("abc");

区别在哪? 下面的例子可以给出一个快速的答案:

1
2
3
4
5
6
7
8
9
String a = "abcd";
String b = "abcd";
System.out.println(a == b);  // True
System.out.println(a.equals(b)); // True
 
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d);  // False
System.out.println(c.equals(d)); // True

关于它们内存分配的更多细节,请参考Create Java String Using ”” or Constructor?

将来的工作

这个列表是我根据大量的GitHub上的开源项目、Stack Overflow上的问题和一些常见的Google搜索得到的。没有明显示的评估证明它们是准确的前10,但它们绝对是很常见的问题。如果您不同意任一部 分,请留下您的评论。如果您能提出其它一些常见的错误,我将会非常感激。

翻译自:Top 10 Mistakes Java Developers Make

http://www.cnblogs.com/liushaobo/p/4375493.html

Top 10 Mistakes Java Developers Make(转)的更多相关文章

  1. Top 10 Mistakes Java Developers Make--reference

    This list summarizes the top 10 mistakes that Java developers frequently make. #1. Convert Array to ...

  2. Yet Another 10 Common Mistakes Java Developers Make When Writing SQL (You Won’t BELIEVE the Last One)--reference

    (Sorry for that click-bait heading. Couldn’t resist ;-) ) We’re on a mission. To teach you SQL. But ...

  3. 100 high quality blogs from java developers

    This list collects 100 high quality blogs from Java developers from all over the world. Some of thes ...

  4. 2016年排名Top 100的Java类库——在分析了47,251个依赖之后得出的结论(16年文章)

    本文由HollisChuang 翻译自 The Top 100 Java Libraries in 2016 – After Analyzing 47,251 Dependencies . 原作者:H ...

  5. 2016 年排名 Top 100 的 Java 类库

    我们分析了GitHub中47,251个依赖,从中找出了排名前一百的Java类库,让我们看看谁在前面,谁在后面. 我们在漫长的周末的消遣方式就是浏览GitHub并且搜索流行的Java类库.我们决定把其中 ...

  6. Top 10 Books For Advanced Level Java Developers

    Java is one of the most popular programming language nowadays. There are plenty of books for beginne ...

  7. Watch out for these 10 common pitfalls of experienced Java developers & architects--转

    原文地址:http://zeroturnaround.com/rebellabs/watch-out-for-these-10-common-pitfalls-of-experienced-java- ...

  8. Top 10 Questions about Java Exceptions--reference

    reference from:http://www.programcreek.com/2013/10/top-10-questions-about-java-exceptions/ This arti ...

  9. Top 10 Methods for Java Arrays

    作者:X Wang 出处:http://www.programcreek.com/2013/09/top-10-methods-for-java-arrays/ 转载文章,转载请注明作者和出处 The ...

随机推荐

  1. cookie的path和domain參数实例解析

    一句话概括两个參数含义各为: path表示cookie所在的文件夹 domain表示的是cookie所在的域,默觉得请求的地址 首先改动我们的 hosts 文件 我本机内网ip 192.168.1.1 ...

  2. 使用ServletFileUpload实现上传

    1.首先我们应该为上传的文件建一个存放的位置,一般位置分为暂时和真是目录,那我们就须要获取这俩个目录的绝对路径,在servlet中我们能够这样做 ServletContext application ...

  3. iOS开发经常使用宏定义

    iOS开发经常使用宏定义 iOS开发中经常须要获取屏幕宽度高度,为view设置颜色,为imgagView设置图片等,我们都可定义一些宏,随时都可拿来使用,方便开发 <span style=&qu ...

  4. uva 11732 - strcmp() Anyone? 不错的Trie题

    题解:http://blog.csdn.net/u013480600/article/details/23122503 我的代码一直TLE,,,看了人家的之后,认为1.链式前向星比較好,2.*dept ...

  5. Linux下经常使用的shell命令记录

    硬件篇 CPU相关 lscpu #查看的是cpu的统计信息. cat /proc/cpuinfo #查看CPU信息具体信息,如每一个CPU的型号,主频等 内存相关 free -m #概要查看内存情况 ...

  6. alv行可编辑时带出描述

    ALV显示可以编辑的状态下可以带出描述信息等,比如维护表程序输入公司代码时需要带出公司代码的描述,这时就需要通过下面事件来触发 定义一个类: CLASS lcl_event_receiver DEFI ...

  7. OCA读书笔记(8) - 管理用户安全

    创建用户:create user +用户 default tablespace + 表空间名 identified + 验证方式 SQL> create user easthome profil ...

  8. 向大家推荐个android的游戏引擎——cocos2d-x

    最近发现单单用android自带的功能函数来编写游戏,往往有很大的局限性,即耗时长,调试繁琐,没有一定的框架.所以博主发现了游戏引擎这个好东西,游戏引擎所拥有的架构和功能函数,使得游戏的编写更加得心应 ...

  9. 嵌入式linux多进程编程

    嵌入式linux多进程编程 在主程序显示文本菜单.提供例如以下服务.要求每一个服务都通过生成子进程来提供. 服务包含:日历信息显示,日期信息显示,推断闰年服务,文件复制功能,数字排序功能.退出功能. ...

  10. WM_ERASEBKGND官方解释(翻译),以及Delphi里所有的使用情况(就是绘制窗口控件背景色,并阻止进一步传递消息)

    #define WM_ERASEBKGND                   0x0014 Parameters wParam A handle to the device context. //  ...