Java异常 学习

思维导图

在这里插入图片描述

导学

本节课程,我们将会学习到异常,以及如何处理可能会发生的异常。
首先,什么是异常?异常可以理解为意外,例外的意思,本质上是程序出现的错误。错误在我们编写程序的过程中经常会发生,包括编译期间和运行期间的错误。比如括号没有正常的配对,语句少写了分号,关键字编写错误等就是编译期间会出现的错误。通常这些编译错误编译器会帮助我们进行修订。
那么,运行期间的错误,我们也曾遇到过。比如使用空的对象引用调用方法、数组访问时下标越界、算数运算除数为0、类型转换时无法正常转型等,这些错误在编译的时候完全没有提示。
在程序运行过程中,意外发生的情况,背离我们程序本身的意图的表现,都可以理解为异常。
那么,为什么会出现异常呢?
用户不正当的输入
本身系统代码编写的问题

如果不对异常进行处理,那么轻则数据错误、丢失,重则程序崩溃。比如下面这段代码

public class ExceptionStudy {
	public static void main(String[] args) {
		System.out.println("******程序开始运行*******");
		System.out.println("******数学计算:"+ (10/2) +"*******");
		System.out.println("******程序运行结束*******");
	}
}
运行结果:
******程序开始运行*******
******数学计算:5*******
******程序运行结束*******
public class ExceptionStudy {
	public static void main(String[] args) {
		System.out.println("******程序开始运行*******");
		System.out.println("******数学计算:"+ (10/0) +"*******");
		System.out.println("******程序运行结束*******");
	}
}
运行结果:
******程序开始运行*******
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.ntdodoke.preparation.usuallyClass.ExceptionStudy.main(ExceptionStudy.java:6)

在出现异常的地方,程序就会被「异常指令流」直接终止,不再往下运行。为了让程序在出现异常后依然可以正常执行,所以我们必须正确处理异常来完善代码的编写。
在Java中,提供了一种强大的异常处理机制来帮助我们实现异常的处理

异常介绍

在这里插入图片描述
Error是程序无法处理的错误,表示运行应用程序中较为严重的问题,是代码运行时Java虚拟机中出现的问题。这种错误是不可查的,它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。

Excepetion是程序本身可以处理的异常。异常处理通常针对这种类型异常的作出处理 。RuntimeException是非检查异常,也就是编译器不要求强制处理的异常。程序员可以针对这些异常进行捕获或放任不管。检查异常包括IO异常和SQL异常,编译器要求必须在代码中处理这些异常。

异常处理

针对于异常处理,通常我们最简单的方式就是思考全面,检查代码。将可能会发生的错误预先考虑透彻。那么也有可能会出现一些无法预料到的情况,我们就可以使用到Java的异常处理机制。在Java中异常处理机制分为两种:抛出异常和捕获异常。
抛出异常指的是一个方法中出现错误引发的异常时,方法会创建异常对象,交给运行时系统进行处理。在异常对象中包含异常类型,异常出现时的程序状态等。运行时系统捕获到这个异常后,进入捕获异常环节,运行时系统会找合适的处理器,与抛出异常匹配后进行处理,如果没找到则程序终止。Java规定,对于检查异常必须捕获、或者声明抛出。对于非检查异常(RuntimeException及其子类)和Error及其子类,允许忽略。
抛出异常、捕获异常通过5个关键字实现:try、catch、finally、throw、throws。try、catch、finally通常用来捕获异常,throws通常用来声明异常,throw通常用来抛出异常。
在这里插入图片描述

try…catch…finally

语法:

try {
    //用于捕获异常
    可能会发生异常的代码块;
} catch(Exception e) {//异常类型
    //用于处理try中捕获的异常
} finally {
    //无论是否发生异常都会执行的代码    
}

try块后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。 也就是说try必须和catch,finally组合使用。catch、finally也不允许单独存在。
案例:

public class TryDemo {
	public static void main(String[] args) {
		//定义两个整数,输出两个数的商
		Scanner input = new Scanner(System.in);
		System.out.println("======运算开始=======");
		try{
			System.out.println("请输入第一个整数");
			int one = input.nextInt();
			System.out.println("请输入第二个整数");
			int two = input.nextInt();
			System.out.println("one和two的商是:" + one / two);
		}catch(Exception e) {//这是对异常种类的猜测,通常可以采用异常的父类,就不用猜测具体是哪个子类异常了
			e.printStackTrace();//在控制台打印出异常种类,错误信息和出错位置等,输出位置比较随机
			System.out.println("程序出错了~~~");
		} finally {
			System.out.println("运算结束");
		}
	}
}

