Java开发中最常见的10个错误,千万别犯!

此列表总结了 Java 开发人员最常犯的 10 个错误。
Array 转 ArrayList
当需要将 Array 转为 ArrayList 时,开发者经常这样做:
List<String> list = Arrays.asList(arr);
Arrays.asList() 会返回一个 ArrayList,但要特别注意的是,这个 ArrayList 是静态的by Arrays Class 内部类不是 java.util.ArrayList 类。 Java.util.Arrays.ArrayList类实现了set()、get()、contains()方法,但是没有实现添加元素的方法(其实可以调用add方法,但是没有具体说明)实现,只是抛出 UnsupportedOperationException 异常),所以大小也是固定的。要创建一个真正的java.util.ArrayList,你应该这样做:
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集合。将列表转换为集合需要额外的时间。其实,我们可以简单地使用以下方法:
Arrays.asList(arr).contains(targetValue);
或
for(String s: arr){
if(s.equals(targetValue))
return true;
}
return false;
第一种方法更具可读性。
在循环内删除List中的一个元素
考虑下面的代码,在迭代过程中删除元素:
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 循环与迭代器非常相似,但事实并非如此。以下代码:
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 和 HashMap
从算法的角度来看,HashTable 是一种数据结构的名称。但在Java中这种数据结构被称为HashMap。 HashTable 和 HashMap 之间的主要区别之一是 HashTable 是同步的,因此通常您希望使用 HashMap 而不是 Hashtable。
使用集合原语
在Java中,原语和无界通配符很容易混淆。以Set为例,Set是原始类型,Set是无界通配符类型。
参见以下代码。 Add 方法使用原始类型 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. lang.Integer 无法在 ...
上转换为 java.lang.String
使用原始类型集合非常危险,因为它会跳过泛型类型检查并且不安全。此外,Set、Set 和 Set 也有很大不同。有关详细信息,请参阅:类型删除和原始类型与原始类型未绑定的通配符。
访问级别
开发人员经常使用 public 来更改类字段。虽然其他人很容易通过引用直接获取该字段的值,但这是一个糟糕的设计。根据经验,应尽可能减少对成员属性的访问级别。
ArrayList 和 LinkedList
为什么开发人员经常使用 ArrayList 和 LinkedList,而不知道它们之间的区别,因为它们看起来相同。然而,它们之间存在巨大的性能差异。简单来说,如果有大量的增删改查,而不是大量的随机访问元素,那么应该首选LinkedList。
可能与不可能不可变
不可变对象有很多优点,比如简单、安全等。但是每个不同的值都需要一个单独的对象。太多的对象会导致大量的垃圾收集,因此在可变和不可变之间进行选择时必须取得平衡。
通常,可变对象用于避免生成大量中间对象。一个典型的例子就是大量字符串的拼接。如果使用不可变对象,就会立即生成大量符合垃圾回收标准的对象,浪费大量CPU时间和精力。使用可变对象是正确的解决方案(StringBuilder);
String result="";
for(String s: arr){
result = result + s;
}
此外,在其他一些情况下也需要可变对象。例如,将可变对象传递给方法,然后收集多个结果,而无需编写太多语法。另一个例子是排序和过滤:当然,您可以编写一个方法来获取原始集合并返回排序后的集合,但这对于大型集合来说太浪费了。更多阅读:为什么String是不可变的?
为什么我们需要可变类?
父类和子类的构造方法

出现这个编译错误的原因是因为父类的默认构造函数该方法未定义。在Java中,如果一个类没有定义构造函数,编译器会默认插入一个无参构造函数;但如果父类中定义了构造函数,在这种情况下,编译器不会自动插入默认构造函数。无参构造函数,正是上面demo中的情况;
对于子类来说,无论是无参构造函数还是有参构造函数,都会默认调用父类的无参构造函数;当编译器试图在子类的这两个构造函数中插入 super() 方法时,编译器会报错,因为父类没有默认的无参构造函数;
要修复此错误非常简单:
1。在父类中手动定义一个不带参数的构造函数:
public Super(){
System.out.println("Super");
}
创建字符串有两种方法:
//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
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。