转载

总结软件开发过程中最常犯的10个问题

总结软件开发过程中最常犯的10个问题

先总结一下Top10吧

  1. Array转ArrayList
  2. 判断一个数组是否包含某个值
  3. 在循环内部删除List中的一个元素
  4. HashTable与HashMap
  5. 使用集合原始类型(raw type)
  6. 访问级别
  7. ArrayList和LinkedList
  8. 可变与不可变
  9. 父类和子类的构造方法
  10. “”还是构造方法

这个列表总结了10个Java开发人员最常犯的错误。

1、Array转ArrayList

当需要把 Array 转成 ArrayList 的时候,开发人员经常这样做:

List list = Arrays.asList(arr);

Arrays.asList() 会返回一个 ArrayList ,但是要特别注意,这个 ArrayListArrays 类的静态内部类,并不是 java.util.ArrayList 类。

java.util.Arrays.ArrayList 类实现了 set()get()contains() 方法,但是并没有实现增加元素的方法(事实上是可以调用add方法,但是没有具体实现,仅仅抛出 UnsupportedOperationException 异常),因此它的大小也是固定不变的。为了创建一个真正的 java.util.ArrayList ,你应该这样做:

ArrayList arrayList = new ArrayList(Arrays.asList(arr));

ArrayList 的构造方法可以接收一个 Collection 类型,而 java.util.Arrays.ArrayList 已经实现了该接口。

2、判断一个数组是否包含某个值

开发人员经常这样做:

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

以上代码可以正常工作,但是没有必要将其转换成 set 集合,将一个 List 转成 Set 需要额外的时间,其实我们可以简单的使用如下方法即可:

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

或者

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

第一种方法可读性更强。

3、在循环内部删除List中的一个元素

考虑如下代码,在迭代期间删除元素:

ArrayList list = new ArrayList(Arrays.asList("a", "b", "c","d"));
for (int i = 0; i
list.remove(i);
}
System.out.println(list);

结果打印: [b, d]

在上面这个方法中有一系列的问题,当一个元素被删除的时候, list 大小减小,然后原先索引指向了其它元素。所以如果你想在循环里通过索引来删除多个元素,将不会正确工作。

你也许知道使用迭代器是在循环里删除元素的正确方式,或许你也知道 foreach 循环跟迭代器很类似,但事实情况却不是这样,如下代码:

ArrayList list = new ArrayList(Arrays.asList("a", "b", "c","d"));
for (String s : list) {
    if (s.equals("a"))
    list.remove(s);
}

将抛出 ConcurrentModificationException 异常。

然而接下来的代码却是OK的:

ArrayList list = new ArrayList(Arrays.asList("a", "b", "c","d"));
Iterator iter = list.iterator();
while(iter.hasNext()) {
    String s = iter.next();
    if (s.equals("a")) {
        iter.remove();
    }
}

next() 方法需要在 remove() 方法之前被调用,在 foreach 循环里,编译器会在删除元素操作化调用 next 方法,这导致了 ConcurrentModificationException 异常。更多详细信息,可以查看 ArrayList.iterator() 的源码。

4、HashTable与HashMap

从算法的角度来讲, HashTable 是一种数据结构名称。但是在 Java 中,这种数据结构叫做 HashMap

HashTableHashMap 的一个主要的区别是 HashTable 是同步的,所以,通常来说,你会使用 HashMap ,而不是 Hashtable

5、使用集合原始类型(raw type)

Java 中,原始类型( raw type )和无界通配符类型很容易让人混淆。举个 Set 的例子, Set 是原始类型,而 Set 是无界通配符类型。

请看如下代码, add 方法使用了一个原始类型的 List 作为入参:

public static void add(List list, Object o){
    list.add(o);
}
public static void main(String[] args){
    List list = new ArrayList();
    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 , SetSet 这三个有很大的不同。

6、访问级别

开发人员经常使用 public 修饰类字段,虽然这很容易让别人直接通过引用获取该字段的值,但这是一个不好的设计。根据经验,应该尽可能的降低成员属性的访问级别。

7、ArrayList和LinkedList

为什么开发人员经常使用 ArrayListLinkedList ,却不知道他们之间的区别,因为它们看起来很像。然而它们之间有着巨大的性能差异。简单的说,如果有大量的增加删除操作并且没有很多的随机访问元素的操作,应该首选 LinkedList

8、可变与不可变

不可变对象有很多优点,如简单、安全等。但是对于每个不同的值都需要一个单独的对象,太多的对象会引起大量垃圾回收,因此在选择可变与不可变的时候,需要有一个平衡。

通常,可变对象用于避免产生大量的中间对象,一个经典的例子是大量字符串的拼接。如果你使用一个不可变对象,将会马上产生大量符合垃圾回收标准的对象,这浪费了CPU大量的时间和精力。使用可变对象是正确的解决方案( StringBuilder );

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

另外,在有些其它情况下也是需要使用可变对象。例如往一个方法传入一个可变对象,然后收集多种结果,而不需要写太多的语法。另一个例子是排序和过滤:当然,你可以写一个方法来接收原始的集合,并且返回一个排好序的集合,但是那样对于大的集合就太浪费了。

9、父类和子类的构造方法

class Super{
    string s;
    public Super(String s){
        this.s=s;
    }
}
public class Sub extends Super{
    int x=200;
    public Sub(String s){
    }
    public Sub(){
        System.out.println("Sub");
    }
    public static void main(String[] args){
        Sub s=new Sub();
    }
}

之所以出现这个编译错误,是因为父类的默认构造方法未定义。在Java中,如果一个类没有定义构造方法,编译器会默认插入一个无参数的构造方法;但是如果一个构造方法在父类中已定义,在这种情况,编译器是不会自动插入一个默认的无参构造方法,这正是以上 demo 的情况;

对于子类来说,不管是无参构造方法还是有参构造方法,都会默认调用父类的无参构造方法;当编译器尝试在子类中往这两个构造方法插入 super( )方法时,因为父类没有一个默认的无参构造方法,所以编译器报错;

要修复这个错误,很简单:

  1. 在父类手动定义一个无参构造方法:
public Super(){
    System.out.println("Super");
}
super(value)

10、“”还是构造方法

有两种创建字符串的方式:

String x = "abc";
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的工程师朋友们加入Java高级架构进阶Qqun:963944895

群内有技术大咖指点难题,还提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)

比你优秀的对手在学习,你的仇人在磨刀,你的闺蜜在减肥,隔壁老王在练腰, 我们必须不断学习,否则我们将被学习者超越!

趁年轻,使劲拼,给未来的自己一个交代!

原文  https://segmentfault.com/a/1190000019702370
正文到此结束
Loading...