在Java中,可以使用多重catch语句块,针对于同一块代码可能发生的不同种类异常作出处理。比如上述代码可能会发生算术异常也可能会发生其他异常。但是两个catch块中对于异常的猜测是不允许相同的,而且对于父类的异常是必须要放置在子类的异常catch代码块下面的。兄弟异常的catch代码块位置可以随意。

public class TryDemo {
	public static void main(String[] args) {
		//定义两个整数,输出两个数的商
		Scanner input = new Scanner(System.in);
		System.out.println("======运算开始=======");
		try{
			System.out.println("请输入第一个整数");
			int one = input.nextInt();
			System.out.println("请输入第二个整数");
			int two = input.nextInt();
			System.out.println("one和two的商是:" + one / two);
		} catch(InputMismatchException a) {//输入格式异常
			System.out.println("请输入整数!");
			a.printStackTrace();
		} catch(ArithmeticException b) {//数学运算异常
			System.out.println("除数不能为0!");
			b.printStackTrace();
		} catch(Exception e) {//这是对异常种类的猜测,通常可以采用异常的父类,就不用猜测具体是哪个子类异常了
			e.printStackTrace();//在控制台打印出异常种类,错误信息和出错位置等,输出位置比较随机
			System.out.println("程序出错了~~~");
		} finally {
			System.out.println("运算结束");
		}
	}
}

我们会发现,最终输出的异常信息,是我们猜测的某一个异常。同样我们不可能猜测出所有的异常信息,所以需要在catch代码块的最下面写出一个父类异常catch代码块,以免发生遗漏!

终止finally执行的方法

在之前的课程中,我们可以看到,finally语句块中的内容无论怎样都会运行,那么有没有一种方法可以终止finally中方法的运行呢

public class TryDemo {
	public static void main(String[] args) {
		//定义两个整数,输出两个数的商
		Scanner input = new Scanner(System.in);
		System.out.println("======运算开始=======");
		try{
			System.out.println("请输入第一个整数");
			int one = input.nextInt();
			System.out.println("请输入第二个整数");
			int two = input.nextInt();
			System.out.println("one和two的商是:" + one / two);
		} catch(InputMismatchException a) {//输入格式异常
			System.exit(1);//终止程序运行
			System.out.println("请输入整数!");
			a.printStackTrace();
		} catch(ArithmeticException b) {//数学运算异常
			System.out.println("除数不能为0!");
			b.printStackTrace();
		} catch(Exception e) {//这是对异常种类的猜测,通常可以采用异常的父类,就不用猜测具体是哪个子类异常了
			e.printStackTrace();//在控制台打印出异常种类,错误信息和出错位置等,输出位置比较随机
			System.out.println("程序出错了~~~");
		} finally {
			System.out.println("运算结束");
		}
	}
}

System中存在exit方法,该方法的作用可以用来终止Java虚拟机的运行,传入的参数是数字,这个数字只要不是数字0描述的都是异常终止状态。可以使用该方法来使当前程序无条件终止运行。

return关键字在异常处理中的作用

在之前的学习中,我们知道return可以用来提供方法的返回值。那么return是否可以用在异常处理中呢,或者又有什么作用呢

public class TryTest {
	public static void main(String[] args) {
		int result = test();
		System.out.println("运行的结果为:" + result);
	}
	
	public static int test() {
		Scanner input = new Scanner(System.in);
		System.out.println("======运算开始=======");
		try{
			System.out.println("请输入第一个整数");
			int one = input.nextInt();
			System.out.println("请输入第二个整数");
			int two = input.nextInt();
			System.out.println("one和two的商是:" + one / two);
			return one / two;
		} catch(ArithmeticException b) {
			System.out.println("除数不能为0!");
			b.printStackTrace();
			return 0;
		} finally {
			System.out.println("运算结束");
			return -10000;
		}
	}
}

当try,catch,finally三个语句块中都存在return关键字时,无论程序运行正常不正常,最终的结果都是-10000;原因在于finally无论如何都会执行。

throw和throws
使用throws声明异常类型

