摘要:在Java中,有些事物若是不了解的话,很容易就会用错,如数组转换为数组列表、元素删除、Hashtable和HashMap、ArrayList和LinkedList、Super和Sub构造函数等,若是这些对你来讲是陌生的,你能够在本文中了解它们。java
本文总结了Java开发者常常会犯的前十种错误列表。算法
将数组转换为数组列表,开发者常常会这样作:数组
List<String> list = Arrays.asList(arr);
Arrays.asList()将返回一个数组内部是私有静态类的ArrayList,这不是java.util.ArrayList类,java.util.Arrays.ArrayList类有set()、 get()、 contains()方法,可是没有任何加元素的方法,所以它的大小是固定的。你应该这么作来建立一个真正的数组:安全
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));
ArrayList的构造函数可以接受一个集合类型,这也是java.util.Arrays.ArrayList的超级类型。数据结构
开发者常常这么作:函数
Set<String> set = new HashSet<String>(Arrays.asList(arr)); return set.contains(targetValue);
代码能够工做,可是没有必要首先转换列表到Set,转换一个列表到一个Set须要额外的时间。所以你能够把它简化为:性能
Arrays.asList(arr).contains(targetValue);
或ui
for(String s: arr){ if(s.equals(targetValue)) return true; } return false;
第一个比第二个更具可读性spa
考虑下面删除元素的代码在迭代中的结果:设计
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);
输出是:
[b, d]
该方法有一个严重的问题,当一个元素被删除时,列表收缩的大小以及指针改变了。因此想要在循环内利用指针删除多个元素是没法正常进行的。
这种状况下使用迭代器才是正确的方法,foreach循环在Java中的工做像是一个迭代器,但实际上并非,考虑下面的代码:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d")); for (String s : list) { if (s.equals("a")) list.remove(s); }
它会报出ConcurrentModificationException异常。
相反下面这个就能够正常工做。
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循环中,编译器将在删除元素操做以后调用.next(),这也是致使ConcurrentModificationException异常的缘由,你能够查看ArrayList.iterator()的源代码。
根据算法的常规,Hashtable是对数据结构的称呼。可是在Java中,数据结构的名称是HashMap。Hashtable和HashMap关键不一样之一是Hashtable是同步的。
关于这一点可查看如下两个连接:
HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap
在Java中,原始类型和无限制的通配符类型很容易被混淆。以Set为例,Set是原始类型,而Set(?)则是无限制的通配符类型。
考虑下面的代码,以一个原始类型List做为参数:
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); }
该代码会抛出一个异常:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at ...
使用原始类型集合是危险的,由于原始类型集合跳过了泛型类型检查,也不安全。Set、Set<?>和Set
Raw type vs. Unbounded wildcard和Type Erasure。
开发者常常对类域使用public,这很容易经过直接引用得到域值,但这是一个很是糟糕的设计。根据经验来讲是给予成员的访问级别越低越好。
详细状况可点击查看Java中成员访问级别:public、protected、private
若是你不知道ArrayList和LinkedList之间的区别时,你可能会常常的选用ArrayList,由于它看起来看熟悉。然而它们之间有巨大的性能不一样。简单的来讲,若是有大量的添加/删除操做,而且没有不少的随机存取操做时,LinkedList应该是你的首选。若是您对此不是很了解的话,点此此处查看更多关于它们性能的信息。
Immutable对象有不少优点,好比简单、安全等等。但它要求每个不一样的值都须要有一个不一样的对象,而太多的对象可能会致使垃圾收集的高成本。因此对Mutable和Immutable的选择应该有一个平衡点。
通常来讲,Mutable对象用于避免产生过多的中间对象,经典的例子是链接大量的字符串数。若是你使用Immutable字符串,那么会产生不少符合垃圾收集条件的对象。这对CPU是浪费时间和精力的,当其可使用Mutable对象做为正确的解决方案。(如StringBuilder)
String result=""; for(String s: arr){ result = result + s; }
这里还有一些其余Mutable对象可取的状况。例如mutable对象传递到方法中容许你在不跳过太多语法的状况下收集多个结果。另外一个例子是排序和过滤,你能够构建一个带有原有集合的方法,并返回一个已排序的,不过这对大的集合来讲会形成更大的浪费。
推荐阅读:为何字符串是Immutable?
编译报错
这个编译错误是由于默认的Super构造函数是未定义的。在Java中,若是一个类没有定义一个构造函数,编译器会默认的为类插入一个无参数构造函数。若是一个构造函数是在Super类中定义的,这种状况下Super(String s),编译器不会插入默认的无参数构造函数。
另外一方面,Sub类的构造函数,不管带不带有参数,都会调用无参数的Super构造函数。
编译器在Sub类中试图将Super()插入到两个构造函数中,可是Super默认的构造函数是没有定义的,编译器才会报错。如何解决这一问题?你只需在Super类中添加一个Super()构造函数,以下所示:
public Super(){ System.out.println("Super"); }
或移除自定义的Super构造函数,又或者在Sub函数中添加super(value)。
这方面想了解更多的能够点击此处查看。
字符串能够经过两种方式建立:
//1. use double quotes String x = "abc"; //2. use constructor String y = new String("abc");
它们之间有何不一样?下面的例子能够给出答案:
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
关于它们如何在内存中分布的更多细节能够查看《使用””或构造函数建立Java字符串》。
推荐阅读:
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