异常处理
异常是程序在执行过程中所产生的问题
异常是描述出错信息的对象
示例:
public static void main(String[] args) {
method1();
}
public static void method1(){
System.out.println("进入method1");
method2();
System.out.println("退出method1");
}
public static void method2(){
System.out.println("进入method2");
method3();
System.out.println("退出method2");
}
public static void method3(){
System.out.println("进入method3");
int i = 1/0; //在java中除0会出现算法异常
System.out.println("退出method3");
}
控制台:
进入method1
进入method2
进入method3
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.lovo.com.lovo.classTest1.ClassTest.method3(ClassTest.java:25)
at com.lovo.com.lovo.classTest1.ClassTest.method2(ClassTest.java:18)
at com.lovo.com.lovo.classTest1.ClassTest.method1(ClassTest.java:12)
at com.lovo.com.lovo.classTest1.ClassTest.main(ClassTest.java:6)
进程已结束,退出代码1
//Exception in thread "main" 表示出错的线程
//java.lang.ArithmeticException: / by zero 显示出错类型以及原因
/*
at com.lovo.com.lovo.classTest1.ClassTest.method3(ClassTest.java:25)
at com.lovo.com.lovo.classTest1.ClassTest.method2(ClassTest.java:18)
at com.lovo.com.lovo.classTest1.ClassTest.method1(ClassTest.java:12)
at com.lovo.com.lovo.classTest1.ClassTest.main(ClassTest.java:6)
表示出错的位置
*/
异常的传播机制
当问题出现的时候,JVM会在出现问题的位置,产生一个描述问题产生原因的异常对象
首先,会在异常对象产生的位置,去查找有无异常解决方案,如果有则在此处解决,如果没有解决方案,异常产生位置的方法会从方法调用栈中弹出
然后将异常对象向方法调用栈下方的方法抛出,依此类推
直到最后异常对象会出现在main方法里面,如果main方法依然没有异常的解决方案,那么异常会导致main方法结束,然后在控制台中打印异常信息
异常控制流程
异常处理方法:
1、捕获这个异常,不让它沿着调用栈向下抛出
2、捕获这个异常,并继续向下抛出
3、不捕获这个异常,从而导致方法从调用栈中被弹出,异常对象继续抛给调用栈下方的方法
API结构和分类
API结构
Error:错误
a、应用程序不应该试图捕获的严重问题
b、可以捕获,不能处理
c、Error是所有错误类的父类
Exception:异常
a、指出了应用程序想要捕获的条件
b、可以捕获,可以处理
c、Exception是所有异常类的父类
Throwable类
两个方法,获取抛出异常的信息
public String getMessage();
public void printStackTrace();
分类
编译期异常(检查异常)
开发者必须处理,否则无法通过编译
运行期异常(非检查异常)
开发这可以处理,也可以不处理,即使不处理也可通过编译
所有运行期异常都是**RuntimeException**的子类
异常处理
异常捕获
三者不能独自使用,必须组合
try
书写可能会抛异常的代码,在其中尝试运行
catch
真正意义上的捕获
finally
无论try块中是否有异常都会执行的代码
一般用于资源清理工作,比如:流的关闭,数据库的关闭等
代码层面上:
``try``和``catch``中的``return``不影响``finally``的执行
只有``System.exit(0);`` 会导致其代码不执行
int x = 9;
try{
return x;
}catch(){
}finally{
x += 2;
}
/*
最终返回的是9
当程序遇到return语句时,不是马上执行finally中的语句
先把return的准备工作做完,包含了返回值的准备(此时是9)
然后再执行finally块中的代码
finally执行完毕后直接进行流程的返回
虽然在finally中对x进行了改变,但是不影响之前return准备好的返回值
*/
代码格式(含组合)
try{
可能会抛出异常的代码
}catch(要处理的异常类 对象){
处理异常的代码
}finally{
不管是否发生异常都会执行的代码,除非虚拟机关闭
}
try{
可能会抛出异常的代码
}catch(要处理的异常类 对象){
处理异常的代码
}
try{
可能会抛出异常的代码
}finally{
不管是否发生异常都会执行的代码,除非虚拟机关闭
}
/*
此时不会捕获异常,没有处理,比较鸡肋
*/
例子
try{
int i = 1/0;
}catch(ArithmeticException e){
e.printStackTrace(); //通过方法调用栈,得到整个错误信息
System.out.println(e.getMessage()); //可省略
}finally{
System.out.println("不管是否发生异常都会执行的代码");
}
捕获或处理的执行顺序
没有异常时
如果try块中没有抛出异常,那么catch块将被忽略,执行finally块
有异常时
先顺序执行try块中的代码
如果try块中出现异常,终止try块剩余代码的执行
直接跳转到与异常类型匹配的catch块中
执行异常处理的代码,再执行finally里面的代码
多catch
JDK7以后出现
-
多个catch里最多被执行一个
-
写在前方的catch捕获的异常类型不能是后面异常类型的父类
异常抛出
开发者可通过throw和throws关键字抛出异常
throw和throws可以不用捆绑出现
throw
用于抛出异常对象
throw new Exception(); // 抛出一个编译期异常
throw new RuntimeException();//抛出一个运行期异常
例子
throw new RuntimeException();
throws
属于方法声明
- 此时方法重写的规范需要加一条:
重写之后的方法不能比重写之前的范围大(运行期异常除外)
标识该方法可能会抛出某种异常
public void test() throws Exception{
}
例子
public static void main(String[] args) {
testThrow(null);
}
public static void testThrow(String str) throws RuntimeException{
if (str == null){
RuntimeException exception = new RuntimeException();
//人为抛出异常
throw exception;
}
}
//控制台
Exception in thread "main" java.lang.RuntimeException
at com.lovo.com.lovo.classTest1.ClassTest.testThrow(ClassTest.java:43)
at com.lovo.com.lovo.classTest1.ClassTest.main(ClassTest.java:8)
进程已结束,退出代码1
如何编写自定义异常
1、创建一个类继承于Exception / RuntimeException
2、添加一个带参构造方法和普通方法
public class MyException extends Exception{
public MyException(String message){
super(message);
}
}