22、Java基础---异常

异常

一、什么是异常

所谓异常就是与程序预期的情况不一致的状态, 或者在通常情况下难以预料的状态

package com.example;// 读入两个整数值,并显示加减乘除运算的结果

import java.util.Scanner;

class ArithInt {

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("对x和y进行加减乘除运算。");

		System.out.print("x的值:");	// 提示输入x的值
		int x = stdIn.nextInt();		// 读入x的整数值

		System.out.print("y的值:");	// 提示输入y的值
		int y = stdIn.nextInt();		// 读入y的整数值

		System.out.println("x + y = " + (x + y));	 // 显示x + y的值
		System.out.println("x - y = " + (x - y));	 // 显示x - y的值
		System.out.println("x * y = " + (x * y));	 // 显示x * y的值
		System.out.println("x / y = " + (x / y));	 // 显示x / y的值(商)
		System.out.println("x % y = " + (x % y));	 // 显示x % y的值(余数)
	}
}

输出:

示例1:对于变量y输入字符串"ABC";当程序的第16行进行读入时,会发生运行时错误,程序不会继续进行加减乘除运算,而是会中断并结束运行

示例2:向变量y中输入0。虽然加法、减法、乘法的运算都可以正常执行,但在程序的第21行执行除法运算时会发生运行时错误, 程序中断并结束运行

运行时错误的消息中开头单词exception有“异常” “例外” “异议” 的意思;运行示例1中发生的是输入不合法的异常,运行示例2中发生的是(除以0导致的)算术运算的异常;如错误消息所示,发生的异常中存在类型,分别为java.util.InputMismatchException类类型和java.lang.ArithmeticException类类型。
与程序预期的情况不一致的状态或在通常情况下未预料到(或者无法预料)的状态就是异常,在大规模的程序或者程序中使用的类和方法等控件中,需要妥善处理异常

二、捕获异常

给上述程序加上对异常的处理,将原来的程序修改为无限循环结构,以便能够循环输入和显示

package com.example;// 读入两个整数值,并显示加减乘除运算的结果

import java.util.Scanner;
import java.util.InputMismatchException;

class ExceptionSample {

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.println("对x和y进行加减乘除运算。");

		while (true) {
			try {
				System.out.print("x的值:");  int x = stdIn.nextInt();
				System.out.print("y的值:");  int y = stdIn.nextInt();

				System.out.println("x + y = " + (x + y));
				System.out.println("x - y = " + (x - y));
				System.out.println("x * y = " + (x * y));
				System.out.println("x / y = " + (x / y));
				System.out.println("x % y = " + (x % y));
			} catch (InputMismatchException e) {
				System.out.println("发生输入错误。" + e);
				String s = stdIn.next();
				System.out.println("忽略了" + s + "。");
			} catch (ArithmeticException e) {
				System.out.println("发生算术错误。" + e);
				System.out.println("请输入不会发生错误的数值。");
			} finally {
				System.out.println("--------------------");
				System.out.print("再来一次?(1…Yes/0…No):");
				int retry = stdIn.nextInt();
				if (retry == 0) break;
				System.out.println("--------------------");
			}
		}
	}
}

输出:由于对异常进行了处理, 因此程序能够从可能发生的致命状况中恢复过来,通过运行结果来理解一下程序的流程

      

向变量y中输入字符串"ABC" ,当调用nextInt方法时, 程序会发生异常处理中断,程序会显示“发生输入错误。java. util. InputMismatchException" "忽略了ABC。”可以推测出,异常是通过e接收的,这个e中包含了各种信息,当输出e时会显示表示
错误内容的简单字符串
(此处为"java.util.InputMismatchException"),接着向变量x和y中输入整数值, 程序不再发生异常, 继续执行处理。

向变量y中输入0,到加法、减法、乘法运算为止,程序都可以正常执行, 但当执行随后的除以0的运符时,就会发生异常处理中断,程序会显示“发生算术错误。java.lang.ArithmeticException: /by zero" "请输入不会发生错误的数值。”无论是否发生异常,都会显示"---------"然后程序会询问是否再次执行相关的操作。

try语句

引发异常的操作称为抛出(throw)异常;检查、捕获(catch)抛出的异常, 并对其进行处理的就是try语句, 如图

try语句由三部分构成:
• try语句块(try block)
• catch子句(catch clause)
• finally子句(finally clause)

另外, catch子句可以有多个, finally子句可以省略

try语句块
其结构为关键字try的后面紧跟着语句块{}, 如果try语句块在执行时抛出异常,处理就会中断,程序流程转移到catch
子句,捕获异常。反之,如果没有try语句块,就不会捕获异常, 当try语句块在执行过程中未抛出异常时,try语句块会执行到最后,程序流程会跳过catch子句, 直接转移到图中3处。

catch子句
catch子句部分会捕获try语句块在执行过程中发生的异常并执行具体的处理, 称为异常处理器(exception handler)
关键字catch后面的括号中会声明表示捕获的异常种类的类型和类型的形参名,虽然与函数的形参声明类似, 但这里只可以声明一个形参