可以通过throws声明将要抛出何种类型的异常,通过throw将产生的异常抛出。如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。谁调用这个方法则谁处理抛出的异常。
throws 后面可以跟多个异常类型,多个异常使用逗号隔开。当方法抛出异常时,方法将不对这些类型及其子类类型异常做处理,而抛向调用该方法的方法,由他去处理。

public class TryTest {
	public static void main(String[] args) {
		try {
			int result = test();
			System.out.println("运行的结果为:" + result);
		} catch(ArithmeticException e) {
			System.out.println("要求除数不能为0");
			e.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();
		}
		//test();写exception异常后会有提示
	}
	
	public static int test() throws ArithmeticException, Exception{//告诉编译器该方法可能会发生何种异常,可以使用多异常或写Exception
		Scanner input = new Scanner(System.in);
		System.out.println("======运算开始=======");
		System.out.println("请输入第一个整数");
		int one = input.nextInt();
		System.out.println("请输入第二个整数");
		int two = input.nextInt();
		System.out.println("one和two的商是:" + one / two);
		System.out.println("运算结束");
		return one / two;
	}
}

如果方法抛出的是非检查异常,那么调用此方法处编译器不会强制要求进行异常处理(不会报错),如果方法抛出的异常是检查异常或者Exception,则调用此方法处编译器会强制要求进行异常处理(不处理会报错)。针对不报错的情况,可以在抛出异常的方法处加文档注释,这样调用此方法处虽然编译器不会提示要异常处理,但是文档注释会提示。

throw抛出异常对象

注意,throw抛出的是异常对象。throw抛出的只能是Throwable或者是其子类的实例对象。

throws往上抛
public class TryTest {
	//酒店入住规则:18以下和80岁以上必须有亲友陪同,不得单独入住
	public static void testAge() throws Exception {
		System.out.println("请输入年龄:");
		Scanner input = new Scanner(System.in);
		int age = input.nextInt();
		if(age < 18 || age > 80) {
			throw new Exception("18岁以下,80岁以上的住客必须由亲友陪同入住");
		} else {
			System.out.println("欢迎入住本酒店!");
		}
	}
	
}

自己抛出自己处理
public class TryTest {
	//酒店入住规则:18以下和80岁以上必须有亲友陪同,不得单独入住
	public static void testAge() {
		System.out.println("请输入年龄:");
		Scanner input = new Scanner(System.in);
		int age = input.nextInt();
		if(age < 18 || age > 80) {
			try {
				throw new Exception("18岁以下,80岁以上的住客必须由亲友陪同入住");
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("欢迎入住本酒店!");
		}
	}
}

throw抛出异常对象的处理方案:

  1. 自己抛出自己处理,通过try-catch包含throw语句。
  2. 用throws往上抛,调用者可以try-catch处理或者继续往上抛。throws抛出异常类型时,要抛出与throw对象相同的类型或者其父类,但不能是子类

PS:throw手动抛出的异常不建议使用非检查类型,因为编译器不提示

自定义异常

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。也可以通过自定义异常描述特定业务产生的异常类型。所谓自定义异常,就是定义一个类,去继承Throwable类或者它的子类。
比如,在之前酒店的例子中,自己新设置的异常信息被多次使用,就可以创建一个异常类

public class HotelAgeException extends Exception{
	public HotelAgeException (){
		super("18岁以下住店必须由亲友陪同");
	}
}

getMessage()方法可以显示异常信息

异常链

public class TryClient {
	
	public static void main(String[] args) {
		try {
			testThree();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void testOne() throws HotelAgeException {
		throw new HotelAgeException();
	}
	
	public static void testTwo() throws Exception {
		try {
			testOne();
		} catch(HotelAgeException e) {
			throw new Exception("我是新产生的异常1",e);//java中保留异常的机制
		}
	}
	
	public static void testThree() throws Exception {
		try {
			testTwo();
		} catch(Exception e) {
			//throw new Exception("我是新产生的异常2",e);或写成另一种方式
			Exception e1 = new Exception("我是新产生的异常2");
			e1.initCause(e);
			throw e1;
		}
	}
}

异常链就是一个异常接着一个异常。最后输出的异常就只有最后一个。
如果想要把前面所有异常都输出的话,则需要通过保留异常的方法:

  1. 通过构造方法对旧异常对象的获取
    Throwable(String message, Throwable cause) – 保留底层异常的异常信息。
  2. 通过initCause(Throwable cause)方法(一个异常的信息来初始化一个新的异常)用来获取原始异常的描述信息,其中cause是原始异常的对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值