java开发者常犯的问题
该文总结了排名前10位的错误,这些都是Java开发人员经常犯的。
1.把数组转成ArrayList
把数组转成ArrayList,开发者通常这样做:
|
|
Arrays.asList()将会返回一个ArrayList 它是一个private static class在Arrays中,并非java.util.ArrayList类。java.util.Arrays.ArrayList类有set(), get(), contains()等方法,但是没有任何添加元素的方法,所以它的大小是固定的。看下面这个例子:
|
|
为了创建一个实际的ArrayList,你应该这样做:
|
|
ArrayList的构造函数可以接受一个集合类型,这包括了超类是java.util.Arrays.ArrayList的类。
2.检查一个集合中是否有一个值
开发者通常这样做:
|
|
这代码可以达到效果,但是在这里list根本没有必要转换成set。转换list成为set也是要花费时间的。使用下面代码就可以了:
|
|
或者:
|
|
第一个可读性比第二个好很多。
3.从list中循环删除元素
考虑一下下面的代码,在迭代过程中删除元素:
|
|
该方法存在一个严重的问题。当一个元素被移除时,该列表的大小减小,而index数的却是变化的。所以,如果你想通过使用索引来删除一个循环中的多个元素,那将是无法正常工作。
你可能知道,使用迭代器是正确的方式来删除循环中的元素,你知道foreach循环中的Java就像一个迭代器,但实际上它不是。考虑下面的代码:
|
|
它会抛出ConcurrentModificationException异常。
使用下面方法可以解决:
|
|
.next() 必须在 .remove()之前调用。在foreach循环中,编译器调用.next() 在.remove()元素之后,这引起了ConcurrentModificationException异常。你最好看看 ArrayList.iterator() 源码深度理解,这将有助于你的理解。
4. Hashtable vs HashMap
通常,在算法中Hashtable是一种数据结构的名称。但是在java中这种数据结构的名称是HashMap。最主要的一个不同是Hashtable是同步的。所以很多时候你是不需要使用Hashtable的,通常使用HashMap。
5. 在集合中使用原始类型
在java中,原始类型和无界通配符类型很容易混合在一起。用set来举个例子,Set是原始类型,当设置Set<?>
是无界通配符类型的时候。
考虑下面的代码,它使用原始类型列表作为参数:
|
|
此代码将抛出一个异常:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at ...
使用原始类型的集合是很危险的,因为在原始类型的集合中跳过了泛型类型的检查,这样是不是安全的。对于Set
, Set<?>
, 和 Set<Object>
是有很多不同的。Set vs. Set<?>和Java Type Erasure Mechanism
6. 访问级别
很多时候开发人员会使用public的字段级别。这很容易通过引用来获得字段的值,但是这是一个很糟糕的设计。经验法则是会给成员尽可能低的访问级别。
Java 访问级别: public, protected, private
7. rrayList vs. LinkedList
当开发者不知道ArrayList 和 LinkedList的区别的时候,他们通常使用ArrayList,因为ArrayList看起来更加熟悉。然而,这直接将是一个很严重的性能问题。通常来说,LinkedList应该是首选的情况是,如果有大量的添加/删除操作和没有大量的随机访问操作。如果这对你有用的话,访问ArrayList vs. LinkedList获得有关他们的性能的详细信息。
8. 可变与不可变(Mutable vs. Immutable)
不可改变的对象有许多优点,例如简单性,安全性等,但它需要对每个不同的值穿件一个单独的对象,对象太多可能导致无用GC,成本过高。可变和不可变之间进行选择时应该有一个平衡点。
在一般情况下,使用可变对象,以避免产生过多的中间对象。一个典型的例子是连接大量的字符串。如果您使用的是不可变的字符串,你会产生很多有资格立即垃圾回收的对象。这浪费时间和精力在CPU上,使用可变对象来解决这个问题(如StringBuilder)。
|
|
还有,可变对象在一些情况也是是可取的。例如通过可变对象的方法,可以让你收集多个结果,而无需通过太多的语法钻圈。另一个例子是排序和筛选:当然,你可以做一个方法,它传入原来的集合,返回一个新的有序的集合,但在大集合中会变得极其浪费空间。(来自dasblinkenlight在Stack Overflow的回答)
9. 超类和子类的构造函数
发生这种编译错误,是因为默认的超级构造函数是不确定的。在Java中,如果一个类没有定义构造函数,编译器会插入一个默认的无参数构造函数。如果构造函数是在超类中定义的,在Super(String s)情况下,编译器将不插入默认的无参数构造函数。Super 类就是以上的情况。
Sub类中的构造函数,无论是带参数还是不带参数,都需要调用 Super 类的无参构造函数。但Super类的默认构造函数是没有定义的,使用编译器报告了这个错误信息。
要解决此问题,只需在 Super中增加一个Super()构造函数,如下:
|
|
或删除自定义的Super构造函数,或增加 super(value)在sub类的构造方法中。
10. “” 还是构造函数?
String 能通过两种方式创建:
|
|
那他们有什么不同?
下面的例子可以提供一个快速的答案:
|
|
关于它们是如何分配的内存的详细信息,请看java中创建字符串使用”” 或者 构造函数?
延伸阅读
这份名单是根据我对大量在GitHub上、Stack Overflow问题和流行的Google查询分析。没有评估,难以证明他们恰恰是前10名,但绝他们对是很常见的。请留下您的评论,如果你有不同的意见。如果你能指出一些错误是十分常见的,我将会十分感激。
- Constructors of Sub and Super Classes in Java?
- How Developers Sort in Java?
- How to Convert Array to ArrayList in Java?
- Java Type Erasure Mechanism