一.概述:
异常你可以认为是对象,在xx情况下产生异常对象,异常对象是虚拟机产生的,不是我们来定义的,java中异常也是对象。
异常:中断了正常指令流的事件
异常和语法错误是完全不一样的东西
异常时运行中出现的,而语法错误连编译都是通不过的
二.异常体系:
类图
Throwable():Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。
Error(错误):一般是指java虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常应用程序无法处理这些错误,因此应用程序不应该捕获Error对象,也无须在其throws子句中声明该方法抛出任何Error或其子类。
Exception(异常):是中断了正常指令流的事件,是程序本身可以处理的异常。 Exception 类有一个重要的子类RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundsException。
注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。程序员对error无能为力,只能处理exception,对异常的处理关系到系统的健壮性。
异常对象包含的常用方法:
1.getMessage();返回该异常的详细描述字符
2.printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。
3.printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定的输出流
4.getStackTrace():返回该异常的跟踪栈信息。
java的异常分类
Exception 这种异常分两大类运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。
运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
三.java异常处理机制
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。捕获的异常有两种处理方法:
1.在方法中用try…catch语句捕获并处理异常,catach语句可以有多个,用来匹配多个异常。例如:
public void p(int x){
try{
...
}catch(Exception e){
...
}finally{
...
}
}
try-catch语句还可以包括第三部分,就是finally子句。它表示无论是否出现异常,都应当执行的内容。
2.对于处理不了的异常或者要转型的异常,在方法的声明处通过throw语句抛出异常。例如:
public void test1() throws MyException{
...
if(....){
throw new MyException();
}
}
四.方法1:try cach finally
说明:
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
**try-catch-finally 规则(异常处理语句的语法规则)
1)必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。
2) 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
3) catch 块与相应的异常类的类型相关。
4) 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块
5) 可嵌套 try-catch-finally 结构。
6) 在 try-catch-finally 结构中,可重新抛出异常。
7) 除了下列情况,总将执行 finally 做为结束:JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击。
**try、catch、finally语句块的执行顺序
1)当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
2)当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
3)当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
五.方法2:抛出异常
使用throws抛出异常
使用throws抛出异常的思路是:当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理,如果main方法也不知道应该如何处理这种类型的异常,也可以使用使用throws声明抛出异常,该异常将交给JVM来处理。
JVM对异常的处理方法:打印异常跟踪栈的信息,并终止程序运行,所以有很多程序遇到异常后自动结束。
使用throws抛出异常的格式:
throws声明的抛出的语法格式紧跟在方法之后,可以声明多个异常类,多个异常类之间以逗号隔开。一旦使用了throws语句声明抛出异常,就不用再使用try…catch来捕获异常了。
如:throws ExceptionClass1,ExceptionClass2…
注意点1:如果某段代码调用了一个带throws声明的方法,该方法声明抛出了Checked异常,这表明该方法希望它的调用者来处理该异常。那么这段代码要么放在try块中显示捕获该异常,要么这段代码处于另一个带throws声明抛出的方法中。
//方法一:
import java.io.FileInputStream;
import java.io.IOException;
public class TestException2 {
// test() 方法抛出了异常,那么test()方法的调用者要么放在try块中显示捕获该异常,要么这段代码处于另一个带throws声明抛出的方法中。
// 以下为后者的处理方法
public static void test() throws IOException
{
FileInputStream fis = new FileInputStream("a.txt");
}
public static void main(String[] args) throws Exception
{
test();
}
}
//方法二:
import java.io.FileInputStream;
import java.io.IOException;
public class TestException2 {
public static void test() throws IOException
{
FileInputStream fis = new FileInputStream("a.txt");
}
public static void main(String[] args)
{
try
{
test();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
使用throw抛出异常
如果需要在程序中自行抛出异常,应使用throw语句,throw语句可以单独使用,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。throw语句的格式如下:throw ExceptionInstance;
throw语句抛出异常的两种情况:
1.当throw语句抛出的异常是Checked异常,则该throw语句要么处于try块里显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把异常交给方法的调用者处理。
2.当throw语句抛出的异常是Runtime异常,则该语句无须放在try块内,也无须放在带throws声明抛出的方法中,程序既可以显式使用try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给方法的调用者处理。
public class TestException3 {
public static void throwChecked(int a) throws Exception
{
if (a < 0)
{
/** * 自行抛出Exception异常 改代码必须处于try块里,或处于带throws声明的方法中 */
throw new Exception("a的值大于0,不符合要求");
}
}
public static void throwRuntime(int a)
{
if (a < 0)
{
/** * 自行抛出RuntimeException异常,既可以显式捕获该异常 也可以完全不用理会该异常,把该异常交给方法的调用者处理 */
throw new RuntimeException("a的值大于0,不符合要求");
} else
{
System.out.println("a的值为:" + a);
}
}
public static void main(String[] args)
{
try
{
/** * 此处调用了带throws声明的方法,必须显示捕获该异常(使用try...catch) 否则,要在main方法中再次声明抛出 */
throwChecked(-3);
} catch (Exception e)
{
System.out.println(e.getMessage());
}
throwRuntime(3);
}
}
throw和throws关键字的区别
throw用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。
throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:throws 异常类型1,异常类型2…异常类型n。
throw用在函数内部,后面跟的是异常类对象,throws用在函数后面,后面跟的是异常类
throw
throw是语句抛出异常。它不可以单独使用,要么与try…catch配套使用,要么与throws配套使用。
格式:throw new 异常名(参数);
假设f方法里某语句抛出了A异常,则f方法有两种方式来处理A异常
①throws A 谁调用f方法,谁处理A异常(用try catch),f方法本身不处理A异常
②try{。。。。}catch{}{。。。}f方法本身自己来处理A异常
throws
throws是方法抛出异常。在方法声明中,如果添加了throws子句,我这个函数不对异常进行处理,而是在调用这个函数的地方对异常进行处理。
作用:把运行时期出现的问题转移到编译时期出现。
使用方法:在函数后使用throws声明抛出异常,那么调用者必须处理
处理方式有两种:
1:使用try{} catch(){}处理
2:使用throws继续声明抛出异常。
六.自定义异常
我们不必拘泥于Java中已有的异常类型
Java定义的异常肯定是一些通用的异常,但是我们做项目,不同的项目的需求和问题是不一样的,问题就是异常,这些异常时java没有给我们定义的,所以我们还需要自己定义自己需要的异常。
Java自定义异常的使用要经历如下四个步骤
1、定义一个类继承Throwable或其子类。
2、添加构造方法(当然也可以不用添加,使用默认构造方法)。
3、在某个方法类抛出该异常。
4、捕捉该异常。
创建Exception或者RuntimeException的子类即可得到一个自定义的异常类。例如:
public class MyException extends Exception{
public MyException(){}
public MyException(String smg){
super(smg);
}
}
使用自定义的异常
用throws声明方法可能抛出自定义的异常,并用throw语句在适当的地方抛出自定义的异常。例如:
在某种条件抛出异常
public void test1() throws MyException{
...
if(....){
throw new MyException();
}
}
七.参考资料: