Code前端首页关于Code前端联系我们

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

terry 2年前 (2023-09-25) 阅读数 48 #后端开发
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开发最常犯的10个错误,打死都不要犯!

出现这个编译错误的原因是因为父类的默认构造函数该方法未定义。在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前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门