怎么又出错了?盘点java中最容易出现的错误。

现现在,java已经普遍应用各类软件开发领域。基于面向对象的设计,java屏蔽了诸如C,C++等语言的一些复杂性,提供了垃圾回收机制,平台无关的虚拟机技术,Java创造了一种史无前例的开发方式。因此,java对比其余程序语言更加受欢迎。所以,Java中的异常也是随时发生,下面我就列出了我认为的Java开发最容易出现的10个错误。java

一、重复造轮子
一个明显的错误就是Java程序员习惯性的忽略已经存在的大量的库。在你决定造一个轮子之间,我建议你试着先搜一下是否有已经存在库。例如日志方面,有logback,新log4j,网络方面,有Netty或者Akka。有一些库,已经逐步变成了标准,好比Java8中加入的Joda-Time。
下面讲述的是我上一个项目中的我的经历。有一部分用于HTML转义的代码是一个开发本身完成的。这个代码正常工做了多年,可是又一次遇到了一个用户输入,代码陷入了死循环。这个用户发现应用没有反应,又从新输入了一遍,服务器由于这个死循环挂了。若是这个开发使用已有的HTML转义工具,好比Google Guava项目提供的HtmlEscaper,这个严重的问题可能就不会出现。而且如今市面上流行的大部分的开源库,背后都有团队和社区在支持,相似这样的错误,都可以及时的被修复。
二、在Switch-Case中错误的使用break
这是一个很尴尬的问题,可是仍然在实际开发中常常出现。瀑布特性在switch语句中有时会很是有用,可是必要的break关键字的缺失,有时会带来灾难性的后果。好比在下面的代码中,若是在case 0中忘记放一个break关键字,代码会继续向下执行,就会在Zero以后再输出一个One:
public static void switchCasePrimer() {
int caseIndex = 0;
switch (caseIndex) {
case 0:
System.out.println("Zero");
case 1:
System.out.println("One");
break;
case 2:
System.out.println("Two");
break;
default:
System.out.println("Default");
}
}程序员

最好的解决办法是使用多态,并把不一样的处理代码放到子类中。固然,相似这样的错误,也能够经过相似FindBugs或者PMD这样的工具检查出来。
三、忘记释放资源
一旦打开一个文件,或者创建一个网络链接,一个很是重要的习惯是记得关闭资源。而且必定记得,若是在使用相似这样的资源过程当中出现了错误,在异常处理中,也须要作对应的关闭操做。可能有人会说,FileInputStream对象在GC的时候,Java终结器(finalizer)会自动调用其close()方法,可是咱们知道,咱们没法预知GC在何时开始,因此咱们没法预知在执行GC以前,会有多少资源没法及时关闭。为了不这种状况,Java7推出的try-with-resources语法,是值得每一个开发使用的。
private static void printFileJava7() throws IOException {
try(FileInputStream input = new FileInputStream("file.txt")) {
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
}
}编程

try-with-resources语法适用于全部实现了AutoClosable接口的类。它能保证每个资源及时的关闭。
四、内存泄露
Java使用自动内存管理,因此大部分时间,咱们都不会去关心内存的分配和释放,可是,这并不意味着Java开发人员须要忽略内存。在Java应用中,内存的问题也常常出现。咱们知道,对象若是没有被引用了,这个对象就会被释放,可是并不意味着,就不会出现内存泄露的问题。在Java中,形成内存泄露的缘由有不少,但最容易出现的状况就是对象引用没法释放,由于GC在回收堆内存的时候,若是一个对象仍然被其余对象引用,这个对象空间是不会被回收的,举个例子,若是在类中,有一个静态字段引用到一个集合,假如咱们没有手动的在使用完成这个集合以后,将他设置为null,那么这个集合及这个集合中的对象,是永远不会被回收的,由于类静态字段是不会被GC的。
好比还有一种形成内存泄露的缘由,就是一组对象互相引用对方,就是咱们常常说的循环引用,由于循环引用,因此GC不能肯定这些互相引用的对象是否还有继续存活的必要。还有一种状况,就是使用JNI时的非堆内存泄露。
一个典型的内存泄露例子:
final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);final Deque numbers = new LinkedBlockingDeque<>();final BigDecimal divisor = new BigDecimal(51);服务器

scheduledExecutorService.scheduleAtFixedRate(() -> {
BigDecimal number = numbers.peekLast();
if (number != null && number.remainder(divisor).byteValue() == 0) {
System.out.println("Number: " + number);
System.out.println("Deque size: " + numbers.size());
}
}, 10, 10, TimeUnit.MILLISECONDS);markdown

scheduledExecutorService.scheduleAtFixedRate(() -> {
    numbers.add(new BigDecimal(System.currentTimeMillis()));
}, 10, 10, TimeUnit.MILLISECONDS);

try {
scheduledExecutorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}网络

在上面的例子中,咱们建立了两个定时任务。第一个定时任务,从deque中获取了最后的一个数字”numbers”并判断,若是这个数字能被51整除,则打印该数字和deque的大小。第二个定时任务,不断的向deque中添加数据。两个任务都间隔10ms执行。若是这个代码执行,你会发现,deque的大小会持续的增长,直到deque中的数据占满整个堆空间。为了阻止这种状况的发生,咱们可使用pollLast方法来代替peekLast方法,由于pollLast方法会在拿到最后一个元素以后,把这个元素从deque中移除。
五、过分产生垃圾数据
过分产生垃圾数据的意思,是程序运行中大量产生短声明周期的对象。这回致使GC频繁的执行,从内存中回收空间,GC的执行是须要完成堆扫描的,这对系统的性能影响是很是大的。下面是一个小例子:
String oneMillionHello = "";for (int i = 0; i < 1000000; i++) {
oneMillionHello = oneMillionHello + "Hello!";
}
System.out.println(oneMillionHello.substring(0, 6));app

在Java中,字符串是不可变的,因此每一次循环都会建立一个新的字符串对象。为了改进这种代码,咱们可使用StringBuilder来代替:
StringBuilder oneMillionHelloSB = new StringBuilder();
for (int i = 0; i < 1000000; i++) {
oneMillionHelloSB.append("Hello!");
}
System.out.println(oneMillionHelloSB.toString().substring(0, 6));ide

第二个版本的代码,在执行的时候会提升很多的性能。工具

可能这些问题在你们的编程过程当中常常出现,可是却没有总结起来,它们其实都是很重要的。将这些问题所有解决,对你的编程能力的提高必定是显而易见的。接下来,咱们会继续总结出你们容易出现的其余问题,助你成为java大神。性能

相关文章
相关标签/搜索