Java 基础 - Exception和Error
综述
Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。从设计初衷也能看出区别:Java希望可以从异常中恢复程序, 但却不应该尝试从错误中恢复程序:
- Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
- Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。Exception 又分为检查型异常(checked exception)和非检查型异常(unchecked exception):
- 检查型异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。
- 非检查型异常就是所谓的运行时异常(runtime exception),类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。RuntimeException是非常特殊的子类,可以不用throw和throws。哪怕你throw了,也没必要throws; 即使你throws了,调用者也没必要try-catch。还是因为runtime exception 还是希望能暴露出来,而不是由程序swallow或者自行处理。
形象比喻,快速理解
1.假如你开车上山,车坏了,你拿出工具箱修一修,修好继续上路(Exception被捕获,从异常中恢复,继续程序的运行)
2.车坏了,你不知道怎么修,打电话告诉修车行,告诉你是什么问题,要车行过来修。(在当前的逻辑背景下,你不知道是怎么样的处理逻辑,把异常抛出去到更高的业务层来处理)。
3.你打电话的时候,要尽量具体,不能只说我车动不了了。那修车行很难定位你的问题。(要捕获特定的异常,不能捕获类似Exception的通用异常)。
4.还有一种情况是,你开车上山,山塌了,这你还能修吗?(Error:导致你的运行环境进入不正常的状态,很难恢复)
操作Throwable的元素/关键字
Throw VS Throws
不同点:
throws出现在方法函数头;而throw出现在函数体。
throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
相同点:
两者都是延迟处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数本身去处理异常,真正的处理异常由函数的上层调用者处理。(这里的延迟并不一定不好,可能函数的调用者更清楚业务逻辑,对该异常能做出更合适的处理方式)
Throw
throw是语句抛出一个异常,一般是在代码块的内部,当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常:
public static void main(String[] args) {
String s = "abc";
if(s.equals("abc")) {
throw new NumberFormatException();
} else {
System.out.println(s);
}
//function();
}
运行时,系统会抛出异常:
Exception in thread "main" java.lang.NumberFormatException at......
Throws
throws是方法可能抛出异常的声明。(用在声明方法时,表示该方法可能要抛出异常)
public void function() throws Exception{......}
当某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理.
public class testThrows { public static void function() throws NumberFormatException {
String s = "abc";
System.out.println(Double.parseDouble(s));
} public static void main(String[] args) {
try {
function();
} catch (NumberFormatException e) {
System.err.println("非数据类型不能强制类型转换。");
//e.printStackTrace();
}
}
}
运行结果:
非数据类型不能强制类型转换。
try catch finally 与 continue, break, return, throw 的执行关系
详细请看:https://www.cnblogs.com/bethunebtj/p/4676020.html
情景1 - finally块不会执行的情景
1-如果程序在try块之前就抛出了异常,finally块不会执行。
2-如果try块中调用了 System.exit(0),终止了 Java 虚拟机的运行,那么finally块不会执行。(不常见的情况)
情景2 - finally块会执行的情景,要点总结
1-在排除了以上 finally 语句块不执行的情况后,finally 语句块就一定会执行
2-finally 语句块是在 try 或者 catch 中的 return 语句之前执行的,更加一般的说法是,finally 语句块应该是在控制转移语句之前执行。(控制转移语句: return、throw、break 和 continue 都是控制转移语句,但是它们之间是有区别的。其中 return 和 throw 把程序控制权转交给它们的调用者(invoker),而 break 和 continue 的控制权是在当前方法内转移。)因此注意:不要在finally代码块中处理返回值。
3-请看下面两个例题:(详细解释请看: https://www.cnblogs.com/bethunebtj/p/4676020.html )
清单 5.
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
} public static int getValue() {
try {
return 0;
} finally {
return 1;
}
}
}
清单 5 的执行结果:
return value of getValue(): 1
清单 6.
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
} public static int getValue() {
int i = 1;
try {
return i;
} finally {
i++;
}
}
}
清单 6.执行结果:
return value of getValue(): 1
利用我们上面分析得出的结论:finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。 由此,可以轻松的理解清单 5 的执行结果是 1。因为 finally 中的 return 1;语句要在 try 中的 return 0;语句之前执行,那么 finally 中的 return 1;语句执行后,把程序的控制权转交给了它的调用者 main()函数,并且返回值为 1。那为什么清单 6 的返回值不是 2,而是 1 呢?按照清单 5 的分析逻辑,finally 中的 i++;语句应该在 try 中的 return i;之前执行啊? i 的初始值为 1,那么执行 i++;之后为 2,再执行 return i;那不就应该是 2 吗?怎么变成 1 了呢?
关于 Java 虚拟机是如何编译 finally 语句块的问题,有兴趣的读者可以参考《 The JavaTM Virtual Machine Specification, Second Edition 》中 7.13 节 Compiling finally。那里详细介绍了 Java 虚拟机是如何编译 finally 语句块。实际上,Java 虚拟机会把 finally 语句块作为 subroutine(对于这个 subroutine 不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解。)直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中。待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。请注意,前文中我们曾经提到过 return、throw 和 break、continue 的区别,对于这条规则(保留返回值),只适用于 return 和 throw 语句,不适用于 break 和 continue 语句,因为它们根本就没有返回值。
由上图,我们可以清晰的看出,在 finally 语句块(iinc 0, 1)执行之前,getValue()方法保存了其返回值(1)到本地表量表中 1 的位置,完成这个任务的指令是 istore_1;然后执行 finally 语句块(iinc 0, 1),finally 语句块把位于 0 这个位置的本地变量表中的值加 1,变成 2;待 finally 语句块执行完毕之后,把本地表量表中 1 的位置上值恢复到操作数栈(iload_1),最后执行 ireturn 指令把当前操作数栈中的值(1)返回给其调用者(main)。这就是为什么清单 6 的执行结果是 1,而不是 2 的原因。
常见的Exception和Error的子类
NoClassDefFoundError VS ClassNotFoundException
ClassNotFoundException |
NoClassDefFoundError |
It is an exception. It is of type java.lang.Exception. |
It is an error. It is of type java.lang.Error. |
It occurs when an application tries to load a class at run time which is not updated in the classpath. |
It occurs when java runtime system doesn’t find a class definition, which is present at compile time, but missing at run time. |
It is thrown by the application itself. It is thrown by the methods like Class.forName(), loadClass() and findSystemClass(). |
It is thrown by the Java Runtime System. |
It occurs when classpath is not updated with required JAR files. |
It occurs when required class definition is missing at runtime. |
ClassNotFoundException
ClassNotFoundException is a runtime exception that is thrown when an application tries to load a class at runtime using the Class.forName() or loadClass() or findSystemClass() methods ,and the class with specified name are not found in the classpath. For example, you may have come across this exception when you try to connect to MySQL or Oracle databases and you have not updated the classpath with required JAR files. Most of the time, this exception occurs when you try to run an application without updating the classpath with required JAR files.
For example, the below program will throw ClassNotFoundException if the mentioned class “oracle.jdbc.driver.OracleDriver” is not found in the classpath.
public class MainClass
{
public static void main(String[] args)
{
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
}catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
If you run the above program without updating the classpath with required JAR files, you will get an exception akin to:
java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at pack1.MainClass.main(MainClass.java:17)
NoClassDefFoundError
NoClassDefFoundError is an error that is thrown when the Java Runtime System tries to load the definition of a class, and that class definition is no longer available. The required class definition was present at compile time, but it was missing at runtime. For example, compile the program below.
class A
{
// some code
}
public class B
{
public static void main(String[] args)
{
A a = new A();
}
}
When you compile the above program, two .class files will be generated. One is A.class and another one is B.class. If you remove the A.class file and run the B.class file, Java Runtime System will throw NoClassDefFoundError like below:
Exception in thread "main" java.lang.NoClassDefFoundError: A
at MainClass.main(MainClass.java:10)
Caused by: java.lang.ClassNotFoundException: A
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
Exception的最佳实践
Reference: https://vitalflux.com/java-top-5-exception-handling-coding-practices-avoid/
不要捕获Throwable
因为Throwable是Exception 和 Error的父类,会连同Error (例如OutOfMemoryError, StackOverFlowError 和 InternalError) 也被捕获。Error在设计之初就是不应该被捕获的, 因为它是那种不可修复的错误。
不要抛出Generic exceptions (Error, RuntimeException, Throwable and Exception)
如果throw generic exceptions, 会导致类和函数无法捕获到预期的异常。导致可能延迟很久才捕获到异常,增大了修复和debug的难度。
不要调用printStackTrace()函数
来打印错误信息。Following are some of the reasons why one should avoid invoking printStackTrace method on Throwable/Exception classes and instead use Logger method using one of the frameworks such as LogBack or Log4J:
- Difficult to Retrieve Logs for Debugging:The logs written using printStackTrace is written to System.err which is hard to route or filter elsewhere. Instead, using Loggers, it is easy to retrieve logs for debugging purpose.
- Violation of Coding Best Practices:Generally, as per coding guidelines in production-ready applications, developers need to use Logger methods for logging different level of information. However, when it comes to exception handling, the instances of printStackTrace are commonly found in various places. This is, thus, a violation of coding practice and, thus, should be avoided.
- Following are some good pages explaining the reasons in detail:
尽量不要捕获类似 Exception 这样的通用异常
而是应该捕获特定异常。在这里是 Thread.sleep() 抛出的 InterruptedException。
try {
// 业务代码
// …
Thread.sleep(1000L);
} catch (Exception e) {
// Ignore it
}
不要生吞(swallow)异常
这是异常处理中要特别注意的事情,因为很可能会导致非常难以诊断的诡异情况。如果我们不把异常抛出来,或者也没有输出到日志(Logger)之类,程序可能在后续代码以不可控的方式结束。没人能够轻易判断究竟是哪里抛出了异常,以及是什么原因产生了异常。
异常处理器应保留原始异常完整信息
When writing code for doing exception handling, I have often seen code such as following which does some of the following:
In the code sample below, the exception is lost.
try {
/* ... */
} catch( Exception e ) {
SomeLogger.info( e.getMessage() ); // The exception is lost. Just that exception message is written; Also, context information is not logged.
}
In the code sample below, whole exception object is lost.
try {
/* ... */
} catch( Exception e ) {
SomeLogger.info( "some context message" ); // The exception is lost
}
In the code sample below, no context message is provided.
try {
/* ... */
} catch( Exception e ) {
SomeLogger.info( e ); // No context message
}
As a best practice, one would want to do something like following:
try {
/* ... */
} catch( Exception e ) {
SomeLogger.info( "some context message", e ); // Context message is there. Also, exception object is present
}
In case of throwable exceptions, following should be done:
try {
/* ... */
} catch (Exception e) {
throw new CustomRuntimeException("context", e); // Context message is there. Also, exception object is present
}
自定义Exception
Demo
前面所讲的异常,都是系统自带的,系统自己处理,但是很多时候项目会出现特有问题,而这些问题并未被java所描述并封装成对象,所以对于这些特有的问题可以按照java的对问题封装的思想,将特有的问题进行自定义异常封装。在Java中要想创建自定义异常,需要继承Throwable或者他的子类Exception。
语法
class 自定义异常类 extends 异常类型(Exception){ // 因为父类已经把异常信息的操作都完成了,所在子类只要在构造时,将异常信息传递给父类通过super 语句即可。
// 重写 有参 和 无参 构造方法
}
例如:
// 备注: 这些方法怎么来的? 重写父类Exception的方法
public class CustomException extends Exception { //无参构造方法
public CustomException(){ super();
} //有参的构造方法
public CustomException(String message){
super(message); } // 用指定的详细信息和原因构造一个新的异常
public CustomException(String message, Throwable cause){ super(message,cause);
} //用指定原因构造一个新的异常
public CustomException(Throwable cause) { super(cause);
} }
如何定义checked异常和unchecked异常
When defining your own exception type, study the existing exception classes in the Java API and try to extend a related exception class. For example, if you’re creating a new class to represent when a method attempts a division by zero, you might extend classArithmeticException because division by zero occurs during arithmetic.
If the existing classes are not appropriate superclasses for your new exception class, decide whether your new class should be a checked or an unchecked exception class.
- If clients should berequired to handle the exception, the new exception class should be a checked exception (i.e., extend Exception but notRuntimeException). The client application should be able to reasonably recover from such an exception.
- If the client code should be able to ignore the exception (i.e., the exception is an unchecked one), the new exception class should extend RuntimeException.
Java 基础 - Exception和Error的更多相关文章
- Java的Exception和Error面试题10问10答
在Java核心知识的面试中,你总能碰到关于 处理Exception和Error的面试题.Exception处理是Java应用开发中一个非常重要的方面,也是编写强健而稳定的Java程序的关键,这自然使它 ...
- JAVA基础 Exception, Error
转载请附上本文地址: http://www.cnblogs.com/nextbin/p/6219677.html 本文参考: JAVA源码 http://swiftlet.net/archives/9 ...
- java中exception和error有什么区别,运行时异常和一般异常有什么区别
1.exception和error都是继承了throwable类,在java中只有throwable类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型 2.ex ...
- Java学习|Exception和Error有什么区别?
典型回答: Exception和Error都继承了Throwable类,java中只有Throwable类型的实例才能被Throw(抛出)或者catch(捕获). Exceptio ...
- 一、基础篇--1.1Java基础-Exception、Error、RuntimeException与一般异常有何异同
Throwable.Error.Exception.RuntimeException 关系如下类图所示: Throwable: Throwable类是java语言中所有错误或者异常的超类.它的两个子类 ...
- 面试官:小伙子,你给我说一下Java Exception 和 Error 的区别吧?
前言 昨天在整理粉丝给我私信的时候,发现了一个挺有意思的事情.是这样的,有一个粉丝朋友私信问我Java 的 Exception 和 Error 有什么区别呢?说他在面试的时候被问到这个问题卡壳了,最后 ...
- Java Exception 和Error
(事先声明:该文章并非完全是我自己的产出,更多的是我个人在看到资料后通过理解并记录下来,作为自己阅读后的一个笔记:我现在试图对自己多年工作中的知识点做一个回顾,希望能融会贯通) (此文参考<Ja ...
- Java基础面试题集(一)
Java基础面试题 一.面向对象编程(OOP) 7 二.常见的Java问题 7 2.1.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? 7 2.2.JDK和JRE的区别是什么? ...
- 精心收集java基础106条
Java基础 1.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 一个Java源文件中可以定义多个类,但最多只能定义一个public的类,并且public ...
随机推荐
- 使用Docker快速部署ELK分析Nginx日志实践
原文:使用Docker快速部署ELK分析Nginx日志实践 一.背景 笔者所在项目组的项目由多个子项目所组成,每一个子项目都存在一定的日志,有时候想排查一些问题,需要到各个地方去查看,极为不方便,此前 ...
- WXSS学习
<view class='container'> <button type='default'>测试</button> <button type='defau ...
- vue $emit 子传父
我们使用子组件传递值给父组件使用 $emit 代码 <!DOCTYPE html> <html lang="en"> <head> <me ...
- 解决 php artisan route:list 报错oauth-private.key文件不存在或不可读的
进入项目根目录命令行执行 php artisan passport:install 然后执行php artisan route:list,会提示 Class App\Http\Controllers\ ...
- delphi 单元 MSHTML 之Ihtmldocument2
delphi : Ihtmldocument2接口的利用 MSHTML是微软公司的一个COM组件,该组件封装了HTML语言中的所有元素及其属性,穿越其供给的规范接口,能够访问指定网页的所有元素. MS ...
- 2019.7.3模拟 光影交错(穷举+概率dp)
题目大意: 每一轮有pl的概率得到正面的牌,pd的概率得到负面的牌,1-pl-pd的概率得到无属性牌. 每一轮过后,都有p的概率结束游戏,1-p的概率开始下一轮. 问期望有多少轮后正面的牌数严格大于负 ...
- thinkphp 模板文件
因为模板文件中可能会泄露数据表的字段信息,有两种方法可以保护你的模板文件不被访问到: 第一种方式是配置.htaccess文件,针对Apache服务器而言. 大理石平台厂家 把以下代码保存在模块的模板目 ...
- web前端开发2018年12月找工作总结
2018年的冬天额外的冷,由内致外... 作为一名刚刚踏入社会的实习生,可谓是狠狠的体验了一把什么叫社会(同时也感叹父母赚钱真的很不容易) 前几天看见这样一句话"如果你不知道社会的辛苦,要么 ...
- css清除浮动的几种方法
推荐几种好用的清除浮动方法: 方法1: .clearfix:after { content:"."; display:block; height:; clear:both; vis ...
- kafaka集群部署
1.集群规划 kafka集群配置是依赖zookeeper的,所以需要保证先安装了zookeeper和jdk注意:kafka内自带zookeeper,我们不使用自带的. hadoop101 hadoop ...