避坑!java程序员最经常犯的十个错误

1、将数组转换为ArrayList

将数组转换为ArrayList,程序员们通常会这么做:

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

如果你这么写的话Arrays.asList()将会返回一个ArrayList,但是这个ArrayList是Arrays这个类内部的一个静态类而非我们最经常使用的java.util.ArrayList这个包下面的ArrayList。Arrays这个类下面的ArrayList拥有set方法,get方法和contain方法,但是没有添加的方法,因此它的大小是固定的。如果我们想创建一个真正的ArrayList其操作方法如下:

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

ArrayList 的构造器可以接受集合类型的数据,集合类型的数据java.util.Arrays.ArrayList的父类。


2、检查数组是否包含值

程序员经常做的:

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

这个代码是有效的,但是没有必要先将一个列表转为一个集合。这么做会额外的消耗时间。我们可以这样简化代码:

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

或者:

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

第一种写法的可读性要高于第二种写法。


3、从循环内的列表中删除元素

思考以下在迭代期间删除元素的代码:

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()的源码,查看更多信息。


4.、Hashtable vs HashMap

在算法的规则中Hashtable 只是一种数据结构的名称。但是在java中数据结构的名称是HashMap。Hashtable 和HashMap最重要的区别是Hashtable是线程安全的。但是你不会经常使用Hashtable,你最经常使用的是HashMap。


5、使用原始集合类型

在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 ...

使用原始集合类型是十分危险的,因为元素集合类型会跳过泛类检查,这是十分不安全的。SetSet<?>, 和 Set<Object>之间存在着巨大的差异。


6、访问级别

许多程序员经常把类的属性设置为public 。通过直接引用,你可以很方便的获取到属性值,但是这是非常糟糕的一种设计。根据经验,我们应该外部访问者较低的权限。


7、ArrayList vs LinkedList

当程序员不知道 ArrayList 和 LinkedList之间的区别的时候,他们会经常使用ArrayList ,因为它更为熟悉。但是ArrayList 和 LinkedList之间却存在巨大的性能差异。简而言之,LinkedList的优势在于添加,删除和修改,而ArrayList 的优势在于查询。


8、Mutable vs Immutable

不可变对象拥有许多优点,例如:简单,安全等。但它要求每个不同的值都有一个单独的对象,对象太多可能会导致垃圾回收的消耗过高。mutable 和 immutable之间需要权衡。

通常,可变对象用于避免生成过多的中间对象。一个经典的例子是串联大量字符串。如果使用不可变字符串,则会生成许多可以立即进行垃圾回收的对象。这在CPU上浪费了时间和算力,使用可变对象是正确的解决方案。

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 static void main(String[] args){
Sub s = new Sub();

 如果你想上面这样写代码,编译将会报错,提示你默认继承的构造器没有被定义。在Java中,如果一个类没有定义构造函数,编译器将在默认情况下为该类插入一个默认的无参数构造函数。如果构造函数是在父类中定义的,在上面案例中,编译器不会插入默认的无参数构造函数。这是上述父类的情况。

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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小海海不怕困难

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值