在图中,1是捕获ExpA的异常处理器,2是捕获ExpB的异常处理器由于异常处理器的排列顺序是1--2,因此程序会按照这个顺序来确认能否捕获异常(根据catch子句的顺序不同. 结果也可能会有所不同)异常处理器的主体中会对捕获的异常执行相应的处理,当异常处理器执行完毕后, 程序流程会转移到最后一个异常处理器的下一个位置

finally子句
无论try语句块中是否发生异常,finally子句都一定会被执行。finally子句执行的是资源的释放处理(例如将打开的文件进行关闭的处理)等善后操作;
代码中第一个异常处理器捕获了java.util. InputMismatchException类型的异常, 第二个异常处理器捕获了java.lang.ArithrneticException类型的异常;这两个异常处理器都通过形参e来接收异常。不过由于形参的类型为类类型. 因此e中接收到的与其说是异常本身,倒不如说是异常类类型的实例的引用更为准确。

三、异常传递

package com.example;// 将值读入到数组元素中,并进行倒序排列(存在Bug)

import java.util.Scanner;

class ReverseArray1 {

	//--- 交换数组中的元素a[idx1]和a[idx2] ---//
	static void swap(int[] a, int idx1, int idx2) {
		int t = a[idx1];
		a[idx1] = a[idx2];
		a[idx2] = t;
	}

	//--- 对数组a的元素进行倒序排列(错误)---//
	static void reverse(int[] a) {
		for (int i = 0; i < a.length / 2; i++)
			swap(a, i, a.length - i);
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.print("元素个数:");
		int num = stdIn.nextInt();		// 元素个数

		int[] x = new int[num];			// 元素个数为num的数组

		for (int i = 0; i < num; i++) {
			System.out.print("x[" + i + "] : ");
			x[i] = stdIn.nextInt();
		}

		reverse(x);						// 对数组x的元素进行倒序排列

		System.out.println("元素的倒序排列执行完毕。");
		for (int i = 0; i < num; i++)
			System.out.println("x[" + i + "] = " + x[i]);
	}
}

输出:

虽然程序的Bug位于第17行,但运行时错误却发生在第10行(示例中试图将元素个数为5的数组中的a[5]的值赋给a[0]),运行示例中显示的错误信息包含如下含义:
1)发生的异常为ArrayindexOutOfBoundsException (超出范围的数组下标)
使用的错误下标为5 (下标必须是0~4)
2)异常通过方法进行传递
具体来说,由于方法swap的第10行中发生的异常未被捕获到, 因此swap的执行被中断。然后,程序流程返回到万法reverse的第17行,在这里也未捕获到异常,因此reverse的执行也被中断。然后返回到main方法的第33行, 在这里依旧未捕获到异常,因此main方法的执行也被中断。

在swap方法中添加对异常的处理:

package com.example;// 将值读入到数组元素中,并进行倒序排列(存在Bug:捕获异常,强制结束)

import java.util.Scanner;

class ReverseArray2 {

	//--- 交换数组中的元素a[idx1]和a[idx2]を交換(捕获异常,强制结束)---//
	static void swap(int[] a, int idx1, int idx2) {
		try {
			int t = a[idx1];
			a[idx1] = a[idx2];
			a[idx2] = t;
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("方法swap中检测出了不正确的下标。");
			System.out.println("结束程序。");
			System.exit(1);
		}
	}

	//--- 对数组a的元素进行倒序排列(错误)---//
	static void reverse(int[] a) {
		for (int i = 0; i < a.length / 2; i++)
			swap(a, i, a.length - i);
	}

	public static void main(String[] args) {
		Scanner stdIn = new Scanner(System.in);

		System.out.print("元素个数:");
		int num = stdIn.nextInt();		// 元素个数

		int[] x = new int[num];			// 元素个数为num的数组

		for (int i = 0; i < num; i++) {
			System.out.print("x[" + i + "] : ");
			x[i] = stdIn.nextInt();
		}

		reverse(x);						// 对数组x的元素进行倒序排列

		System.out.println("元素的倒序排列执行完毕。");
		for (int i = 0; i < num; i++)
			System.out.println("x[" + i + "] = " + x[i]);
	}
}

输出:

在这个swap方法中,当idxl和idx2中的一个或者两个都被传入了不正确的值时, 在显示了相关信息后, 程序会被强制结束。
但并不是所有使用本方法的人都希望使用“强制结束程序" 的解决方案。
当开发方法或类等控件时,会遇到如下难题:找到异常或者错误比较容易,但决定如何处理该异常或者错误则比较困难,甚至是不可能的;这是因为在很多情况下,异常或者错误的处理方法不是由控件的开发人员而是应该由使用人员来决定的。如果控件的使用人员可以根据不同悄况来决定相应的处理方法, 那么软件就会变得更加灵活。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值