异常是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异常坑
最顽固的坑--NullPointerException
空指针异常应该是每个程序员必需要踩得的坑,并且应该是常常踩不断踩的坑,极其广泛且难以根除。可是这个坑能够经过某些方法有效的避免。
(1)什么是空指针?
当一个变量的值为 null 时,在 Java 里面表示一个不存在的空对象,没有实际内容,没有给它分配内存,null 也是对象成员变量的默认值。
因此,一个对象若是没有进行初始化操做,这时候,若是你调用这个对象的方法或者变量,就会出现空指针异常。
以下面示例会发生空指针异常:
Object object = null;
String string = object.toString();
空指针它是属于运行时异常 RuntimeException 的子类,它不是捕获型的,只有在程序运行时才可能报出来,并且会形成程序中断。
如何有效避免空指针?
如下是我在编码过程当中遇到的问题及解决办法。
字符串比较,常量放前面---老是从已知的非空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 新特性
Optional是Jdk8提供的一个能够包含null值的容器对象,能够用来代替xx != null的判断。(这个暂时由于咱们车道系通通一适用的是Java7,这个方案尚未在车到系统代码里使用过。)
OutOfMemoryError
内存异常异常也是一个常常出现的Bug,可是这个不是程序能控制的,这个问题是指要分配的对象的内存超出了当前最大的堆内存,须要调整堆内存大小(-Xmx)以及优化程序。
(1)常见的有如下几种缘由:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的太小
(2)常看法决方法:
1.应用服务器提示错误的解决:
把启动参数内存值设置足够大。
2.Java代码致使错误的解决:
1)检查代码中是否有死循环或递归调用。
2)检查是否有大循环重复产生新对象实体。
3)检查对数据库查询中,是否有一次得到所有数据的查询。通常来讲,若是一次取十万条记录到内存,就可能引发内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引发内存溢出。所以对于数据库查询尽可能采用分页的方式查询。
4 )检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
IOException
IO,即:input, output,咱们在读写磁盘文件、网络内容的时候常常会生的一种异常,这种异常是受检查异常,须要进行手工捕获。经常使用的一种异常处理方式有两种,一种是:使用throws抛出可能发生的异常,另外一种是:直接try-catch。
ClassNotFoundException
类找不到异常,Java开发中常常遇到,是否是很绝望?这是在加载类的时候抛出来的,即在类路径下不能加载指定的类。
注意:在车道程序中不推荐使用throws把异常抛给系统处理,车道系统要捕获全部能捕获的异常并进行处理,目的是当发生程序级异常时要保证车道程序能够正常收费,不可由于程序异常影响到正在进行的收费处理。
异常处理的通常原则
一、 能处理就早处理,抛出不去还不能处理的异常就要想办法消化掉,或者转换为RuntimeException处理。由于对于一个应用系统来讲,抛出大量异常是有问题的,应该从程序开发角度尽量的控制异常发生的可能。
二、 对于检查异常,若是不能行之有效的处理,还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。
三、 对于一个应用系统来讲,应该有本身的一套异常处理框架,这样当异常发生时,也能获得统一的处理风格,将优雅的异常信息反馈给用户。
异常的转译与异常链
一、异常转译的原理
所谓的异常转译就是将一种异常转换另外一种新的异常,也许这种新的异常更能准确表达程序发生异常。
在Java中有个概念就是异常缘由,异常缘由致使当前抛出异常的那个异常对象,几乎全部带异常缘由的异常构造方法都使用Throwable类型作参数,这也就为异常的转译提供了直接的支持,由于任何形式的异常和错误都是Throwable的子类。好比将SQLException转换为另一个新的异常DAOException,能够这么写:
先自定义一个异常DAOException:
好比有一个SQLException类型的异常对象e,要转换为DAOException,能够这么写:
public class DAOException extends RuntimeException {
//(省略了部分代码)
public DAOException(String message, Throwable cause) {
super(message, cause);
}
}
DAOException daoEx = new DAOException ( "SQL异常", e);
异常转译是针对全部继承Throwable超类的类而言的,从编程的语法角度讲,其子类之间均可以相互转换。可是,从合理性和系统设计角度考虑,可将异常分为三类:Error、Exception、RuntimeException。
异常的处理存在着一套哲学思想:对于一个应用系统来讲,系统所发生的任何异常或者错误对操做用户来讲都是系统"运行时"异常,都是这个应用系统内部的异常。这也是异常转译和应用系统异常框架设计的指导原则。在系统中大量处理非检查异常的AxiTrader返佣www.fx61.com/brokerlist/axitrader.html负面影响不少,最重要的一个方面就是代码可读性下降,程序编写复杂,异常处理的代码也很苍白无力。所以,颇有必要将这些检查异常Exception和错误Error转换为RuntimeException异常,让程序员根据状况来决定是否捕获和处理所发生的异常。
①:Error到Exception:将错误转换为异常,并继续抛出。例如Spring WEB框架中,将org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中,将捕获的错误转译为一个NestedServletException异常。这样作的目的是为了最大限度挽回因错误发生带来的负面影响。由于一个Error经常是很严重的错误,可能会引发系统挂起。
②:Exception到RuntimeException:将检查异常转换为RuntimeException可让程序代码变得更优雅,让开发人员集中精力设计更合理的程序代码,反过来也增长了系统发生异常的可能性。
③:Error到RuntimeException:目的仍是同样的。把全部的异常和错误转译为不检查异常,这样可让代码更为简洁,还有利于对错误和异常信息的统一处理。
一、 异常链
异常链顾名思义就是将异常发生的缘由一个传一个串起来,即把底层的异常信息传给上层,这样逐层抛出。Java API文档中给出了一个简单的模型:
try {
lowLevelOp();
} catch (LowLevelException le) {
throw (HighLevelException)
new HighLevelException().initCause(le);
}
当程序捕获到了一个底层异常le,在处理部分选择了继续抛出一个更高级别的新异常给此方法的调用者。这样异常的缘由就会逐层传递。这样,位于高层的异常递归调用getCause()方法,就能够遍历各层的异常缘由。这就是Java异常链的原理。异常链的实际应用不多,发生异常时候逐层上抛不是个好注意,上层拿到这些异常又能奈之何?并且异常逐层上抛会消耗大量资源,由于要保存一个完整的异常链信息。html