目录java
前言:Java 中的异常处理是处理程序运行错误时的强大机制之一,它能够保证应用程序的正常流程。程序员
首先咱们将了解java异常、异常的类型以及受查和非受查异常之间的区别。数组
字面意义:异常是一种不正常的状况。安全
在 java 中,异常是扰乱程序正常流程的事件,它是在程序运行时抛出的对象。学习
异常处理一种在运行时解决程序错误的机制,例如 ClassNotFound、IO、SQL、Remote 等。3d
异常一般会干扰程序的正常流程,而异常处理的核心优点是维护程序的正常流程。如今让咱们假设一下:指针
statement 1; statement 2; statement 3; statement 4; statement 5;//发生异常 statement 6; statement 7; statement 8; statement 9; statement 10;
假设你的程序中有10条语句,若是在第5条中出现了一个异常,那么语句6-10将不会继续执行。若是你使用了异常处理,那么语句6-10的部分将正常执行,这就是咱们为何须要在程序中使用异常处理的缘由。调试
你知道吗?code
- 受查和非受查异常之间的区别是什么?
- 代码
int data=50/0;
后面发生了什么?- 为何须要使用多个
catch
块?finally
块是否有可能不执行?- 什么是异常传递?
throw
和throws
关键字之间的区别?- 对方法重写使用异常处理的4条规则是什么?
如今让咱们带着以上问题继续下面的学习。orm
主要有两种类型的异常:受查和非受查异常,Error
被视为非受查异常。Sun公司认为有三种异常类型:
1)受查异常
除了RuntimeException
和Error
外,继承自Throwable
类的类称为受查异常,例如:IOException、SQLException 等。受查异常在编译时进行检查。
常见的有如下几个方面:
2)非受查异常
继承自RuntimeException
类的异常被称为非受查异常,例如:ArithmeticException、 NullPointerException、 ArrayIndexOutOfBoundsException 等。非受查异常不会在编译时检查,而是在运行时进行检查。
常见的有如下几个方面:
“若是出现了RuntimeException
异常,那么必定是你自身的问题”,是一条至关有道理的规则。
3)错误(Error)
错误是一种没法恢复的异常类型,一般是在java运行时系统的内部错误和资源耗尽错误。应用程序不该该抛出这种类型的对象。若是出现了这样的内部错误,除了通告给用户,并尽力的使得程序安全的终止以外,再也无能为力了。这种状况不多出现。
在某些状况下,可能出现未检查的异常,它们以下:
1)发生ArithmeticException
的场景
若是咱们将任何数字除以0,就会出现一个 ArithmeticException 异常。
int a = 50/0;//ArithmeticException
2)发生NullPointerException
的场景
若是变量的值为null
,那么调用此变量将会出现 NullPointerException 异常。
String s = null; System.out.println(s.length());//NullPointerException
3)发生NumberFormatException
的场景
任何值的格式错误,都有肯能发生 NumberFormatException 异常。假设一个字符串变量,其中包含了字符,若将此变量转换为数字类型,将会发生 NumberFormatException 异常。
String s = "abc"; int i = Integer.parseInt(s);//NumberFormatException
4)发生ArrayIndexOutOfBoundsException
的场景
若是你在一个不存在的的数组索引中插入任何值,则会致使 ArrayIndexOutOfBoundsException 异常。
int a[] = new int[5]; a[10] = 50; //ArrayIndexOutOfBoundsException
下面是 Java 异常处理中的5个关键字:
try
、catch
、finally
、throw
、throws
在程序中,可能会遇到任何标准异常类都没有可以充分地描述清楚的问题。在这种状况下,建立本身的异常类就是一件瓜熟蒂落的事情了。咱们须要作的只是定义一个派生于 Exception 的类,或者派生于 Exception 子类的类。例如,定义一个派生于 IOException 的类。
习惯上,定义的类应该包含两个构造器,一个是默认构造器,一个是描述详细信息的的构造器(超类 Throwable 的 toString 方法将会打印出这些详细信息,这在调试中很是有用。)
示例以下:
class FileFormatException extends IOException { public FileFormatException() {} public FileFormatException(String gripe) { super(gripe); } }
如今,就能够抛出本身定义的异常类型了。
String readData(BufferedReader in) throws FileFormatException { ... while (...) { // EOF encountered if (ch == -1) { if (n < len) throw new FileFormatException(); } ... } return s; }
将可能发生异常的代码放在try
块中,且必须在方法中才能使用。try 块后必须使用catch
块或finally
块。
1)try-catch 语法
try{ // 可能抛出异常的代码 }catch(Exception_class_Name ref){}
2)try-finally 语法
try{ // 可能抛出异常的代码 }finally{}
Java catch
块被用于处理异常,必须在try
块后使用。
你能够在一个try
块后使用多个catch
块
若是咱们不使用try-catch
处理异常,看看会发生什么。
public class Testtrycatch1 { public static void main(String args[]) { int data=50/0;// 可能抛出异常 System.out.println("代码的其他部分..."); } }
输出:
Exception in thread main java.lang.ArithmeticException:/ by zero
如上面的示例所示,代码的其他部分并无执行。("代码的其他部分..."未打印)
让咱们经过try-catch
块来查看上述问题的解决方案。
public class Testtrycatch2 { public static void main(String args[]) { try { int data = 50/0; } catch(ArithmeticException e) { System.out.println(e); } System.out.println("代码的其他部分..."); } }
输出:
Exception in thread main java.lang.ArithmeticException:/ by zero 代码的其他部分...
如今,正如上面的示例所示,代码的其他部分执行了.(也就是"代码的其他部分..."被打印)
Java 虚拟机首先检查异常是否被处理,若是异常未处理,则执行的一个默认的异常处理程序:
若是程序员处理了异常,则应用程序按照正常流程执行。
若是须要在发生不一样异常时执行不一样的任务,则须要使用多个 catch 块。
查看下面一个简单的多 catch 块示例。
public class TestMultipleCatchBlock{ public static void main(String args[]) { try{ int a[] = new int[5]; a[5] = 30/0; } catch(ArithmeticException e) { System.out.println("任务1已完成"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("任务2已完成"); } catch(Exception e) { System.out.println("已完成通用任务"); } System.out.println("代码的其他部分..."); } }
输出:
任务1已完成 代码的其他部分...
规则:一次只有一个异常发生,而且一次只执行一个catch块。
规则: 全部异常必须从最具体到最通用的顺序排序,即捕获
ArithmeticException
必须在捕获Exception
以前发生。
class TestMultipleCatchBlock1 { public static void main(String args[]) { try{ int a[]=new int[5]; a[5]=30/0; } catch(Exception e) { System.out.println("已完成通用任务"); } catch(ArithmeticException e) { System.out.println("任务1已完成"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("任务2已完成"); } System.out.println("代码的其他部分..."); } }
输出:
Compile-time error
Java try块中的try块被称为try嵌套块。
有时可能会出现一种状况,一个块的某个部分可能致使一个错误,而整个块的自己可能会致使另外一个错误。在这种状况下,必须使用嵌套异常处理程序。
语法:
.... try { statement 1; statement 2; try { statement 1; statement 2; } catch(Exception e) { ... } } catch(Exception e) {...} ....
class Excep6 { public static void main(String args[]) { try { // try 嵌套块1 try { System.out.println("try 嵌套块1"); int b = 39 / 0; } catch(ArithmeticException e) { System.out.println(e); } // try 嵌套块2 try { int a[] = new int[5]; a[5] = 4; } catch(ArrayIndexOutOfBoundsException e) { System.out.println(e); } System.out.println("try外部块其余语句..."); } catch(Exception e) { System.out.println("handeled"); } System.out.println("正常流..."); } }
输出:
try 嵌套块1 java.lang.ArithmeticException: / by zero java.lang.ArrayIndexOutOfBoundsException: 5 try外部块其余语句... 正常流...
Java finally 块是用来执行重要代码的块(如关闭链接、流等)。
不管是否处理异常,最终都会执行 finally 块。
finally 块紧跟 try 或 catch 块后:
注意:不管你是否处理异常,在终止程序以前,JVM都将执行finally块(若是存在的话)
finally 块能够用于放置"clear"代码,例如关闭文件,关闭链接等。
接下来让咱们来看看在不一样状况下使用 finally 块。
1)案例1
当前没有发生异常:
class TestFinallyBlock { public static void main(String[] args) { try { int data = 25 / 5; System.out.println(data); } catch (NullPointerException e) { System.out.println(e); } finally { System.out.println("finally 块老是执行"); } System.out.println("代码的其他部分..."); } }
输出:
5 finally 块老是执行 代码的其他部分...
2)案例2
发生异常但未处理:
class TestFinallyBlock1 { public static void main(String[] args) { try { int data = 25 / 0; System.out.println(data); } catch (NullPointerException e) { System.out.println(e); } finally { System.out.println("finally 块老是执行"); } System.out.println("代码的其他部分..."); } }
输出:
finally 块老是执行 Exception in thread main java.lang.ArithmeticException:/ by zero
3)案例3
发生异常并处理异常:
public class TestFinallyBlock2 { public static void main(String args[]) { try { int data = 25 / 0; System.out.println(data); } catch(ArithmeticException e) { System.out.println(e); } finally { System.out.println("finally 块老是执行"); } System.out.println("代码的其他部分..."); } }
输出:
Exception in thread main java.lang.ArithmeticException:/ by zero finally 块老是执行 代码的其他部分...
规则:对于 try 块能够有0个或多个 catch 块,但仅仅只能有一个 finally 块。
规则:若是程序退出(经过调用 System.exit() 或经过致使进程停止的致命错误),finally块将不会被执行。
Java throw 关键字用于显示的抛出异常。
咱们可使用 throw 关键字在 Java 中抛出检查(Checked)或未检查(UnChecked)异常。throw 关键字主要用于抛出自定义异常。
Java throw 语法以下:
throw exception;
抛出IOException
异常的例子:
throw new IOException("sorry device error");
在本例中,咱们建立了一个将整数值做为参数的 validate 方法。若是年龄小于18岁,咱们将抛出一个ArithmeticException
异常,不然打印一条消息"欢迎投票"。
public class TestThrow1 { static void validate(int age) { if(age < 18) throw new ArithmeticException("无效"); else System.out.println("欢迎投票"); } public static void main(String args[]) { validate(13); System.out.println("代码的其他部分..."); } }
输出:
Exception in thread main java.lang.ArithmeticException:无效
异常首先从堆栈顶部抛出,若是未捕获,则将调用堆栈降低到前一个方法,若是没有捕获,则将异常再次降低到先前的方法,以此类推,知道它们被捕获或到达调用堆栈底部为止。以上称为异常传递。
规则:默认状况下,非受查异常在调用链中(传递)转发。
异常传递示例:
class TestExceptionPropagation1 { void m(){ int data = 50 / 0; } void n() { m(); } void p() { try{ n(); } catch(Exception e) { System.out.println("异常处理器"); } } public static void main(String args[]) { TestExceptionPropagation1 obj = new TestExceptionPropagation1(); obj.p(); System.out.println("正常流..."); } }
输出:
异常处理器 正常流...
在上面的示例中。异常发生在 m() 方法中,若是未对其进行处理,则将其传递到未处理它的前 n() 方法,再次将其传递处处理异常的 p() 方法。
能够在 main()、p()、n()、p()、 m() 中的任何方法中处理异常。
规则:默认状况下,受查异常不会在调用链中(传递)转发。
用于描述受查异常不会在程序中传递的示例:
class TestExceptionPropagation2{ void m(){ throw new java.io.IOException("设备异常"); // 受查异常 } void n(){ m(); } void p(){ try{ n(); } catch(Exception e){ System.out.println("异常处理器"); } } public static void main(String args[]){ TestExceptionPropagation2 obj=new TestExceptionPropagation2(); obj.p(); System.out.println("正常流..."); } }
输出:
Compile Time Error
编译时发生一个错误,证实受查异常并不会在程序中进行传递。
Java throws
关键字被用于声明一个异常。它给程序员提供了一个信息,说明可能会发生异常,因此程序员最好提供异常处理代码,以保证程序正常的流程。
异常处理主要用于处理受查异常,若是出现任何非受查异常,如"NullPointerException",都是程序员自身的错误,请认真检查你的代码。
return_type method_name() throws exception_class_name { // method code }
仅仅声明受查异常,由于:
VirtualMachineError
或 StackOverflowError
等异常,将没法进行任何操做。使用 throws 声明受查异常后,使得受查异常能够在调用堆栈中进行(传递)转发。它向处理该异常的方法提供异常信息。
下面的示例描述了受查异常能够经过throws
关键字进行传递:
import java.io.IOException; class Testthrows1{ void m() throws IOException{ throw new IOException("设备异常"); // 受查异常 } void n()throws IOException{ m(); } void p(){ try{ n(); } catch(Exception e){ System.out.println("异常处理器"); } } public static void main(String args[]){ Testthrows1 obj=new Testthrows1(); obj.p(); System.out.println("正常流..."); } }
输出:
异常处理器 正常流...
规则:若是你正在调用一个声明了异常的方法,则必须捕获或声明异常。
如今有两种状况:
1) 状况1:处理了异常
import java.io.*; class M{ void method() throws IOException{ throw new IOException("设备异常"); } } public class Testthrows2{ public static void main(String args[]){ try{ M m = new M(); m.method(); } catch(Exception e){ System.out.println("异常处理器"); } System.out.println("正常流..."); } }
输出:
异常处理器 正常流...
2) 状况2:声明了异常
A)声明了异常但未发生异常:
import java.io.*; class M{ void method()throws IOException{ System.out.println("执行设备操做"); } } class Testthrows3{ public static void main(String args[])throws IOException{ // 声明了异常 M m=new M(); m.method(); System.out.println("正常流..."); } }
输出:
执行设备操做 正常流...
B)声明了异常且发生了异常:
import java.io.*; class M{ void method()throws IOException{ throw new IOException("设备错误"); } } class Testthrows4{ public static void main(String args[])throws IOException{ // 声明了异常 M m=new M(); m.method(); System.out.println("正常流..."); } }
输出:
Runtime Exception
程序编译时将直接出现了一个编译错误。
No. | throw | throws |
---|---|---|
1) | Java throw 关键字用于显示的抛出异常 | Java throws 关键字用于声明一个异常 |
2) | 受查异常不能只使用 throw 进行传递 | 受查异常能够经过 throws 进行传递 |
3) | Throw 后面跟着一个异常实例 | Throws 后面跟着一个异常类 |
4) | 在方法中使用 Throw | Throws 与方法签名一块儿使用 |
5) | 你不能抛出多个异常 | 你能够声明多个异常,例如public void method() throws IOException,SQLException |
1)Java throw 示例:
void m(){ throw new ArithmeticException("sorry"); }
2)Java throws 示例:
void m()throws ArithmeticException{ // method code }
3)Java throw 和 throws 示例:
void m()throws ArithmeticException{ throw new ArithmeticException("sorry"); }
答案固然是能够的,能够在 catch 块中抛出相同的异常。这种方法一般用于只想记录一个异常,但不作任何改变。
代码示例:
try { // access the database } catch (Exceptiom e) { logger.log(level, message, e); throw e; }
在 Java SE7 以前,这种方法存在一个问题,假设这段代码在如下方法中:
public void updateRecord() throws SQLException
Java 编译器查看 catch 块中的 throw 语句,而后查看 e 的类型,会指出这个方法能够抛出任何 Exception 而不只仅是 SQLException。如今这个问题已经有所改进,编译器会追踪到 e 来自 try 块。假设这个 try 块仅有的受查异常是 SQLException 实例,另外,假设 e 在 catch 块中未改变,将外围方法声明为 throws SQLException 是合法的。
Final 和 Finally 和 Finalize 三者之间的差别以下:
No. | final | finally | finalize |
---|---|---|---|
1) | final 用于对类、方法和变量加以限制,final 类不能被继承,final 方法不能被重写,final 变量不能被更改 | finally 用于放置重要的代码,不管异常是否被处理它都会执行 | finalize 用于在对象被垃圾回收以前执行清理操做 |
2) | final 是一个关键字 | finally 是一个块 | finalize 是一个方法 |
1)Java final 示例:
class FinalExample{ public static void main(String[] args){ final int x = 100; x = 200; // final 修饰的变量不能被更改 // 编译时将出错 } }
2)Java finally 示例:
class FinallyExample{ public static void main(String[] args){ try{ int x = 300; } catch(Exception e){ System.out.println(e); } finally{ System.out.println("finally 块始终被执行"); } } }
3)Java finalize 示例:
class FinalizeExample{ public void finalize(){ System.out.println("finalize called"); } public static void main(String[] args){ FinalizeExample f1 = new FinalizeExample(); FinalizeExample f2 = new FinalizeExample(); f1 = null; f2 = null; System.gc(); } }
关于重写异常处理方法的规则以下:
1)若是超类方法没有声明异常
超类方法未声明异常,子类重写方法声明受查异常的示例:
import java.io.*; class Parent{ void msg(){ System.out.println("parent"); } } class TestExceptionChild extends Parent{ void msg() throws IOException{ System.out.println("Child"); } public static void main(String args[]){ Parent p = new TestExceptionChild(); p.msg(); } }
输出:
Compile Time Error
超类方法未声明异常,子类重写方法声明非受查异常的示例:
import java.io.*; class Parent{ void msg(){ System.out.println("parent"); } } class TestExceptionChild1 extends Parent{ void msg() throws ArithmeticException{ System.out.println("child"); } public static void main(String args[]){ Parent p = new TestExceptionChild1(); p.msg(); } }
输出:
child
2)若是超类方法声明了异常
A)超类方法声明了异常,子类重写方法声明不相同父类异常的示例:
import java.io.*; class Parent{ // 声明了子类异常 void msg() throws ArithmeticException{ System.out.println("parent"); } } class TestExceptionChild2 extends Parent{ // 声明了父类异常 void msg() throws Exception{ System.out.println("child"); } public static void main(String args[]){ Parent p = new TestExceptionChild2(); try{ p.msg(); } catch(Exception e){ } } }
输出:
Compile Time Error
B)超类方法声明了异常,子类重写方法声明相同异常的示例:
import java.io.*; class Parent{ void msg()throws Exception{ System.out.println("parent"); } } class TestExceptionChild3 extends Parent{ void msg()throws Exception{ System.out.println("child"); } public static void main(String args[]){ Parent p=new TestExceptionChild3(); try{ p.msg(); } catch(Exception e){ } } }
输出:
child
C)超类方法声明了异常,子类重写方法声明不相同子类异常的示例:
import java.io.*; class Parent{ // 声明了父类异常 void msg()throws Exception{ System.out.println("parent"); } } class TestExceptionChild4 extends Parent{ // 声明了子类异常 void msg()throws ArithmeticException{ System.out.println("child"); } public static void main(String args[]){ Parent p=new TestExceptionChild4(); try{ p.msg(); } catch(Exception e){ } } }
输出:
child
D)超类方法声明了异常,子类重写方法未声明异常的示例:
import java.io.*; class Parent{ void msg()throws Exception{ System.out.println("parent"); } } class TestExceptionChild5 extends Parent{ void msg(){ System.out.println("child"); } public static void main(String args[]){ Parent p=new TestExceptionChild5(); try{ p.msg(); } catch(Exception e){ } } }
输出:
child
参考文章:https://www.javatpoint.com/exception-handling-in-java 参考书籍:《Java核心技术 卷1》