JavaRush /Java 博客 /Random-ZH /Java 开发人员常犯的 10 个错误
theGrass
第 24 级
Саратов

Java 开发人员常犯的 10 个错误

已在 Random-ZH 群组中发布
Java 开发人员常犯的 10 个错误 - 1
此列表包括Java开发人员经常犯的 10 个错误。
  1. 将数组转换为ArrayList

    要将数组转换为ArrayList,开发人员经常使用以下方法:

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

    Arrays.asList()会返回一个类对象,它是该类的ArrayList内部私有静态类,并且这不是一个类,该类包含方法,,,但不包含任何添加元素的方法,其大小是固定的。要创建一个真正的,请执行以下操作:(private static class)Arraysjava.util.ArrayList.java.util.Arrays.ArrayListset()get()contains()java.util.ArrayList

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

    类构造函数java.util.ArrayList可以将所有实现接口的对象作为参数Collection,该接口的实现由类继承。java.util.Arrays.ArrayList

    (私有静态类 ArrayList<E> 扩展 AbstractList<E> 实现 RandomAccess、java.io.Serializable)。

  2. 检查数组中的特定值。

    开发人员经常这样做:

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

    该代码可以工作,但无需将其转换ListSet. 转换为Set将需要额外的时间。其实一切都很简单:

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

    或者

    for(String s: arr){
    	if(s.equals(targetValue))
    		return true;
    }
    return false;

    第一种方法要短得多。

  3. 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]

    事实证明这是一个严重的错误。当删除元素时,大小List会减小并且元素索引会发生变化。

    因此,如果您想使用索引删除循环中的多个元素,请避免使用此方法。

    您可能知道使用迭代器是删除循环中元素的正确解决方案,并且您知道样式循环的for-each工作方式类似于迭代器,但事实并非如此。

    考虑以下代码:

    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()

    在循环风格中for-each,编译器将调用该方法remove(),只有在那时next(),才会引发错误ConcurrentModificationException。你可以看一下代码ArrayList.iterator()

  4. Hashtable反对HashMap

    由于相应的实现,Hashtable是数据结构的名称。

    但在Java中,数据结构的名称是HashMapHashtable和之间的主要区别之一HashMap是它是Hashtable同步的,因此不应该Hashtable在 where 中使用HashMap

    HashMap 对比 树状图对比 哈希表对比 LinkedHashMap

    关于地图界面的 10 个基本问题

  5. 使用没有内容限制的集合

    В Java часто путают коллекции без ограничений по содержимому, и коллекции с маской по типу содержимого. К примеру, для множеств - Set это коллекция без ограничений по содержимому, а Set<?> — коллекция у которой все-таки есть ограничения, но эти ограничения ничего на самом деле не ограничивают. Рассмотрим следующий code, где 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);
    }

    Данный code выбросит исключение:

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

    Использование коллекций без ограничений по содержимому очень опасно, потому что вы не можете быть уверенными в том что там лежит внутри. А для того чтобы понять всю глубину разницы между Set, Set<?> и Set<Object> — почитайте вот эту и эту ссылки.

  6. Уровень доступа

    Очень часто для полей класса разработчики используют модификатор доступа public. Так проще получить meaning поля напрямую. Правильнее использовать How можно более ограниченный доступ к членам класса.

    public, default, protected, and private.

  7. ArrayList против LinkedList

    Когда разработчики не знают чем отличается ArrayList от LinkedList, они используют первый в силу его большей известности. Однако, есть огромная разница в производительности между ними. На самом деле, выбор между ними должен быть продиктован их внутренними особенностями — ArrayList позволяет быстро производить доступ к произвольному элементу массива, а LinkedList — быстро добавлять/удалять элементы в массиве. Почитайте статью по ссылке ArrayList vs. LinkedList чтобы понять причины их разной производительности.

  8. Mutable (Изменяемый) против Immutable (Неизменяемый)

    Неизменяемый an object имеет много преимуществ: простота, безопасность и т.д. Но он требует отдельного an object для каждого нового значения, и за слишком большое количество an objectов придется заплатить понижением производительности. Должен быть баланс при выборе между изменяемым и неизменяемым обьектом.

    В основном чтобы избежать подготовки промежуточных an objectов используется изменяемый an object. Один из классических примеров это конкатенация большого количества строк. Если вы используете неизменяемый an object типа String, то вы создаете много an objectов, которые сразу попадут в сборщик мусора. Это тратит время и энергию процессора, поэтому правильное решение это использование изменяемых обьектов (например StringBuilder).

    String result="";
    for(String s: arr){
    	result = result + s;
    }

    Еще один пример использования изменяемых an objectов — передача такого an object в метод. Это позволит вернуть результат в нем же, без создания лишних an objectов. При работе с an objectми большого размера, к примеру коллекциями, передача коллекции в метод для сортировки и возвращение результата в другой коллекции приводит к совершенно излишнему расходу ресурсов. (Ответ пользователя dasblinkenlight на Stack Overflow).

    Почему an object класса String неизменяем?

  9. Конструкторы классов Super и Sub

    Java 开发人员经常犯的 10 个错误 - 2

    这个编译错误是由于祖先类没有定义默认构造函数导致的。在Java中,除非您自己指定类构造函数,否则编译器将创建一个不需要参数的默认构造函数。如果构造函数在类中描述SuperSuper(String s){},则编译器本身不会添加任何内容。这就是我们在示例中看到的。

    类的构造函数Sub,无论是哪一个,都会调用该类的默认构造函数Super,因为没有指定其他内容。由于类中没有默认构造函数Super,这将导致编译错误。

    解决这个问题的第一个方法是给类添加一个默认构造函数Super

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

    第二个选项是从类中删除我们描述的构造函数Super,以便编译器创建默认构造函数。

    最后一个选项是添加super(value)对类构造函数的调用Sub,以便调用现有的类构造函数而不是默认构造函数Super

    Super 和 Sub 的构造函数

  10. “”还是构造函数?

    创建字符串有两种方法:

    //1. использовать двойные кавычки
    String x = "abc";

    //2. использовать конструктор
    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 字符串?

未来的计划。 该列表基于我对 GitHub 上的大量开源项目、Stack Overflow 上的问题以及 Google 上的热门查询的分析。我不想证明它们真的是十大最严重的错误之一,但它们实际上很常见。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION