从零开始的Java开发1-5-1 异常:分类异常处理try-catch-finally多重catchfinally与returnthrowsthrow关键字自定义异常类异常链

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始的Java开发1-5-1 异常:分类异常处理try-catch-finally多重catchfinally与returnthrowsthrow关键字自定义异常类异常链相关的知识,希望对你有一定的参考价值。

文章目录

什么是异常

  • 异常就是程序的错误。
  • 错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误。
  • 在程序运行过程中,意外发生的情况,背离我们程序本身意图的表现,都可以理解为异常。

举个例子:这些都算是运行的错误——在编译时没有错误提示。

异常分类

在Java中,用Throwable和它的子类对异常进行描述。

Throwable分为ErrorException

  • Error:是程序中无法处理的错误,表示运行应用程序中较严重的问题——大多数错误与代码编写者执行的操作无关,主要是代码运行是Java虚拟机出现的问题,如:虚拟机错误VirtualMachineError,内存溢出OutOfMemoryError,线程死锁ThreadDeath
  • Exception:是程序本身可以处理的异常。异常处理通常指针对这种异常的处理。包括:非检查异常Unchecked Exception,检查异常Checked Exception

关于Error:它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况,对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况

关于Unchecked Exception:编译器不要求强制处理的异常,包含RuntimeException以及它的相关子类。如:

  • 空指针异常
  • 数组下标越界异常
  • 算数异常
  • 类型转换异常

关于Checked Exception:编译器要求在代码中处理的异常。是RuntimeException以及它的相关子类之外的异常。

思维导图

异常处理

在Java应用程序中,异常处理机制分为:

  • 抛出异常
  • 捕捉异常

关于捕获异常
对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同。
Java规定:

  • 对于可查异常必须捕捉、或者声明抛出
  • 允许忽略不可查的RuntimeException(含子类)和Error(含子类)

关于关键字trycatchfinallythrowthrows

try-catch-finally

try:用于捕获异常
catch:用于处理捕获到的异常
finally:无论是否发生异常,代码总能执行

**try块后可接零个或者多个catch块,如果没有catch块,必须跟一个finally块。**简言之,try必须跟catch或finally组合使用,不允许单独存在。

举例:要求:定义两个整,接收键盘用户输入,输出两数之商。

代码:

public class TryDemoOne 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		System.out.println("请输入两个整数:");
		Scanner input = new Scanner(System.in);
		int one = input.nextInt(), two = input.nextInt();
		System.out.println("商为" + one / two);
	


奇葩输入1:12 0
则输出:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)

奇葩输入2:12 a
则输出:

Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:12)

接下来我们使用try-catch结构处理上述异常。

若有异常,catch会捕获它,然后输出其代码块内的内容;否则完成try内的操作。

public class TryDemoOne 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try 
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		 catch (Exception e) 
			System.out.println("程序出错啦!");
		

		System.out.println("运算结束");

	



输入:12 a
输出:

程序出错啦!
运算结束

输入:12 0
输出:

程序出错啦!
运算结束

如果我们想知道错误的类型和位置,可以在catch中添加语句:e.printStackTrace();
如:

public class TryDemoOne 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try 
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		 catch (Exception e) 
			e.printStackTrace();
			System.out.println("程序出错啦!");
		

		System.out.println("运算结束");

	


那么输入12 0时,会输出:

java.lang.ArithmeticException: / by zero
程序出错啦!
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:14)
运算结束

ps:e.printStackTrace();的错误信息的输出位置相对随机,不用计较。

我们该怎么看异常信息呢?

at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)

为例,这是输入12 a的异常信息。

我们要从下往上看。

最后一行at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)表示:异常发生的最外层的表示是在TryDemoOne.java文件的第13行:

在这个位置它调用了Scanner.nextInt方法,在Scanner.java:2212,我们点击它,则跳转到Scanner.class

而这个位置调用了nextInt(defaultRadix),其实就是在Scanner.nextInt(Scanner.java:2258),点击它则跳转到:

到这里我们可以看到integerPattern(),大概能猜出来这是一个整数转换的方法,而a肯定不能转化为整数,所以会跳转到Scanner.next(Scanner.java:1594),即:throwFor(),异常抛出。

异常抛出的具体代码在Scanner.throwFor(Scanner.java:939),即:

所以,我们这里的异常是:InputMismatchException,会被catch所捕获。

这就是try-catch产生异常、捕获异常的整体流程。

关于finally:我们如果想要某个语句不管是否产生异常都一定要执行,可以在这个语句外套一个finally,如:

public class TryDemoOne 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try 
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		 catch (Exception e) 
			e.printStackTrace();
			System.out.println("程序出错啦!");
		 finally 
			System.out.println("运算结束");
		

	


使用多重catch结构处理异常

如果针对不同的异常,有不同的处理方式,该怎么解决呢?
答:用多重catch解决

ps:

  • 多重catch时不能出现同类型异常
  • 安全起见,多重catch块的最后一个块中异常为Exception(不能放第一个,会报错)

代码如下:

public static void main(String[] args) 
		// TODO Auto-generated method stub
		// 要求:定义两个整,接收键盘用户输入,输出两数之商。
		try 
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			System.out.println("商为" + one / two);
		 catch (ArithmeticException e) 
			System.out.println("除数不能为0");
			e.printStackTrace();
		 catch (InputMismatchException e) 
			System.out.println("请输入整数");
			e.printStackTrace();
		 catch (Exception e) 
			System.out.println("其他异常");
			e.printStackTrace();
		 finally 
			System.out.println("运算结束");
		

	

测试输入:12 0
输出:

除数不能为0
java.lang.ArithmeticException: / by zero
运算结束
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:14)

测试输入:12 a

请输入整数
java.util.InputMismatchException
运算结束
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemoOne.main(TryDemoOne.java:13)

终止finally执行的方法

catchfinally不能脱离try独自出现。
一般情况下,不管finally一定会执行,有没有终止finally执行的方法呢?

答:有。System.exit(1);



则若catch的代码改成:

catch (ArithmeticException e) 
			System.exit(1);
			System.out.println("除数不能为0");
			e.printStackTrace();
		

那么他会在System.exit(1);时终止程序的执行,就不会允许到finally了。

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

类如下:

public class Test 
	public static void main(String[] args) 
		int res=test();
		System.out.println("商为"+res);
	
	
	public static int test() 
		try 
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			return one/two;
		 catch (ArithmeticException e) 			
			System.out.println("除数不能为0");
			return 0;		
		 catch (Exception e) 
			System.out.println("其他异常");
			e.printStackTrace();
		 finally 
			System.out.println("运算结束");
			return -10000;
		
		
	


测试输入为:2 1
输出:

运算结束
商为-10000

输入为:2 0
输出:

除数不能为0
运算结束
商为-10000

由此可见,在try-catch-finally中,三个语句都有return的情况下,永远都是返回finally中的return

也就是说,从语法的角度上finally块中带return是没问题的,但从逻辑的角度上,finally块中的return是一定会被返回的——这也就是为什么finally中有return就会有Warning:

如果把finally中的return删去,即:

public static int test() 
		try 
			System.out.println("请输入两个整数:");
			Scanner input = new Scanner(System.in);
			int one = input.nextInt(), two = input.nextInt();
			return one/two;
		 catch (ArithmeticException e) 
			System.out.println("除数不能为0");
			return 0;		
		 catch (Exception e) 
			System.out.println("其他异常");
			e.printStackTrace();
			return 0;
		 finally 
			System.out.println("运算结束");
		
		
	

再测试一下:
输入:2 1
输出:

运算结束
商为2

输入:2 0
输出:

除数不能为0
运算结束
商为0

这就正确了。

使用throws声明异常类型

可以通过throws声明将要抛出何种类型的异常,通过throw将产生的异常抛出。如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。

throws语句用在方法定义时声明该方法要抛出的异常类型

public void method() throws Exception 1,Exception 2
	//可能产生异常的代码

当方法抛出异常列表中的异常时,方法将不对这些类型及其子类作处理,而抛向调用该方法的方法,由他去处理。

举个例子:
测试类代码如下:

public class TryDemo3 
	public static void main(String[] args) 
		
		try 
			int res = test();
			System.out.println("商为" + res);
		 catch (ArithmeticException e) 
			// TODO Auto-generated catch block
			System.out.println("除数不允许为0");
			e.printStackTrace();
		
	

	//表示这个函数可能会抛出ArithmeticException这样的异常
	public static int test() throws ArithmeticException 

		System.out.println("请输入两个整数:");
		Scanner input = new Scanner(System.in);
		int one = input.nextInt(), two = input.nextInt();
		System.out.println("运算结束");
		return one / two;
	


测试输入1:2 1
输出:

运算结束
商为2

测试输入2:2 0
输出:

运算结束
java.lang.ArithmeticException: / by zero
除数不允许为0
	at ExceptionProj/com.test.TryDemo3.test(TryDemo3.java:26)
	at ExceptionProj/com.test.TryDemo3.main(TryDemo3.java:10)

这段代码能处理别的异常吗?答案是不可以。
测试输入3:2 a
输出:这个异常是虚拟机在处理。

Exception in thread "main" java.util.InputMismatchException
	at java.base/java.util.Scanner.throwFor(Scanner.java:939)
	at java.base/java.util.Scanner.next(Scanner.java:1594)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
	at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
	at ExceptionProj/com.test.TryDemo3.test以上是关于从零开始的Java开发1-5-1 异常:分类异常处理try-catch-finally多重catchfinally与returnthrowsthrow关键字自定义异常类异常链的主要内容,如果未能解决你的问题,请参考以下文章

java异常设计

Java开发从零开始!java游戏服务器开发教程

Java开发从零开始!java游戏服务器开发教程

从零开始学习springBoot(全局异常捕捉)

从零开始学习Java多线程

基于Pytorch热门深度学习框架 从零开发NLP聊天机器人