一、Exception 和 Error有什么区别?运行时异常与通常异常有什么区别?html
Exception和Error都继承自java.lang.Throwable。在Java中只有Throwable的实例才能够被抛出和捕获,是异常处理机制的基本组成类型。java
之因此区分 checked/unchecked exception,JAVA的设计思想是区分从类/方法设计者角度来看两种不一样的异常: 一种是设计者认为这个方法在使用过程当中使用者可以处理的异常,这些每每做为checked exception。
好比一个IO系统的设计者会认为诸如物理文件不存在或者介质没法读取等异常时极可能发生,而使用者彻底可能捕获这个异常,经过让用户 从新输入文件名等方式从新进行这个操做,也就是说,
这是一个可恢复的操做。因此我会在诸如 read()/write()等操做中throw 一个 IOException(checked exception)。 第二种是设计者认为使用者不可以处理的异常,好比我写一个函数要求传入的参数是个正数,那么当我发现使用者传 了个负数进来时,合理的预期是程序中出bug了。若是我抛出一个异常描述这件事,
即便我要求调用者捕获这个异常,他确定也不知道该怎么办(总不能随便传一 个正数进来吧)。这时候我就会抛出一个IllegalArgumentException(uncheck exception),这里面的潜台词
是:小子,我知道你也是帮人背黑锅的,处理不了这个,你仍是交给你的领导(调用你的程序)去处理这个异常吧。 同理,当JVM发现除数为0时,抛出的ArithmeticException也是一个
unchecked exception。
从这里能够看出,checked exception和 unchecked exception的根本区别在于设计者认为使用者是否可以而且应该处理这个异常。不幸的是,因为Java使用者水平的良莠不齐,大量的 unchecked
exception该被设计成了checked exception,而对于真正的checked exception,又有太多被catch了以后啥都不做就悄无声息了。尤为是不声不响吞噬exception的行为,不但达不到设计者原本的
要求(进行 恢复处理),甚至问题更大(连 unchecked exception那种最后报错的效果都没了)。
二、常见的Error异常数据库
java.lang.AbstractMethodError 抽象方法错误。当应用试图调用抽象方法时抛出。 java.lang.AssertionError 断言错。用来指示一个断言失败的状况。 java.lang.ClassCircularityError 类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。 java.lang.ClassFormatError 类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。 java.lang.Error 错误。是全部错误的基类,用于标识严重的程序运行问题。这些问题一般描述一些不该被应用程序捕获的反常状况。 java.lang.ExceptionInInitializerError 初始化程序错误。当执行一个类的静态初始化程序的过程当中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。 java.lang.IllegalAccessError 违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,可是又违反域或方法的可见性声明,则抛出该异常。 java.lang.IncompatibleClassChangeError 不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。通常在修改了应用中的某些类的声明定义而没有对整个应用从新编译而直接运行的状况下,容易引起该错误。 java.lang.InstantiationError 实例化错误。当一个应用试图经过Java的new操做符构造一个抽象类或者接口时抛出该异常. java.lang.InternalError 内部错误。用于指示Java虚拟机发生了内部错误。 java.lang.LinkageError 连接错误。该错误及其全部子类指示某个类依赖于另一些类,在该类编译以后,被依赖的类改变了其类定义而没有从新编译全部的类,进而引起错误的状况。 java.lang.NoClassDefFoundError 未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。 java.lang.NoSuchFieldError 域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。 java.lang.NoSuchMethodError 方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。 java.lang.OutOfMemoryError 内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。 java.lang.StackOverflowError 堆栈溢出错误。当一个应用递归调用的层次太深而致使堆栈溢出时抛出该错误。 java.lang.ThreadDeath 线程结束。当调用Thread类的stop方法时抛出该错误,用于指示线程结束。 java.lang.UnknownError 未知错误。用于指示Java虚拟机发生了未知严重错误的状况。 java.lang.UnsatisfiedLinkError 未知足的连接错误。当Java虚拟机未找到某个类的声明为native方法的本机语言定义时抛出。 java.lang.UnsupportedClassVersionError 不支持的类版本错误。当Java虚拟机试图从读取某个类文件,可是发现该文件的主、次版本号不被当前Java虚拟机支持的时候,抛出该错误。 java.lang.VerifyError 验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。 java.lang.VirtualMachineError 虚拟机错误。用于指示虚拟机被破坏或者继续执行操做所需的资源不足的状况。
三、常见的可检查异常编程
java.lang.Exception 根异常。用以描述应用程序但愿捕获的状况。 java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH以后找不到对应名称的class文件时,抛出该异常 java.lang.IllegalAccessException 当应用程序试图反射性地建立一个实例(而不是数组)、设置或获取一个字段,或者调用一个方法,但当前正在执行的方法没法访问指定类、字段、方法或构造方法的定义时,抛出 IllegalAccessException java.lang.IOException 当发生某种 I/O 异常时,抛出此异常。此类是失败或中断的 I/O 操做生成的异常的通用类。 java.lang.SQLException 提供关于数据库访问错误或其余错误信息的异常。
java.lang.InterruptedException
线程在等待,睡眠或以其余方式占用时抛出,线程在活动以前或活动期间中断。
四、常见的运行时异常数组
java.lang.RuntimeException 运行时异常。是全部Java虚拟机正常操做期间能够被抛出的异常的父类。 java.lang.ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常。 java.lang.IllegalArgumentException 抛出的异常代表向方法传递了一个不合法或不正确的参数。 java.lang.IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 java.lang.NullPointerException 当应用程序试图在须要对象的地方使用 null 时,抛出该异常。
五、 检查代码反映了异常处理中哪些不当之处?安全
public static void main(String[] args) { try{ // 业务代码 Thread.sleep(1000L); } catch (Exception e){ //Ignore it }
}
这段代码违反了异常处理的两个原则:函数
第一:尽可能不要捕获Exception这样的通用异常,应该捕获特定异常Thread.sleep()抛出的是java.lang.InterruptedException,另外咱们也要保证程序不会捕获到咱们不但愿捕获的异常,例如运行时异常RuntimeException。另外除非特殊状况不要去捕获Throwable或者Error,这样很难保证咱们可以正常程序处理java.lang.OutOfMemoryError;性能
第二:不要生吞(swallow)异常,这样极可能致使很是难以诊断的诡异状况;优化
public static void main(String[] args) { try{ // 业务代码 } catch (Exception e){ e.printStackTrace(); } }
这段代码若是做为实验代码没有问题,若是做为生产代码就有问题了。ui
java.lang.Throwable中的printStackTrace()方法是将此Throwable和其追溯打印到标准错误流。 此方法在错误输出流上为该Throwable
对象打印一个堆栈跟踪,该值为字段System.err
的值。
问题就出在这里,在稍微复杂的生产环境中,标准出错(stderr)不是一个合适的输出选项,由于你很难判断出到底输出到哪了。最好使用产品日志,详细的输入到日志系统中。
public void readPreferences(String fileName){ //...perform operations... InputStream in = new FileInputStream(fileName); //...read the preferences fil... }
Throw early,catch late 原则
若是参数fileName为null,则会抛出java.lang.NullPointerException。因为没有第一时间暴露问题,堆栈信息可能很是使人费解。在生产环境可能有各类各样的场景,若是在发现问题的时候,能第一时间抛出,能更加清晰的反应问题。下面是咱们修改后的代码,让Throw early,异常信息就很是直观了:
public void readPreferences(String fileName){ Objects. requireNonNull(fileName); //...perform operations... InputStream in = new FileInputStream(fileName); //...read the preferences fil... }
catch late 咱们通常有两种处理方式,一种就是生吞,本质其实就是掩盖异常。另外一种就是保留原有异常的cause信息,直接抛出或者构建新的异常抛出。在更高层面由于有了清晰的业务逻辑,每每更清楚怎么处理。
六、自定义异常
有时候咱们会根据须要自定义异常,这个时候除了保证提供足够的信息,还须要考虑两点:
第一:是否须要定义成Check Exception,这种类型的设计初衷更是为了从异常状况恢复;另外若是一个异常所表示的并非代码自己的不足所致使的非正常状态,而是一系列应用自己也没法控制的状况,那么咱们将须要使用Checked Exception。
第二:在保证诊断信息足够的同时,也须要考虑避免包含敏感信息;例如:java.net.ConnectException出错的信息是相似“Connection refused”而不包含具体的机器名,IP等。
业界有一种争论,java语言的Check Exception也许是一个设计错误:
Check Exception的假设是咱们捕获了异常,而后恢复程序。但实际大多数状况不可能恢复;
Check Exception不兼容functional编程;
七、从性能角度解析Java的异常处理机制
参考:
https://time.geekbang.org/column/article/6849
http://www.importnew.com/27834.html