java

Java开发人员最常犯错误整理

Java开发人员最常犯错误整理

本文共整理出12种常犯错误, 部分来自网上资源. 组织文字, 补充代码, 总结而出. 希望能够帮到你

一、把数组转成ArrayList

为了将数组转换为ArrayList,开发者经常会这样做:

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

使用 Arrays.asList() 方法可以得到一个ArrayList,但是得到这个 ArrayList其实是定义在Arrays类中的一个私有的静态内部类。这个类虽然和 java.util.ArrayList同名,但是并不是同一个类。 java.util.Arrays.ArrayList类中实现了 set(), get(), contains()等方法,但是并没有定义向其中增加元素的方法。也就是说通过 Arrays.asList() 得到的ArrayList的大小是固定的。

如果在开发过程中,想得到一个真正的ArrayList对象( java.util.ArrayList的实例),可以通过以下方式:

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

java.util.ArrayList中包含一个可以接受集合类型参数的构造函数。因为java.util.Arrays.ArrayList这个内部类继承了AbstractList类,所以,该类也是Collection的子类。


二、在循环中删除列表中的元素

在讨论这个问题之前,先考虑以下代码的输出结果

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中所有元素,但是结果却没有成功。原因是忽略了一个关键的问题:当一个元素被删除时,列表的大小缩小并且下标也会随之变化,所以当你想要在一个循环中用下标删除多个元素的时候,它并不会正常的生效

所以,正确的在遍历过程中删除元素的方法应该是使用Iterator:

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()方法之前调用。如果在循环过程中先调用 remove() ,再调用next(),就会导致异常ConcurrentModificationException。原因如上。

如果你用java8, 运用lambda表达式那么更简单了, 一句话的事~

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
list.removeIf(item -> item.equals("a"));

三、不能在foreach循环中修改list结构

在jdk1.5版以上的foreach 循环写法中,不能在循环代码中对正在循环的list的结构进行修改,即对list做 add、remove等操作,如果做了这些操作,必须 立即退出循环,否则会抛出异常。


四、可变与不可变

不可变对象有许多的优点,比如简单,安全等等。同时,也有人提出疑问:既然不可变有这么多好处,为什么不把所有类都搞成不可变的呢?

通常情况下,可变对象可以用来避免产生过多的中间对象。一个经典的实例就是连接大量的字符串,如果使用不可变的字符串,将会产生大量的需要进行垃圾回收的对象。这会浪费CPU大量的时间,使用可变对象才是正确的方案(比如 StringBuilder)。

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

五、""还是构造函数

如果你只需要创建一个字符串,你可以使用双引号的方式,如果你需要在堆中创建一个新的对象,你可以选择构造函数的方式。

String d = new String("abcd")时,因为字面值“abcd”已经是字符串类型,那么使用构造函数方式只会创建一个额外没有用处的对象。


六、比较

当我们使用==运算符的时候,我们实际上是在比较两个对象的引用,来看看他们是不是指向的同一个对象。举个例子,我们不能使用==运算符来比较两个字符串是否相等。我们应该使用。equals方法来比较两个对象,这个方法是所有类共有的,它继承自java.lang.Object。


七、混淆值传递和引用传递

这是一个不太容易发现的错误。因为,当你看代码的时候,你会十分确定这是一个引用传递,而它实际上却是一个值传递。Java这两者都会使用,所以你要理解你什么时候需要值传递,什么时候需要引用传递。当你要传递一个简单的数据类型到一个函数中,比如,char、int、float或者double,你是在传递一个值。这个意味着这种数据类型的被复制了一个拷贝,是这个拷贝被传递到了函数中。如果这个函数去修改这个值,仅仅是这个值的拷贝被修改了。这个函数结束以后,将会返回到控制调用函数去,这时候那个“真正的”值没有收到影响,没有任何改变被存储。

如果你想修改一个简单的数据类型,可以将这个数据类型定位一个返回值或者将它封装到一个对象中。

当你要传递一个java对象到一个函数中,比如,数组、向量或者是一个字符串,此时你传递的就是一个对象的引用。这里的字符串也是一个对象,而不是一个简单数据类型。这就意味着你要传递一个对象到一个函数,你就要传递这个对象的引用,而不能去复制它。任何对这个对象的成员变量的改变都会持久化,这种改变的好坏要取决于你是否是刻意而为之。

有一点要注意,如果字符串没有包含任何方法改变它的值的时候,你最好将它作为值来传递。


八、忘记java中索引是从0开始的

在java中数组的索引是从 0 开始的,这就是说第一个元素的索引必须是0


九、日志规范性

有时候logger.error不能完全地打印出网站的错误堆栈信息,只能打印这个错误是一个什么错误。
为什么?
看Logger.error源码

public void error(String msg, Throwable t);
public void error(String msg);

如果只传一个参数e进去,那么e就被认为是String类型(会自动调toString()方法把Exception转成String),而不是Exception类型。
如果想打印堆栈信息,那么必须传两个或以上参数,实际上就是为了调用public void error(String msg, Throwable t);
所以我们的写法可以是:

Logger.error(“xxx出错”,e); //第二个参数是e

而不是:

Logger.error(“xxx出错:”+e) logger.error(e) 或logger.error(e.getMessage);

十、魔鬼数字

在代码中使用魔鬼数字(没有具体含义的数字、字符串等)将会导致代码难以理解,应该将数字定义为名称有意义的常量。

错误示例如 :

public void addProduct(Product product) 
{ 
 // 魔鬼数字,无法理解3具体代表产品的什么状态 
 if(product.getProduct().getProductStatus() != 3) 
 { 
    //todo
 } 
} 

正确示例为:

/** 
*产品未激活状态 
*/ 
private static final int UNACTIVATED = 0; 
/** 
*产品已激活状态 
*/ 
private static final int ACTIVATED = 1; 
 
public void addProduct(Product product) 
{ 
 if(product.getProduct().getProductStatus() != ACTIVATED) 
 {
     //todo
 } 
}

十一、空指针异常

空指针异常是编码过程中最常见的异常,在使用一个对象的时候,如果对象可能为空,并且使用次对象可能会造成空指针异常,那么需要先判断对象是否为空,再使用这个对象。

在进行常量和变量的相等判断时,建议将常量定义为Java对象封装类型(如将int类型的常量定义为Integer类型),这样在比较时可以将常量放在左边,调用equals方法进行比较,可以省去不必要的判空。


十二、下标越界

访问数组、List等容器内的元素时,必须首先检查下标是否越界,杜绝下标越界异常的发生。

public class ArrayOver 
{ 
 public void checkArray(String name) 
 { 
 // 获取一个数组对象 
 String[] cIds = ContentService.queryByName(name); 
 if(null != cIds) 
 { 
 // 只是考虑到cids有可能为null的情况,但是cids完全有可能是个0长度的数组,因此cIds[0]有可能数组下标越界 
 String cid=cIds[0]; 
 cid.toCharArray(); 
 } 
 } 
} 

附:
阿里巴巴java开发手册v1.4.0.pdf

hope can help you!


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!
文章若有侵权请立即与我联系, 我将及时处理
微信扫一扫,向我赞赏

微信扫一扫,向我赞赏

微信扫一扫,向我赞赏

支付宝扫一扫,向我赞赏

回复

This is just a placeholder img.
Title - Artist
0:00