Java十大低级错误
1、 不能用“==”比较两个字符串内容相等。
2、 对list做foreach循环时,循环代码中不能修改list的结构。
java foreach只能用于只读的情况.如果需要删除操作,请用迭代器或者直接遍历List.
3、 空指针异常。
4、 数组下标越界。
// 获取一个数组对象
String[] cIds = ContentService.queryByName(name);
if(null != cIds)
{
// 只是考虑到cids有可能为null的情况,但是cids完全有可能是个0长度的数组,因此cIds[0]有可能数组下标越界
5、 将字符串转换为数字时没有捕获NumberFormatException异常。
6、 对文件、IO、数据库等资源进行操作后没有及时、正确进行释放。
7、 循环体编码时不考虑性能,循环体中包含不需要的重复逻辑。
8、 数据类没有重载toString()方法。
9、 嵌套使用try-catch,或者try-catch后面没有必要的finally操作。
10、 equals操作时没有将常量放在equals操作符的左边。
java低级错误
Calendar的错误使用
Calendar从星期日开始到星期六为一个周期,数字表示依次为:1,2,3……7;MONTH的表示是从数字0开始,所以月份应该是该数字+1。所以我们在使用的时候一定要仔细的阅读API文档,避免类似的陷阱。
序列化类的多版本问题
Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。当实现java.io.Serializable接口的实体(类)没有显式地定义serialVersionUID时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的 class才会生成相同的serialVersionUID
---解决方法:可以在程序一生成一个
serialVersionUID属性,这样运行
就不会出错。
正确定义hashcode
解读
类A覆写了hashCode方法,并采用A的属性value来生成A的hashCode。使用HashSet作为集合对象,把类A的一个对象a加入到HashSet中后,如果对象a的属性value发生了变化,则a的hashCode()方法返回的值也就发生变化,则无法将对象a从HashSet中删除。
预防措施如下:
1、如果覆写了equals方法,必须同时覆写hashCode方法。
2、equals方法和hashCode方法应避免使用易变的属性,避免该对象的hashCode频繁变化。
3、当对象被加入到HashSet(或作为Key加入到HashMap)后,应该避免该对象的hashCode发生变化。
4.正确理解Java的浅clone和深clone
浅clone和深clone都是clone,它们本质区别是对象内部的成员属性(非原生类型属性,如int等)在clone时是否处理为引用。如果仍然保留为引用,则称为浅clone,反之称为深clone。浅clone方式得到clone对象即可,深clone方式在得到clone对象后,还需要对引用的成员属性进行“clone”处理。使用Vector等容器的clone方法一定要注意:对于不变属性可以直接拷贝,对于可变属性需要手动写方法实现深层拷贝防止引用调用。深拷贝要深到什么程度,答案就是一直深到你想要的程度,包括深到不能再深的程度。
为什么定义equals方法的同时也要定义hashCode方法
equals方法用于实现对象之间逻辑上是否相等的判断,而不是判断两个引用是否指向同一个对象,hashCode用于返回对象的哈希码(也有翻译成散列码的),逻辑上相等(equals比较相等)的两个不同对象它们返回的hashCode值肯定不相等。 Java规范中规定:如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果,所以定义equals的同时一定要定义hashCode,并且要保证equals比较相等的对象,hashCode返回值也必须相同。
如果定义了equals方法,没有定义hashCode,两个对象的hashCode却不同,Map查找对象的时候会首先比较hashCode值,然后再使用equals比较,造成Map无法找到期望的对象。
大小写转换的正确处理
String提供有大小写转换方法:String.toUpperCase()和String.toLowerCase(),另外还有带Locale参数的大小写转换方法:String.toUpperCase(Locale locale)和String.toLowerCase(Locale locale)。 String.toUpperCase(Locale locale)和String.toLowerCase(Locale locale)之所以要带Locale参数,就是希望你指定使用的是哪种语言,不带参数的String.toUpperCase()和String.toLowerCase()使用的是系统缺省的语言,例如操作系统的当前语言。有些语言的大小写转换使用了较特殊的规则,甚至不是1:1的字符对应关系,也就是说转换前和转换后的字符串长度不一定相等。
---如果在Eclipse的run对话框中显示告诉VM我用的是Turkish,那最后输出的结果已经不是我预期的大写的I了,而是另外一个字符(İ)。
这个例子充分说明,大小写转换因语言环境的不同,转换结果可能并非如你所预期的。
如果大小写转换之后你是要用于字符串比较(这样的转换纯粹是为了比较的方便),也就是在我们的26个字母通常大小写转换预期内,则强烈建议你显示指定“英文”的Locale(Local.US)。
如果你的大小写转换就是希望遵循当前使用软件的国家语言大小写转换习惯,则使用不带参数的String.toUpperCase()和String.toLowerCase()更好一些,这样的代码反而能够适应不同的语言环境。
使用包装器对象带来的低效问题
解读
每个基本类型(primitive)都有相应的包装器(wrapper)对象:Integer、Long、Float、Double、Short、Byte、Character和Boolean,我们在使用时不要直接new Integer对象(这样的做法是低效的),而应该调用包装器对象的valueOf方法
为什么以上代码是低效的,而要使用Integer result = Integer.valueOf(-1)或者Integer result = -1来代替?我们来看看JDK的Integer.valueOf方法实现
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
可以看到内存中已经缓存有-128到127这个区间的256个Integer对象,如果方法传入的i值介于这个范围就不用再new Integer对象了,省去了运行时间,也节省了内存资源。另外的几个包装器对象( Long、Short、Byte、Character和Boolean )类似。
对于Map元素的遍历使用entrySet还是KeySet?
JDK实现的数据结构中常用的Map有两类:HashMap和TreeMap。keySet和entrySet在Map元素数较少时(小于10000)在查询速度上的区别不大,它们对于程序性能的影响可以忽略不计。但在元素较多时(大于100000)时entrySet的速度要明显快于keySet,尤其是TreeMap更明显。
对InputStream.skip()返回值的处理(隐藏的异常)
java.io.InputStream.skip(long n):跳过和放弃此输入流中的 n 个数据字节,返回的是跳过的实际字节数。如果skip方法的返回值小于要跳过得字节数,则说明有异常发生,此时需要对异常情况进行处理。
需要比较skip的返回值和输入参数,如果两者不相等时,需做特殊处理。
两者不相等的情况可能的原因有:
1)在跳过 n 个字节之前已到达文件的末尾;
2)输入参数为负;
正确理解String/StringBuffer/StringBuilder的区别
StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。
StringBuilder提供一个与StringBuffer兼容的API,该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。
11.
Java中流的操作,不要使用匿名流对象,以免出现异常,没有句柄,无法关闭
例子:
try
{
File file = new File("test.exe");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
//之后只关闭了一个流bis,匿名流new FileInputStream(file)没有关闭
........
}
改写之后:
try
{
File file = new File("test.exe");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
//之后关闭两个流fis和bis
........
}
这个效率相对低点
m.keySet().iterator()
以后最好用下面这个
jmap.entrySet().iterator()