本文首发自「慕课网」,想理解更多 IT 干货内容,程序员圈内热闻,欢送关注!
作者 | 慕课网精英讲师 ColorfulC
Java 的异样解决是 Java 语言的一大重要个性,也是进步代码健壮性的最弱小办法之一。当咱们编写了谬误的代码时,编译器在编译期间可能会抛出异样,有时候即便编译失常,在运行代码的时候也可能会抛出异样。本篇文章咱们将介绍什么是异样、Java 中异样类的架构、如何进行异样解决、如何自定义异样、什么是异样链、如何应用异样链等内容。1. 什么是异样异样就是程序上的谬误,咱们在编写程序的时候常常会产生谬误,这些谬误划分为编译期间的谬误和运行期间的谬误。上面咱们来看几个常见的异样案例。如果语句漏写分号,程序在编译期间就会抛出异样,实例如下:public class Hello {
public static void main(String[] args) {System.out.println("Hello World!")
}
}
代码块 12345 运行后果:$ javac Hello.java
Hello.java:3: 谬误: 须要 ’;’
System.out.println("Hello World!")
^
1 个谬误
代码块 12345 运行过程:
因为代码的第 3 行语句漏写了分号,Java 编译器给出了明确的提醒。static 关键字写成了 statci,实例如下:Hello.java:2: 谬误: 须要 < 标识符 >
public statci void main(String[] args) {^
1 个谬误
代码块 1234 当数组下标越界,程序在编译阶段不会产生谬误,但在运行时会抛出异样。实例如下:public class ArrayOutOfIndex {
public static void main(String[] args) {int[] arr = {1, 2, 3};
System.out.println(arr[3]);
}
}
代码块 123456 运行后果:Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at ArrayOutOfIndex.main(ArrayOutOfIndex.java:4)
代码块 12 运行过程:
-
Java 异样类架构在 Java 中,通过 Throwable 及其子类来形容各种不同类型的异样。如下是 Java 异样类的架构图(不是全副,只展现局部类):
2.1 Throwable 类 Throwable 位于 java.lang 包下,它是 Java 语言中所有谬误(Error)和异样(Exception)的父类。Throwable 蕴含了其线程创立时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。次要办法:fillInStackTrace:用以后的调用栈档次填充 Throwable 对象栈档次,增加到栈档次任何先前信息中;getMessage:返回对于产生的异样的详细信息。这个音讯在 Throwable 类的构造函数中初始化了;getCause:返回一个 Throwable 对象代表异样起因;getStackTrace:返回一个蕴含堆栈档次的数组。下标为 0 的元素代表栈顶,最初一个元素代表办法调用堆栈的栈底;printStackTrace:打印 toString() 后果和栈档次到 System.err,即谬误输入流。2.2 Error 类 Error 是 Throwable 的一个间接子类,它能够批示正当的应用程序不应该尝试捕捉的重大问题。这些谬误在应用程序的管制和解决能力之外,编译器不会查看 Error,对于设计正当的应用程序来说,即便产生了谬误,实质上也无奈通过异样解决来解决其所引起的异样情况。常见 Error:AssertionError:断言谬误;VirtualMachineError:虚拟机谬误;UnsupportedClassVersionError:Java 类版本谬误;OutOfMemoryError:内存溢出谬误。2.3 Exception 类 Exception 是 Throwable 的一个间接子类。它批示正当的应用程序可能心愿捕捉的条件。Exception 又包含 Unchecked Exception(非查看异样)和 Checked Exception(查看异样)两大类别。2.3.1 Unchecked Exception(非查看异样)Unchecked Exception 是编译器不要求强制解决的异样,蕴含 RuntimeException 以及它的相干子类。咱们编写代码时即便不去解决此类异样,程序还是会编译通过。常见非查看异样:NullPointerException:空指针异样;ArithmeticException:算数异样;ArrayIndexOutOfBoundsException:数组下标越界异样;ClassCastException:类型转换异样。2.3.2 Checked Exception(查看异样)Checked Exception 是编译器要求必须解决的异样,除了 RuntimeException 以及它的子类,都是 Checked Exception 异样。咱们在程序编写时就必须解决此类异样,否则程序无奈编译通过。常见查看异样:IOException:IO 异样 SQLException:SQL 异样 3. 如何进行异样解决在 Java 语言中,异样解决机制能够分为两局部:抛出异样:当一个办法产生谬误时,会创立一个异样对象,并交给运行时零碎解决;捕捉异样:在办法抛出异样之后,运行时零碎将转为寻找适合的异样处理器。Java 通过 5 个关键字来实现异样解决,别离是:throw、throws、try、catch、finally。异样总是先抛出,后捕捉的。上面咱们将围绕着 5 个关键字来具体解说如何抛出异样以及如何捕捉异样。4. 抛出异样 4.1 实例咱们先来看一个除零异样的实例代码:public class ExceptionDemo1 {
// 打印 a / b 的后果
public static void divide(int a, int b) {System.out.println(a / b);
}
public static void main(String[] args) {
// 调用 divide() 办法 divide(2, 0);
}
}
代码块 1234567891011 运行后果:Exception in thread “main” java.lang.ArithmeticException: / by zero
at ExceptionDemo1.divide(ExceptionDemo1.java:4)
at ExceptionDemo1.main(ExceptionDemo1.java:9)
代码块 123 运行过程:咱们晓得 0 是不能用作除数的,因为 divide() 办法中除数 b 为 0,所以代码将进行执行并显示了相干的异样信息,此信息为堆栈跟踪,下面的运行后果通知咱们:main 线程产生了类型为 ArithmeticException 的异样,显示音讯为 by zero,并且提醒了可能产生异样的办法和行号。4.2 throw 下面的实例中,程序在运行时引发了谬误,那么如何来显示抛出(创立)异样呢?咱们能够应用 throw 关键字来抛出异样,throw 关键字前面跟异样对象,改写下面的实例代码:public class ExceptionDemo2 {
// 打印 a / b 的后果
public static void divide(int a, int b) {if (b == 0) { // 抛出异样 throw new ArithmeticException("除数不能为零"); } System.out.println(a / b);
}
public static void main(String[] args) {
// 调用 divide() 办法 divide(2, 0);
}
}
代码块 123456789101112131415 运行后果:Exception in thread “main” java.lang.ArithmeticException: 除数不能为零
at ExceptionDemo2.divide(ExceptionDemo2.java:5)
at ExceptionDemo2.main(ExceptionDemo2.java:12)
代码块 123 运行过程:代码在运行时同样引发了谬误,但显示音讯为“除数不能为零”。咱们看到 divide() 办法中退出了条件判断,如果调用者将参数 b 设置为 0 时,会应用 throw 关键字来抛出异样,throw 前面跟了一个应用 new 关键字实例化的算数异样对象,并且将音讯字符串作为参数传递给了算数异样的构造函数。咱们能够应用 throw 关键字抛出任何类型的 Throwable 对象,它会中断办法,throw 语句之后的所有内容都不会执行。除非曾经解决抛出的异样。异样对象不是从办法中返回的,而是从办法中抛出的。4.3 throws 能够通过 throws 关键字申明办法要抛出何种类型的异样。如果一个办法可能会出现异常,然而没有能力解决这种异样,能够在办法申明处应用 throws 关键字来申明要抛出的异样。例如,汽车在运行时可能会呈现故障,汽车自身没方法解决这个故障,那就让开车的人来解决。throws 用在办法定义时申明该办法要抛出的异样类型,如下是伪代码:public void demoMethod() throws Exception1, Exception2, … ExceptionN {
// 可能产生异样的代码
}
代码块 123throws 前面跟的异样类型列表能够有一个也能够有多个,多个则以 , 宰割。当办法产生异样列表中的异样时,将把异样抛向办法的调用方,由调用方解决。throws 有如下应用规定:如果办法中全副是非查看异样(即 Error、RuntimeException 以及的子类),那么能够不应用 throws 关键字来申明要抛出的异样,编译器可能通过编译,但在运行时会被零碎抛出;如果办法中可能呈现查看异样,就必须应用 throws 申明将其抛出或应用 try catch 捕捉异样,否则将导致编译谬误;当一个办法抛出了异样,那么该办法的调用者必须解决或者从新抛出该异样;当子类重写父类抛出异样的办法时,申明的异样必须是父类所申明异样的同类或子类。5. 捕捉异样应用 try 和 catch 关键字能够捕捉异样。try catch 代码块放在异样可能产生的中央。它的语法如下:try {
// 可能会产生异样的代码块
} catch (Exception e1) {
// 捕捉并解决 try 抛出的异样类型 Exception
} catch (Exception2 e2) {
// 捕捉并解决 try 抛出的异样类型 Exception2
} finally {
// 无论是否产生异样,都将执行的代码块
}
代码块 123456789 咱们来看一下下面语法中的 3 种语句块:try 语句块:用于监听异样,当产生异样时,异样就会被抛出;catch 语句块:catch 语句蕴含要捕捉的异样类型的申明,当 try 语句块产生异样时,catch 语句块就会被查看。当 catch 块尝试捕捉异样时,是依照 catch 块的申明程序从上往下寻找的,一旦匹配,就不会再向下执行。因而,如果同一个 try 块下的多个 catch 异样类型有父子关系,应该将子类异样放在后面,父类异样放在前面;finally 语句块:无论是否产生异样,都会执行 finally 语句块。finally 罕用于这样的场景:因为 finally 语句块总是会被执行,所以那些在 try 代码块中关上的,并且必须回收的物理资源(如数据库连贯、网络连接和文件),个别会放在 finally 语句块中开释资源。try 语句块后能够接零个或多个 catch 语句块,如果没有 catch 块,则必须跟一个 finally 语句块。简略来说,try 不容许独自应用,必须和 catch 或 finally 组合应用,catch 和 finally 也不能独自应用。实例如下:public class ExceptionDemo3 {
// 打印 a / b 的后果
public static void divide(int a, int b) {System.out.println(a / b);
}
public static void main(String[] args) {
try { // try 语句块 // 调用 divide() 办法 divide(2, 0); } catch (ArithmeticException e) { // catch 语句块 System.out.println("catch: 产生了算数异样:" + e); } finally { // finally 语句块 System.out.println("finally: 无论是否产生异样,都会执行"); }
}
}
代码块 1234567891011121314151617181920 运行后果:catch: 产生了算数异样:java.lang.ArithmeticException: / by zero
finally: 无论是否产生异样,都会执行
代码块 12 运行过程:divide() 办法中除数 b 为 0,会产生除零异样,咱们在办法调用处应用了 try 语句块对异样进行捕捉;如果捕捉到了异样,catch 语句块会对 ArithmeticException 类型的异样进行解决,此处打印了一行自定义的提醒语句;最初的 finally 语句块,无论产生异样与否,总会执行。Java 7 当前,catch 多种异样时,也能够像上面这样简化代码:try {
// 可能会产生异样的代码块
} catch (Exception | Exception2 e) {
// 捕捉并解决 try 抛出的异样类型
} finally {
// 无论是否产生异样,都将执行的代码块
}
代码块 12345676. 自定义异样自定义异样,就是定义一个类,去继承 Throwable 类或者它的子类。Java 内置了丰盛的异样类,通常应用这些内置异样类,就能够形容咱们在编码时呈现的大部分异常情况。一旦内置异样无奈满足咱们的业务要求,就能够通过自定义异样形容特定业务产生的异样类型。实例:public class ExceptionDemo4 {static class MyCustomException extends RuntimeException {
/** * 无参构造方法 */ public MyCustomException() {super("我的自定义异样"); }
}
public static void main(String[] args) {
// 间接抛出异样 throw new MyCustomException();
}
}
代码块 12345678910111213141516 运行后果:Exception in thread “main” ExceptionDemo4$MyCustomException: 我的自定义异样
at ExceptionDemo4.main(ExceptionDemo4.java:13)
代码块 12 运行过程:在代码中写了一个自定义异样 MyCustomException,继承自 RuntimeException,它是一个动态外部类,这样在主办法中就能够间接抛出这个异样类了。当然,也能够应用 catch 来捕捉此类型异样。7. 异样链异样链是以一个异样对象为参数结构新的异样对象,新的异样对象将蕴含先前异样的信息。简略来说,就是将异样信息从底层传递给下层,逐层抛出,咱们来看一个实例:public class ExceptionDemo5 {
/**
- 第一个自定义的动态外部异样类
*/
static class FirstCustomException extends Exception {
// 无参构造方法 public FirstCustomException() {super("第一个异样"); }
}
/**
- 第二个自定义的动态外部异样类
*/
static class SecondCustomException extends Exception {
public SecondCustomException() {super("第二个异样"); }
}
/**
- 第三个自定义的动态外部异样类
*/
static class ThirdCustomException extends Exception {
public ThirdCustomException() {super("第三个异样"); }
}
/**
- 测试异样链静态方法 1,间接抛出第一个自定义的动态外部异样类
- @throws FirstCustomException
*/
public static void f1() throws FirstCustomException {
throw new FirstCustomException();
}
/**
- 测试异样链静态方法 2,调用 f1()办法,并抛出第二个自定义的动态外部异样类
- @throws SecondCustomException
*/
public static void f2() throws SecondCustomException {
try {f1(); } catch (FirstCustomException e) {throw new SecondCustomException(); }
}
/**
- 测试异样链静态方法 3,调用 f2()办法,并抛出第三个自定义的动态外部异样类
- @throws ThirdCustomException
*/
public static void f3() throws ThirdCustomException {
try {f2(); } catch (SecondCustomException e) {throw new ThirdCustomException(); }
}
public static void main(String[] args) throws ThirdCustomException {
// 调用静态方法 f3() f3();
}
}
代码块 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 运行后果:Exception in thread “main” ExceptionDemo5$ThirdCustomException: 第三个异样
at ExceptionDemo5.f3(ExceptionDemo5.java:46)
at ExceptionDemo5.main(ExceptionDemo5.java:51)
代码块 123 运行过程:通过运行后果,咱们只获取到了静态方法 f3() 所抛出的异样堆栈信息,后面代码所抛出的异样并没有被显示。咱们改写下面的代码,让异样信息以链条的形式“连贯”起来。能够通过改写自定义异样的构造方法,来获取到之前异样的信息。实例如下:/**
- @author colorful@TaleLin
*/
public class ExceptionDemo6 {
/**
- 第一个自定义的动态外部异样类
*/
static class FirstCustomException extends Exception {
// 无参构造方法 public FirstCustomException() {super("第一个异样"); }
}
/**
- 第二个自定义的动态外部异样类
*/
static class SecondCustomException extends Exception {
/** * 通过构造方法获取之前异样的信息 * @param cause 捕捉到的异样对象 */ public SecondCustomException(Throwable cause) {super("第二个异样", cause); }
}
/**
- 第三个自定义的动态外部异样类
*/
static class ThirdCustomException extends Exception {
/** * 通过构造方法获取之前异样的信息 * @param cause 捕捉到的异样对象 */ public ThirdCustomException(Throwable cause) {super("第三个异样", cause); }
}
/**
- 测试异样链静态方法 1,间接抛出第一个自定义的动态外部异样类
- @throws FirstCustomException
*/
public static void f1() throws FirstCustomException {
throw new FirstCustomException();
}
/**
- 测试异样链静态方法 2,调用 f1()办法,并抛出第二个自定义的动态外部异样类
- @throws SecondCustomException
*/
public static void f2() throws SecondCustomException {
try {f1(); } catch (FirstCustomException e) {throw new SecondCustomException(e); }
}
/**
- 测试异样链静态方法 3,调用 f2()办法,并抛出第三个自定义的动态外部异样类
- @throws ThirdCustomException
*/
public static void f3() throws ThirdCustomException {
try {f2(); } catch (SecondCustomException e) {throw new ThirdCustomException(e); }
}
public static void main(String[] args) throws ThirdCustomException {
// 调用静态方法 f3() f3();
}
}
代码块 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182 运行后果:Exception in thread “main” ExceptionDemo6$ThirdCustomException: 第三个异样
at ExceptionDemo6.f3(ExceptionDemo6.java:74)
at ExceptionDemo6.main(ExceptionDemo6.java:80)
Caused by: ExceptionDemo6$SecondCustomException: 第二个异样
at ExceptionDemo6.f2(ExceptionDemo6.java:62)
at ExceptionDemo6.f3(ExceptionDemo6.java:72)
… 1 more
Caused by: ExceptionDemo6$FirstCustomException: 第一个异样
at ExceptionDemo6.f1(ExceptionDemo6.java:51)
at ExceptionDemo6.f2(ExceptionDemo6.java:60)
… 2 more
代码块 1234567891011 运行过程:通过运行后果,咱们看到,异样产生的整个过程都打印到了屏幕上,这就是一个异样链。8. 小结通过本篇文章,咱们晓得了异样就是程序上的谬误,良好的异样解决能够进步代码的健壮性。Java 语言中所有谬误(Error)和异样(Exception)的父类都是 Throwable。Error 和 Exception 是 Throwable 的间接子类,咱们通常说的异样解决实际上就是解决 Exception 及其子类,异样又分为查看型异样和非查看型异样。通过抛出异样和捕捉异样来实现异样解决。咱们亦能够通过继承 Throwable 类或者它的子类来自定义异样类。通过构造方法获取之前异样的信息能够实现异样链。
- 第一个自定义的动态外部异样类
欢送关注「慕课网」,发现更多 IT 圈优质内容,分享干货常识,帮忙你成为更好的程序员!