Java常见异常处理

异常是Java程序中经常遇到的问题,一个异常就是一个Bug,就要花很多时间来定位异常。

  • Java异常

(1)Throwable是Java异常的顶级类,所有的异常都继承于这个类。

(2)Error,Exception是异常类的两个大分类。

(3)Error是非程序异常,即程序不能捕获的异常,一般是编译或者系统性的错误,如OutOfMemorry内存溢出异常等。

(4)Exception是程序异常类,由程序内部产生。Exception又分为运行时异常、非运行时异常。

(5)运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过,运行时异常可处理或者不处理。运行时异常一般常出来定义系统的自定义异常,业务根据自定义异常做出不同的处理。常见的运行时异常如NullPointException、ArrayIndexOutOfBoundsException等。

(6)非运行时异常是程序必须进行处理的异常,捕获或者抛出,如果不处理程序就不能编译通过。如常见的IOException、ClassNotFoundException等。

  • 常见的Java异常坑
  1. 最顽固的坑--NullPointerException

空指针异常应该是每一个程序员必须要踩得的坑,而且应该是经常踩不断踩的坑,极其普遍且难以根除。但是这个坑可以通过某些方法有效的避免。

(1)什么是空指针?

当一个变量的值为 null 时,在 Java 里面表示一个不存在的空对象,没有实际内容,没有给它分配内存,null 也是对象成员变量的默认值。

所以,一个对象如果没有进行初始化操作,这时候,如果你调用这个对象的方法或者变量,就会出现空指针异常。

如下面示例会发生空指针异常:

Object object = null;
String string = object.toString();

空指针它是属于运行时异常 RuntimeException 的子类,它不是捕获型的,只有在程序运行时才可能报出来,而且会造成程序中断。

  1. 如何有效避免空指针?

以下是我在编码过程中遇到的问题及解决办法。

字符串比较,常量放前面---总是从已知的非空String对象中调用equals()方法

if(status.equals(SUCCESS)){

}

这个时候 status 可能为 null 造成空指针异常,应该把常量放前面,就能避免空指针异常。这个应该在各种开发规范里面都会提到,也是最基础的。

if(SUCCESS.equals(status)){

}

当valueOf()和toString()返回相同的结果时,宁愿使用前者。

因为调用null对象的toString()会抛出空指针异常,如果我们能够使用valueOf()获得相同的值,那宁愿使用valueOf(),传递一个null给valueOf()将会返回“null”,尤其是在那些包装类,像Integer、Float、Double和BigDecimal。

BigDecimal bd = getPrice();

System.out.println(String.valueOf(bd)); //不会抛出空指针异常

System.out.println(bd.toString()); //抛出 NullPointerException

初始化默认值

在对象初始化的时候给它一个默认值或者默认构造实现,如:

User user = new User();
String name = StringUtils.EMPTY;

避免返回空集合

在返回一个集合的话,默认会是 null,统一规范返回一个空集合。

举个 List 例子,如:

public List getUserList(){
    List list = userMapper.gerUserList();
    return list == null ? new ArrayList() : list;
}

这样接收方就不用担心空指针异常了,也不会影响业务。

采用JDK8 Optional 新特性

(这个暂时因为我们车道系统统一适用的是Java7,这个方案还没有在车到系统代码里使用过。)

  1. OutOfMemoryError

内存异常异常也是一个经常出现的Bug,但是这个不是程序能控制的,这个问题是指要分配的对象的内存超出了当前最大的堆内存,需要调整堆内存大小(-Xmx)以及优化程序。

(1)常见的有以下几种原因

1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;

2.集合类中有对对象的引用使用完后未清空,使得JVM不能回收;

3.代码中存在死循环或循环产生过多重复的对象实体;

4.使用的第三方软件中的BUG;

5.启动参数内存值设定的过小

(2)常见解决方法:

1.应用服务器提示错误的解决:
  把启动参数内存值设置足够大。

2.Java代码导致错误的解决:

1)检查代码中是否有死循环或递归调用。

2)检查是否有大循环重复产生新对象实体。

3)检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。

4 )检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

  1. IOException

IO,即:input, output,我们在读写磁盘文件、网络内容的时候经常会生的一种异常,这种异常是受检查异常,需要进行手工捕获。常用的一种异常处理方式有两种,一种是:使用throws抛出可能发生的异常,另一种是:直接try-catch。

  1. ClassNotFoundException

类找不到异常,Java开发中经常遇到,是不是很绝望?这是在加载类的时候抛出来的,即在类路径下不能加载指定的类。

注意:在车道程序中不推荐使用throws把异常抛给系统处理,车道系统要捕获所有能捕获的异常并进行处理,目的是当发生程序级异常时要保证车道程序可以正常收费,不可因为程序异常影响到正在进行的收费处理。