目录
一、引言
- 这篇文章篇幅有限,所以会写常用到的异常相关。
- 了解异常之前,若看过相关部分的Java API更好。
- 作为初接触异常,可以参考性的看看《Java编程思想》,希望在以后实际中深刻理解异常。
- 红色标记表示十分重要的地方,蓝色标记表示需要注意的细节
二、异常认识(不涉及code)
1.异常概念
- 异常就是一种对象(Exception),表示阻止程序正常执行的错误或情况。
- 异常层次结构(来源于JavaAPI 网络图片)
- 可以看出,异常类Exception继承了throwable类,并且拥有它的方法。
- Throwable分成了两个不同的分支error(程序不能捕获、无法处理的错误),和Exception(用户可以捕获、可以处理的异常)
- Exception又分为异常类可以分为运行时(RuntimeException)异常和非运行时异常
PS:附加异常解释可以参考JavaAPI,例如ArrayIndexOutOfBoundsException表示数组下标越界异常
三、异常处理的基本语法
1.异常处理
PS:运行时异常可以忽略,系统会自动抛出,但是无法执行后面的代码
- 异常处理的本质是抛出异常(不进行错误处理)和捕获异常(寻找合适处理器进行处理)。
- 异常处理的目的:使程序继续进行或终止,有以下三个方式:
恢复正常的执行; 日志处理; 提示用户(通过错误提示或者对话框)
- 使用到的关键字:
try:用于监听。将可能发生错误的代码块放入。
catch:用于捕获try语句块中的异常。
finally:该语句只要存在就一定会执行。主要用于释放资源,最好不要用return等语句。
throw:抛出异常,实际的处理。
throws:在方法名后,声明该方法可能抛出异常,会使用throw。
接下来会一一讲解。
2.异常处理语法
- try-catch (我们只是获取了被Java运行时系统引发的异常)
public class ExceptionTest {
public static void main(String[] args) {
try{
//try语句中方式你要监测的代码块
String str="llii";
System.out.println(str+"年龄是:");
int age=Integer.parseInt("20L");
System.out.println(age);
}catch (Exception e){ //创建异常对象,将异常对象抛出try监控语句
//在命令行打印异常信息在程序中出错的位置及原因。注:是打印异常的堆栈信息
//printStackTrace为Exception类中的方法,表示按照栈
e.printStackTrace();
}
System.out.println("program over");
}
}
结果如下
PS:异常类型只能是Throwable类或者子类的对象,不能用 int ,char等类型
- 因为"20L"不能转换为整型,“L”字符无法转换,则出现NumberFormatException异常,即数字格式异常
- try - catch - finally(finally是异常处理结果的最后执行部分)
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
public class ExceptionLink {
public static void main(String[] args) {
System.out.println("请输入两个加数");
int result; //设置一个变量接收求和值
try{
List<Integer> nums = getInput(); //调用getInput()方法,接收输入值
result = nums.get(0)+nums.get(1); //将这两个值相加
System.out.println(" 结果为:"+result);
}catch (Exception e ){
e.printStackTrace(); //若错误,打印栈中的错误信息
}
}
//获取两个输入的整数
private static List<Integer> getInput(){ //使用一个整型集合,用于存储输入的两个值
List<Integer> nums = new ArrayList<>(); //实例化数组,nums为数组名
Scanner scan = new Scanner(System.in); //该句为输入方式
try{ //try语句中监控输入是否有错
int num1 =scan.nextInt(); //输入
int num2 = scan.nextInt();
nums.add(new Integer(num1)); //将输入的值放入数组中
nums.add(new Integer(num2));
}catch (InputMismatchException immexp){ //捕获输入异常的对象
throw immexp; //抛出异常对象
}finally { //若该输入结束时,关闭输入,防止输入异常后资源空间浪费
scan.close(); //该句一定会执行
System.out.println("关闭输入");
}return nums; //若无误,返回数组
}
}
分别输入R,1 ; 1,1两个不同输入,结果如下
PS:① finally语句可有可无,但是每个try语句至少有一个catch语句或者finally语句
② 若try语句与一个finally语句连用,finally语句会在try结束之前执行
- 过程在code中已经解释得差不多了,接下来就写一下注意事项
- 使用情况:如果一个方法打开了一个文件并关闭,然后退出,你不希望关闭文件的代码被异常处理机制旁路,可以使用
该句稍微有点复杂,使用了两个try-catch语句嵌套(扩展总结中会写),finally创建的代码块在try-catch块完成之后另一个try-catch出现之前执行;throw会在接下来写。
只要抛出异常,没有catch语句也会执行。
- try-catch-throw(之前的try-catch只是获取系统运行异常,接下来是明确的抛出异常)
public class ExceptionTest{
static void methods(){ //创建静态方法,可以不用实例化就调用
try{
//为了code简洁,假设该处有一个空指针异常,使用throw
throw new NullPointerException("空指针异常");
}catch (NullPointerException e){ //catch捕获空指针异常
System.out.println("已在methods方法中捕获");
throw e; //抛出该对象到main方法的try监控语句中
}
}
public static void main(String[] args) {
try {
methods();
}catch (NullPointerException e){
System.out.println("主方法中返回的"+e);//打印异常
}
}
}
结果如下:
- 这里也用到了try-catch嵌套,其实就是向上抛出异常
- finally语句后的任何语句都不可以被执行
四、自定义异常
class MyException extends Exception //创建自定义异常,继承了Exception类
{
public MyException(String ErrorMessage){ //构造器
super(ErrorMessage); //调用父类构造方法
}
}
public class MyExceptionTest{
static int avg(int n1,int n2)
throws MyException{
if(n1<0||n2<0)
throw new MyException("不可以用负数");
if(n1>100||n2>100)
throw new MyException("数值太大了");
return (n1+n2)/2;
}
public static void main(String[] args) {
try{
//调用avg方法
int result = avg(2,-2);
System.out.println(result);
}catch (Exception e){
System.out.println(e); //简洁输出错误
// e.printStackTrace(); 表示exception类的异常堆栈输出错误和位置
}
}
}
结果如下:
- 若使用 avg(2,2)等符合要求的数值,则输出平均值(读者自己尝试)
- 具体步骤:创建自定义异常类(继承Exception类)-> 在方法总通过throw关键字抛出异常 -> 在调用者中捕获处理异常
五、异常扩展总结
1.多重try
- try语句可以被嵌套,一个try语句中可以包含另外一个try语句
- 每次加入try语句,异常的上下关系都会进入堆栈,若一个try语句无特殊异常,则弹出堆栈,依次检查,直到检查完毕
- 若try 语句的中try语句无catch语句匹配,系统自动处理
例如:
try{ //第一层
try{
}catch{ 内部异常对象}
}
catch(外部异常对象){
//第一层try块
}
PS:两个异常对象应该为不同异常类型,内部异常对象匹配内部异常,外部异常对象匹配外部异常
2.多重catch
- 当一个代码块含有多个异常时,需要使用多重catch语句来处理
- 一个catch语句处理一个异常
例如
try{ 该语句含有多个异常
}catch(异常对象1){}
catch(异常对象2){}
catch(异常对象3){}
PS:每个catch块处理不同异常;且不要将父类异常位于子类异常之前,编译器会报错(不会执行到后面)
如:异常对象1 不能为异常对象2的父类
3.throws与throw
throw
- 用在方法体内,跟的是异常对象名,
- 只能抛出一个异常对象名
- 表示抛出异常,由方法体内的语句处理
- throw则是抛出了异常,执行throw则一定抛出了某种异常
throws
- 用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用 我我就抛给谁。
- 用在方法声明后面,跟的是异常类名
- 可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的调用者来处理
- throws表示出现异常的一种可能性,并不一定会发生这些异常
PS:finally 出讲过具体代码
4.为大家介绍一个总结(来源网络回答)
1.Java语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各种异常(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异常状况都要被执行;try语句可以嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。
PS:若有不正之处,还望指出。谢谢!