一级目录
二级目录
三级目录
1.异常概述
-
在Java语言中,将程序执行中发生的不正常情况称为异常。语法错误和逻辑错误不是异常
-
Java程序执行过程中所发生的异常事件可分为两类:
-
Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。StackOverflowError和OutOfMemoryError等
-
Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以用针对性的代码进行处理。例如:空指针访问异常NullPointerException、试图读取不存在的文件、网络连接中断、数据角标越界等
-
异常分为编译期异常和运行时异常
-
编译期异常
- 指编译器要求必须捕获或声明的异常,在程序由于外界因素造成的一般性异常,这类异常不处理则会带来不可估量的后果
-
运行时异常
- 指编译器不要求强制处理的异常,一般是指编程时的逻辑错误,是程序员应该可以避免的异常。java.lang.RuntimeException类及其子类都是运行时异常
如图所示为编译时异常和运行时异常被发现的区域
常见异常:思维导图中也标记了,后续会在实践中更新常见异常并说明解决方法
-
java.lang.RuntimeException
-
ClassCastException
-
ArrayIndexOutOfBoundsException
-
NullPointerException
-
ArithmeticException
-
NumberFormatException
-
InputMismatchException
-
java.io.IOExeption
-
FileNotFoundException
-
EOFException
-
-
java.lang.ClassNotFoundException
-
java.lang.InterruptedException
-
java.sql.SQLException
2.异常处理方式
异常的处理采用”抓抛模型“
”抛“过程:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应的异常类的对象,并抛出此对象,抛出此对象后将不再执行后面的代码。
”抓“过程:抛出的异常对象可以通过两种方式进行抓取
方式一:try-catch-finally
方式二:throws+异常类型
2.1 try-catch
格式:
try{//一个try-catch-finally只能有一个try
//可能会出现异常的代码
}catch(异常类 异常形参){//可以有多个catch
//处理异常
}finally{//只能有一个finally或0个finally
//一定会执行的代码
}
测试:
public static void main(String[] args) {
int x=0;
int y=3;
//异常的捕获
//方式一:
try{
y=y/x;
}catch(ArithmeticException e){
System.out.println("算术异常");
}catch(RuntimeException e){
System.out.println("运行时异常");
}catch(Exception e){
System.out.println("异常父类");
}
System.out.println("捕获异常后输出");
// try{
// y=y/x;
// }finally{//finally并不能处理异常
// System.out.println("aaa");
// }
try{
y=y/x;
}catch(ArithmeticException e){
e.printStackTrace();
}
try{
y=y/x;
}catch(ArithmeticException e){
System.out.println(e.getMessage());// / by zero
}
System.out.println("---------");
}
输出结果:
java.lang.ArithmeticException: / by zero
at com.carl.javacore.exceptionTest.ExceptionTest.main(ExceptionTest.java:48)
格式说明:
-
try有且仅有一个,不可不写
-
catch或finally有一个存在,另一个可不写
-
catch可以写多个,不受限制,finally只能写一个
try-catch执行问题:
-
try块中执行可能出现异常的代码,在执行过程中如果出现异常,则会生成一个对应异常类的对象,通过捕获的这个对象与catch块括号中的对象进行匹配,如果匹配上,则执行catch块中的内容,如果catch中无可匹配项,则相当于没写try-catch,程序会在异常处报错并终止
-
执行完catch块中的内容,则会跳出当前的try-catch结构,继续执行下面的代码
-
catch写多个的情况下,如果没有父子类,则顺序不受限制;如果存在父子关系,子类必须写在上面,父类必须写在下面
-
常用的打印异常信息的方法:
e.getMessage():打印异常信息的具体问题,处理完成后,后续执行顺序不变
e.printStackTrace():打印异常的全限定名及具体信息、报错的位置
-
问题:
e.printStackTrace()输出混乱的问题:我们希望java.lang.ArithmeticException这个异常在/ by zero和---------之前被输出,然而却是在后面输出
原因:
-
e.printStackTrace()方法有三个public重载
- e.printStackTrace()
- e.printStackTrace(PrintStream s)
- e.printStackTrace(PrintWriter)
-
我们使用的e.printStackTrace()本质上是调用了e.printStackTrace(PrintStream s),这里的PrintStream获取的就是System.err
-
e.printStackTrace(PrintStream s)调用的是printStackTrace(PrintStreamOrWriter s)私有方法
-
PrintStreamOrWriter s是一个异常输出的对象,首先将控制台打印的异常信息传到PrintStreamOrWriter对象,由该对象对异常信息做输出控制
-
printStackTrace(PrintStreamOrWriter s)该私有方法获取到输出信息后会获取一个同步锁,同步锁执行后释放,而System.out是一个final static对象,因此一次性获取锁后会直到所有的System.out全部输出,再获取System.err,对err进行输出打印
测试:
//在上述代码上添加如下代码:
public void printStreamTest(int x,int y){
try{
y=y/x;
}catch(ArithmeticException e){
e.printStackTrace();
}
}
//在第一个System.out前面添加如下代码:
et.printStreamTest(x,y);
输出结果:
java.lang.ArithmeticException: / by zero
at com.carl.javacore.exceptionTest.ExceptionTest.printStreamTest(ExceptionTest.java:114)
at com.carl.javacore.exceptionTest.ExceptionTest.main(ExceptionTest.java:34)
java.lang.ArithmeticException: / by zero
at com.carl.javacore.exceptionTest.ExceptionTest.main(ExceptionTest.java:48)
算术异常
捕获异常后输出
/ by zero
---------
解决办法:
不用使用空参PrintStreamTrace方法,将参数的System.err改成System.out
2.2 try-catch-finally
-
finally块是可选的
-
finally块是一定会被执行的
-
finally块一定会在try-catch-finally结束之前被执行
-
如果try-catch-finally中有return,不论是在try块还是catch块,都会在return之前去执行finally块内容
测试:
无返回值方法有异常执行测试:
public void tryCatchFinally(int x,int y){
try {
y = y / x;
System.out.println("try");
} catch (ArithmeticException e) {
System.out.println("catch-算术异常");
} finally {
System.out.println("fianlly");
}
System.out.println("我不属于try-catch-finally");
}
执行结果:
catch-算术异常
fianlly
我不属于try-catch-finally
无返回值方法无异常执行测试:
public void tryCatchFinally(int x,int y){
try {
System.out.println("try");
} catch (ArithmeticException e) {
System.out.println("catch-算术异常");
} finally {
System.out.println("fianlly");
}
System.out.println("我不属于try-catch-finally");
}
执行结果:
try
fianlly
我不属于try-catch-finally
有返回值方法有异常执行测试:
public int tryCatchFinally2(int x,int y){
try {
y=y/x;
System.out.println("try");
return y;
} catch (ArithmeticException e) {
System.out.println("catch-算术异常");
return -1;
} finally {
System.out.println("fianlly");
return 0;
}
}
执行结果:
catch-算术异常
fianlly
0
有返回值方法无异常执行测试
public int tryCatchFinally2(int x,int y){
try {
System.out.println("try");
return y;
} catch (ArithmeticException e) {
System.out.println("catch-算术异常");
return -1;
} finally {
System.out.println("fianlly");
return 0;
}
}
执行结果:
try
fianlly
0
如果finally没有return,返回哪个结果?
测试:
有返回值方法无异常执行测试:
public int tryCatchFinally2(int x,int y){
try {
System.out.println("try");
return y;
} catch (ArithmeticException e) {
System.out.println("catch-算术异常");
return -1;
} finally {
System.out.println("fianlly");
}
}
执行结果:
try
fianlly
3
有返回值方法有异常执行测试:
public int tryCatchFinally2(int x,int y){
try {
y=y/x;
System.out.println("try");
return y;
} catch (ArithmeticException e) {
System.out.println("catch-算术异常");
return -1;
} finally {
System.out.println("fianlly");
}
}
执行结果:
catch-算术异常
fianlly
-1
总结:finally是在try-catch结束之前必须执行的块,常用于:清理垃圾,日志信息提示等
1.无返回值情况
a.有异常步骤
* try块顺序执行到出现异常,将捕获的异常抛给catch进行处理
* 如果catch不能处理,则报错,并去找finally块,执行完成后返回try块结束程序
* catch执行过程中如果无异常,顺序执行完最后一行代码(不会跳出catch(){}最后的花括号),开始执行finally块中的内容,执行完以后跳转到catch内结束try-catch-finally
* catch执行过程中如果有异常,则在异常出现的位置报错,并进入finally块中执行块中内容,并返回异常发生的位置结束程序
b.无异常步骤
* 无异常正常执行try,直到最后一行,跳转到finally块执行块中内容,返回try块中结束try-catch-finally
2.有返回值情况
a.有异常步骤
* try块顺序执行到出现异常,将捕获的异常抛给catch进行处理
* 如果catch不能处理,则报错,并去找finally块,如果finally语句中有return语句,则直接return结束当前方法
* 如果finally语句中无return语句,执行完成后返回try块,由try块中的return来结束方法
* catch执行过程中如果无异常,顺序执行到return语句时,跳转开始执行finally块中的内容,如果finally语句中有return语句,则直接return结束当前方法
* 如果finally语句中无return语句,执行完成后返回catch块,由catch块中的return来结束方法
* catch执行过程中如果有异常,则在异常出现的位置报错,并进入finally块中执行块中内容,如果finally语句中有return语句,则直接return结束当前方法
* 如果finally语句中无return语句,执行完成后返回异常发生的位置结束程序
b.无异常步骤
* try顺序执行到return之前,跳转到finally块中执行,如果fianlly块中有return,执行完finally块中的内容return直接结束方法,,如果fianlly块中无return,则执行完fianlly块中内容,返回try块中return的位置,直接return结束该方法
注意:在这里我们可以发现,实际上报错并不会立刻终止程序,本质上的异常也是类,因此,异常和JVM的终止并不是同时发生的
总结:
-
try-catch-finally可以将一个编译时异常延迟到运行时出现
-
try-catch-finally通常在开发中不用于处理运行时异常,由throws来处理
-
异常的本质还是类对象调用native本地方法进行异常反馈
-
异常的出现很多可以通过控制语句来判断,但是过于冗余,异常的出现,不仅不可保障完整的程序运行,还可以减少代码量
-
e.printStreamTrace和e.getMassage方法只是对异常信息的打印,并没有解决实质性的问题
-
finally块的作用是用于解决流、连接等引用的关闭的
2.3 throws
-
异常处理方式二:
throws+异常类型
写在异常出现的方法的声明处,指明此方法执行时,可能会出现的异常类型
一旦当方法体执行时出现异常,仍然会在异常代码处生成一个异常类的对象,此对象满足throws的异常类型,就会抛出,异常的代码后续的代码就不再执行
格式:
public void file()throws IOException,NullPointerException{
//方法体
}
测试:
public void File() throws IOException {
File file=new File("hello.txt");
FileInputStream fis=new FileInputStream(file);//Unhandled exception: java.io.FileNotFoundException
int data = fis.read();//Unhandled exception: java.io.IOException
while(data!=-1){
System.out.println((char)data);
data=fis.read();//Unhandled exception: java.io.IOException
}
fis.close();//Unhandled exception: java.io.IOException
System.out.println("测试出现异常后是否可以执行此处");
}
public static void main(String[] args) throws IOException {
ExceptionTest et=new ExceptionTest();
//异常的测试
//et.test();
int x=0;
int y=3;
//异常的捕获
//方式一
//int num=et.tryCatchFinally2(x,y);
//System.out.println(num);
//方式二
try {
et.File();
}catch(FileNotFoundException e){
System.out.println(e.getMessage()+1);
}catch(IOException e){
System.out.println(e.getMessage()+2);
}
}
总结:
-
throws并不会直接处理异常,而是抓取到异常对象后将异常向下抛出,抛出到调用者调用处,给调用者处理
-
异常最终的处理还是需要try-catch来处理,这样做可以在最后处理异常,不用频繁的处理异常,而且便于维护
-
子类重写的方法抛出的异常类型必须不大于父类被重写的方法抛出的异常类型
2.4 抛过程
抛异常可以由系统自动抛出–该过程报错并无法处理,但是可以避免程序的终止
也可以自己抛出异常:采用throw关键字
测试
public void throwTest(int a) throws Exception {
if (a>0) {
a=a/2;
}else{
throw new Exception("您输入的数据非法!");
}
}
测试结果
Exception in thread "main" java.lang.Exception: 您输入的数据非法!
at com.carl.javacore.exceptionTest.ExceptionTest.throwTest(ExceptionTest.java:174)
at com.carl.javacore.exceptionTest.ExceptionTest.main(ExceptionTest.java:42)
可以在调用的使用采用e.getMessage()方法,输出"您输入的数据非法!" 如下测试所示:
try {
et.throwTest(-1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
测试结果:
您输入的数据非法!
3.用户自定义异常类
/**
* 如何自定义异常类
* 1.继承于现有的异常结构
* 常用继承:Exception RuntimeException
* 2.提供全局常量serialVersionUID:唯一标识当前异常类
* 3.提供重载构造器
*/
public class MyException extends Exception{
static final long serialVersionUID = -3387516877824222448L;
public MyException(){}
public MyException(String msg){
super(msg);
}
}
自定义异常类可以通过throw抛出 自定义异常类的作用: 最重要的时异常类的名字,当异常出现时,可以根据名字判断异常的类型