一. 异常原因
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
异常发生的原因有很多,一般包括以下几类:
- 用户输入非法数据
- 要打开的文件不存在
- 网络通信时连接中断或者JVM内存溢出
这些异常有的是程序错误引起的,有的是用户错误引起的吗,还有其他一些物理错误引起的。
二. 异常类型
类关系图如下:
Exception和Error类均是Throwable 类的子类。
异常一般分为3类:
- 检查性异常。 程序员无法预测,在编译时检查出来。如:用户错误或问题 引起的异常,打开一个不存在的文件。
- 运行时异常。 在编译时被忽略。
- 错误 。脱离程序员控制的问题,如栈溢出。
1. 错误-Error
用来指示运行时环境发生的错误。例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
2. Exception类的层次
Exception是使用者发现的异常,Java 程序通常不捕获Error。Error一般发生在严重故障时,它们在Java程序处理的范畴之外。Exception可以范围两类,检查异常和非检查异常。
- 一类是检查型异常/IOException(I/O 输入输出异常)。在Java中所有非 RuntimeException派生的Exception类都是检查型异常,其中 IOException 及其子类异常又被称作「受查异常」。当函数中存在抛出检查型异常的操作时,调用该函数的函数也必须对该异常进行处理,如不进行处理则必须在调用函数上声明throws语句。
- 另一类是非检查异常,在Java中所有 RuntimeException类的派生类都是非检查型异常(运行时异常)。检查型异常是JAVA首创的,在编译期对异常的处理有强制性的要求。在JDK代码中大量的异常属于检查型异常,包括IOException,SQLException等等。与检查型异常对比,非检查型异常可以不在函数声明中添加throws语句,调用函数上也不需要强制处理。
常见的检查异常:
常见的非检查异常:
三. 捕获异常
try
{//程序代码(也是保护代码)
}catch(ExceptionName e){
//catch块
}
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
例子:
import java.io.FileNotFoundException;
public class ExceptionTest {
public static void main(String[] args)
{
ExceptionTest1();
}
//try-catch捕获异常
public static void ExceptionTest1()
{
try {
int[] a = {1, 2};
int b = a[10];
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println("错误类型是:"+e);
}
}
}
程序执行输出结果是:
错误类型是:java.lang.ArrayIndexOutOfBoundsException: 10
Process finished with exit code 0
1. 多重捕获
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}
可以在 try 语句后面添加任意数量的 catch 块。如果保护代码中发生异常,异常被抛给第一个 catch 块。如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。如果不匹配,它会被传递给第二个 catch 块。如此,直到异常被捕获或者通过所有的 catch 块。
class MyMath{
public static int div(int x,int y) throws Exception{
return x/y;
}
}
public class Main {
public static void main(String args[]) throws Exception{
System.out.println(MyMath.div(10,0));
}
}
如果主方法继续向上抛出异常,那么就表示此异常将交由JVM负责处理。
2. finally关键字
finally 关键字用来创建在 try 代码块后面执行的代码块。无论是否发生异常,finally 代码块中的代码总会被执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。finally 代码块出现在 catch 代码块最后,语法如下:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
例如:
import java.io.FileNotFoundException;
public class ExceptionTest {
public static void main(String[] args)
{
ExceptionTest2();
}
//try-catch捕获异常
//finally
public static void ExceptionTest2()
{
int[] data={1,2};
try {
System.out.println(data[10]);
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println("错误信息是:"+e);
}
finally {
System.out.print("执行finally语句部分:");
data[0]=11;
System.out.print("data数组0位置存储的数据是:"+data[0]);
}
}
}
程序输出结果是:
错误信息是:java.lang.ArrayIndexOutOfBoundsException: 10
执行finally语句部分:data数组0位置存储的数据是:11
Process finished with exit code 0
注意下面事项:
catch 不能独立于 try 存在。
在 try/catch 后面添加 finally 块并非强制性要求的。
try 代码后不能既没 catch 块也没 finally 块。
try, catch, finally 块之间不能添加任何代码。
四. 声明自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class MyException extends Exception{
}
五. throws/throw 关键字
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
1. throws关键字
通过之前的程序可以发现,在执行程序的过程中可能会产生异常,但是如果说现在假设你定义了一个方法,实际上就应该明确地告诉使用者,这个方法可能会产生何种异常,那么此时就可以在方法的声明上使用throws关键字。
例子:
class MyMath{
public static int div(int x,int y) throws Exception{
return x/y;
}
}
public class Main{
public static void main(String args[]) throws Exception{
System.out.println(MyMath.div(10,0));
}
}
在主方法上继续抛出异常主方法本身也是一个方法,那么实际上主方法也可以继续向上抛出。
class MyMath{
public static int div(int x,int y) throws Exception{
return x/y;
}
}
public class Main{
public static void main(String args[]) throws Exception{
System.out.println(MyMath.div(10,0));
}
}
如果主方法继续向上抛出异常,那么就表示此异常将交由JVM负责处理。
2. throw关键字
throw关键字,此关键字的主要作用在于表示手工进行异常的抛出,即:此时将手工产生一个异常类的实例化对象,并且进行异常的抛出处理。
public class Main002 {
public static void main(String[] args) {
try {//异常对象不是由系统生成的,而是由手工定义的
throw new Exception("自己抛出一个异常");
}catch (Exception e){
e.printStackTrace();
}
}
}
- throw:在代码块中使用的,主要是手工进行异常对象的抛出。
- throws:在方法定义上使用的,表示此方法中可能产生的异常明确告诉调用处,由调用处进行处理。
六、try-catch-finally实例
返回值原理:返回值在try中被缓存了,后面的更改并不能修改到缓存了的返回值。
先记录原理,后续写实例