内容导航
在程序开发中,经常会出现各种突发问题,Java语言作为主流的面向对象开发语言,提供了强大的异常处理机制。Java的所有异常都是会封装到一个类中,在程序错误的时候,及时抛出异常,给程序处理,解决问题的机会,提高了应用的容错性和健壮性。
认识异常
在生活中,异常情况随时都有可能会发生。下面将从生活中的异常过度到程序的异常。
在日常生活中,几乎随时随处可能会发生意外情况,以学校生活为例,在上学的路上发生交通事故或拥堵、教员生病、教室投影仪不能使用,机房网络感染病毒等等,这些都是意外情况。虽然意外情况是偶然事件,但是一但发生就会带来不小麻烦,影响正常的学习生活。同理,在程序中也会发生类似的情况,下面看看程序中的异常。
在程序开发中,程序员虽然会尽量避免错误的发生,但是总会遇到一些不可预期的问题,如除法运算时除数为0,数组下标越界、数据类型不一致、内存不足、栈溢出等,这些就是异常。下面将以代码示列来介绍什么是程序中的异常。
示列1:
package abnormal;
import java.util.Scanner;
/**
* 计算果商供应量
*/
public class Test {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入果实的采摘量(公斤):");
int weight = scanner.nextInt();
System.out.println("请输入果商数(家):");
int num = scanner.nextInt();
System.out.println("每家果商供应量"+weight/num+"公斤水果。");
System.out.println("欢迎再来,预祝生意兴隆!");
}
}
运行结果:
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
8
每家果商供应量400公斤水果。
欢迎再来,预祝生意兴隆!
Process finished with exit code 0
因为本程序并没有产生任何异常,所以代码可以全部执行完毕。但有时候,输入错误数据错误会导致程序出错如下例子:
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at abnormal.Test.main(Test.java:15)
Process finished with exit code 1
在上面的执行过程中,因为输入的果商为0即在运算中除数为0,所以程序出去了异常。另外如果输入的数据类型不是整数,那么也会发生异常,如下例子:
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
八家
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 abnormal.Test.main(Test.java:14)
Process finished with exit code 1
从运行结果可以发现,当异常产生后,会在控制台输入异常提示信息,同时将中止执行异常后面的程序,最后两条输出语句也将不会继续执行。
提示:
关于除数不能为0的说明如下。
在计算机的发展中有两大计算"杀手",一个是断电,另外一个是除数为0,因为除数为0在数学上是无穷大的,对于计算机来说,如果是无穷大,则意味着内存将被全部占满,所以,在程序开发中,务必避免除数为0的情况。
对于一个程序设计人员来说,需要尽量预知所有可能发生的情况,从而保障程序中所有糟糕情况下都能运行,那么应该怎么解决人为造成的问题呢?根据以上产生的错误,可以所有if-else
语句进行改造如下
示列2
package abnormal;
import java.util.Scanner;
/**
* 计算果商供应量(使用if-else语句处理异常)
*/
public class Test2 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入果实的采摘量(公斤):");
int weight = 0;
if (input.hasNextInt()) {
weight = input.nextInt();
}else{
System.err.println("果实采摘量必须是整数!");
System.exit(1);
}
System.out.println("请输入果商数(家):");
int num = 0;
if (input.hasNextInt()){
num = input.nextInt();
if (num<=0){
System.err.println("果商数必须大于零!");
System.exit(1);
}
}else{
System.out.println("果商数必须输入整数!");
System.exit(1);
}
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
System.out.println("欢迎再来,预祝生意兴隆!");
}
}
使用if-else
语句进行处理后会发现,程序增加了很多判断和分支。以上例子只考虑了两种可能发生的错误,代码量就会急剧增加了。但实际上,在程序开发中,意外情况是不能穷举的,程序可能发生的异常远远都于程序员能考虑到的,如果每次都在实现真正业务逻辑之前,都需要不厌其烦地考虑各种可能出错的情况,针对各种错误情况给出补救措施,那么是多么乏味的的事情啊!
对于上面的错误处理机制,总结有如下两个缺点。/
无法穷举所有的异常情况
- 因为受程序员知识的限制,异常情况总比可以考虑到的情况多,总会有"漏网之鱼",所以程序总不够健壮
影响程序可读性,维护难度高
- 这种错误处理和业务逻辑混杂的代码严重影响程序的可读性,增加程序维护的难度
异常处理机制
在程序设计时,必须考虑到可能发生的异常事件并进行相对应的处理,这样才能保证程序正常运行。
JAVA的异常处理机制也秉承着面向对象的基本思想,在Java中,所有的异常都定义为类。除了内置的异常类,Java也可以自定义异常类。此外,Java的异常处理机制也允许自行抛出异常。
那么应该如何使用java异常处理机制自动处理异常呢?
异常处理结果
Java针对异常处理提供了 try、catch、finally、throws、throw
五个核心关键字,其中前三个关键字就可以组成常用的异常处理结构,语法如下所示
语法:
try{
//有可能出去异常的语句
}[catch(异常类型 异常对象){
//异常处理语句
}][finally{
//一定会运行到的语句
}]
其中try
语句用于监听,将可能抛出异常的代码放在try
语句块内,当try
语句块发生异常时,异常就被抛出;catch
语句用来捕获try
语句块中抛出的异常;flnally
语句块总会被执行,主要用于回收try
语句块内打开的资源,如数据库连接、网格连接和磁盘文件。
注意:
以上格式中的catch
语句、flnally
语句都可选。实际上,这并不是表示catch
语句、flnally
语句可以同时消失。异常格式的常见组合有try-catch
、try-catch-flnally
、try-flnally
三种。
在程序中处理异常
既然程序员通过自行判断处理异常有诸多不便,那么下面运用Java异常处理机制修改示列1,让程序能够自行判断并处理异常。
使用try-catch语句处理异常
使用try-catch
语句处理异常,关键代码如下所示
示列3
package abnormal;
import java.util.Scanner;
/**
* 使用try-catch语句处理异常
*/
public class Test3 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
System.out.println("欢迎再来,预祝生意兴隆!");
}catch (Exception ex){
System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
}
}
}
以上程序使用了异常处理语句,当程序程序异常时,异常会被try
语句监听到,然后被JVM抛出,被catch
语句捕获进行处理,执行catch
语句内的异常处理代码,不再需要程序员自行编写if
语句进行判断,简化了代码。示列3运行结果如下:
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
0
出现错误:采摘量和果商数应为整数,果商数应大于零!
Process finished with exit code 0
使用Java异常处理机制的目的是帮助成都学院分析异常并解决异常。示列3采用输出错误提示的方式处理异常,不能明确地描述异常类型,不能精确定位问题所在。更好的方法是使用异常类中提供的printStackTrace()
方法进行异常信息的完整输出,如下所示
示列4
package abnormal;
import java.util.Scanner;
/**
* 使用try-catch语句处理异常
*/
public class Test4 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
System.out.println("欢迎再来,预祝生意兴隆!");
}catch (Exception ex){
System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
//输出完整异常信息
ex.printStackTrace();
}
}
}
当输入的果商为0时,示列4的运行结果如下
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
0
出现错误:采摘量和果商数应为整数,果商数应大于零!
java.lang.ArithmeticException: / by zero
at abnormal.Test4.main(Test4.java:16)
Process finished with exit code 0
当输入数据非整数时结果如下
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
八斤
出现错误:采摘量和果商数应为整数,果商数应大于零!
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 abnormal.Test4.main(Test4.java:15)
Process finished with exit code 0
从上述结果可以看出,Java异常处理机制可以自动捕获不同类型的异常,如果try
语句在执行过程中遇到异常,则异常处之后的代码将不再被执行,系统会将异常信息封装成相应类型的异常对象,包含异常类型,异常查询时程序的运行状况及对该异常的详细描述。如果这个异常对象与catch
语句中声明的异常类型相匹配,则会被自动捕获,Java异常处理机制把该异常对象赋值给catch关键字后的异常参数,执行catch
语句块中的语句。在示列4中,通过printStackTrace()
语句读取异常信息。Exception
类型的常用方法如下
方法 | 描述 |
---|---|
Void printStackTrack() | 输出异常堆栈信息,堆栈信息中中包含程序运行当前类的执行流程,它将输出从方法调用处到异常抛出处的方法调用序列 |
String getMessage() | 返回异常详细信息。该信息描述异常产生的原因,是printStackTrack()方法输出信息的一部分 |
提示:
因为使用printStackTrack()
方法输出的异常详细是最完整的,所以后续会使用该方法进行异常信息输出。在其输出结果中,可以自下向上观察程序的执行轨迹,最终定位到异常发生的位置。
使用try-catch-finally语句处理异常
在示列4中,try
语句块执行到System.out.println("每家果商供应量" + weight / num + "公斤水果。");
语句时出现异常,程序将自动跳转到catch
语句块中处理异常,后续输出语句将不再被执行。如果需要进行执行后面的语句System.out.println("欢迎再来,预祝生意兴隆!");
则需要添加flnally
语句。代码如下
示列5
package abnormal;
import java.util.Scanner;
public class Test5 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
}catch (Exception ex){
System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
//输出完整异常信息
ex.printStackTrace();
}finally {
System.out.println("欢迎再来,预祝生意兴隆!");
}
}
}
运行结果如下
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
0
出现错误:采摘量和果商数应为整数,果商数应大于零!
java.lang.ArithmeticException: / by zero
at abnormal.Test4.main(Test4.java:16)
欢迎再来,预祝生意兴隆!
Process finished with exit code 0
在示列5中增加了finally
语句,这样可以在异常处理过程中,无论是是否出现异常,最终都会执行finally
语句块中的代码。finally
语句往往会在开发中进行一些资源释放操作,无论代码是否正常执行都必须完成这些操作。
需要注意的是,即使在try
语句块或catch
语句块中添加return
语句,finally
语句也会被正常执行,代码如下
示列6
package abnormal;
import java.util.Scanner;
public class Test6 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
//不会影响finally语块执行
return;
}catch (Exception ex){
System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
//输出完整异常信息
ex.printStackTrace();
//不会影响finally语块执行
return;
}finally {
System.out.println("欢迎再来,预祝生意兴隆!");
}
}
}
以上代码的执行结果,与上上代码块运行结果相同。
那么,是否有finally
语句块不被执行的情况呢?在异常处理代码中执行System.exit(1)
,将退出JVM,代码如下
示列7
package abnormal;
import java.util.Scanner;
/**
* 使用try-catch语句处理异常
*/
public class Test7 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
} catch (Exception ex) {
System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
//输出完整异常信息
ex.printStackTrace();
//finally语句块不被执行的唯一条件
System.exit(1);
} finally {
System.out.println("欢迎再来,预祝生意兴隆!");
}
}
}
运行结果如下
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
0
出现错误:采摘量和果商数应为整数,果商数应大于零!
java.lang.ArithmeticException: / by zero
at abnormal.Test7.main(Test7.java:16)
Process finished with exit code 1
从以上可以看出,使用了System.exit(1)
,finally
语句块就不再被执行。
多重catch语句
在之前的示列中,程序出现的全部异常有统一的处理方式。但在实际开发中,有时会希望针对不同的异常类型采取不同的处理方法,这样就需要使用多重catch
语句进行异常处理。如下所示
语法:
try{
//有可能出现异常的语句
}[catch(异常类型 1 异常对象){
//异常处理语句
}catch(异常类型 2 异常对象){
//异常处理语句
}......]
[finally{
//一定会运行到的语句
}]
在示列5中,可能会出现算术异常(ArithmeicException
)和输入类型不匹配异常(InputMismatch-Exception
),由于程序中只定义了一个catch
语句块且其参数类型为Exception
,是以上两种异常类型的父类,所以能够被该catch
语句全部捕获,执行相同的异常处理程序,如果需要针对可能发生的异常类型采取不同的处理方式,则需要使用多个catch
语句块分别捕获不同的异常类型,输出不同的提示信息。最后一个catch
语句使用Exception
类型作参数,因为它是所有异常类型的父类,可以处理前面的catch
语句未匹配的异常。这样,当try
块中出现异常时,所封装的异常对象就会从多重catch
语句所提供的参数类型中找到能够匹配的一个,执行其中的异常代码,如下所示
示列8
package abnormal;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test8 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
} catch (ArithmeticException ex) {
System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
//输出完整异常信息
ex.printStackTrace();
} catch (InputMismatchException ex){
System.err.println("出现错误:果实采摘量和果商数应为整数!");
}catch (Exception ex){
System.err.println("其他未知错误!");
ex.printStackTrace();
}finally {
System.out.println("欢迎再来,预祝生意兴隆!");
}
}
}
如果输入的果商数为0或小于0,则会发生ArithmeticException
算术异常,被第一条catch语句捕获,运行结果如下
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
0
出现错误:采摘量和果商数应为整数,果商数应大于零!
java.lang.ArithmeticException: / by zero
at abnormal.Test8.main(Test8.java:15)
欢迎再来,预祝生意兴隆!
Process finished with exit code 0
如果输入的果商数为非整数类型,则会发生InputMismatchException
类型不匹配异常,被第二条catch
语句捕获,运行结果如下
请输入果实的采摘量(公斤):
3200
请输入果商数(家):
八家
出现错误:果实采摘量和果商数应为整数!
欢迎再来,预祝生意兴隆!
Process finished with exit code 0
注意:
- 当
try
语句块中发生异常时,系统将会按照从上到下的顺序依次检测每个catch
语句,当匹配到某条catch
语句后,后续其他catch
语句块将不再执行。 - 以
Exception
作为参数的catch
语句必须放在最后的位置,否则所有的异常都会被捕获,后面以其子类异常作为参数的catch
语句将得不到被执行的机会。
异常分类以处理流程
前面已经介绍了异常处理及对程序正常执行的重要性。为了能更好的理解异常的继承结构,必须理解异常的继承结构及处理流程。下面举例了两个异常类的继承更新。
如何打开类或接口的继承结构
选中要打开的类 Ctrl+U 快捷键打开
功能介绍:
ArithmeticException
类的继承更新如图
InputMismatchException
类的继承如图
通过这两个异常类可以发现,所有的异常类型都继承Java.lang.Throwable
类,它有两个重要的子类,分别是Error类和Exception类,异常体系结构图如下所示
下面分别 介绍Error类和Exception类。
- Error类
Java.lang.Error
类包含了仅靠程序本身无法恢复的严重错误,一般与JVM相关的问题,是Java运行环境的内部错误或硬件问题,如内存资源不足,JVM错误等。应用程序不应该抛出这种类型的对象(一般由JVM抛出)。假如出现这种错误,除尽力使程序安全退出外,其他方法是无能为力的。因此在进行程序设计时,应该更关注Exception
类。
- Exception类
Java.lang.Exception
类是程序本身可以处理的异常,可分为运行时(RunTimeExcption
)异常与检查(Checked)异常。
- 运行时异常:可以在程序中避免的异常。在类异常在编译代码时不会被编译器检测出来,可以正常编译运行,但当程序进行时发现异常,会输出异常堆栈信息并中止程序运行,程序员可以根据需要使用
try-catch
语句捕获这类异常,如空指针异常、类型转换异常、数组越界异常等,这些异常包括Java.long.RuntimeException
类及其子类,通过这些具体的异常类型,能够判断程序的问题所在。Java程序常见的运行异常如下 - Checked异常:除运行时异常的异常,是由用户错误或问题引起的异常,这是程序员无法预见的。在编译时,编译器会提示这类异常需要捕获。如果不进行捕获,则会出现编译错误。常见的编译错误异常有
FileNotFoundException
异常、SQLException
异常等。
下面通过示列9说明Checked
异常,实现对文件流对象的操作。
### 示列9
package abnormal;
import java.io.File;
import java.io.FileInputStream;
public class FileNoFoundExcep {
public static void main(String[] args){
//创建一个文件流对象
FileInputStream fis = null;
File file = new File("d:\\test.txt");
//初始化文件流对象
fis = new FileInputStream(file);
fis.close();
}
}
在以上代码中,调用文件d:\test.txt,在编译 器中出现未处理异常:FileNotFoundException
异常和IOException
异常如下所示:编译器错误提示
D:\idea源文件\JAVA\练习\src\abnormal\FileNoFoundExcep.java:12:16
java: 未报告的异常错误java.io.FileNotFoundException; 必须对其进行捕获或声明以便抛出
D:\idea源文件\JAVA\练习\src\abnormal\FileNoFoundExcep.java:13:19
java: 未报告的异常错误java.io.IOException; 必须对其进行捕获或声明以便抛出
FileNotFoundException
异常和IOException
异常为Checked
异常,必须进行异常处理才能通过编译,对示列9的代码进行修改,如下所示
package abnormal;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileNoFoundExcep {
public static void main(String[] args){
//创建一个文件流对象
FileInputStream fis = null;
File file = new File("d:\\test.txt");
try {
//初始化文件流对象
fis = new FileInputStream(file);
}catch (FileNotFoundException e){
e.printStackTrace();
}finally {
try {
fis.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
提示:
在Java中进行异常类子类命名时都会使用XXXError或XXXExeption的形式,这样也是为了从名称上帮助开发人员进行区分。
异常处理的完整流程
- 生成异常对象。如果程序在运行过程中出现异常,则JVM自动根据异常的连续实例化一个与之类型匹配的异常对象,如果发生异常的语句存在于
try
语块中,则去匹配对应的catch
语句;否则由JVM进行默认处理,输出异常信息后中断执行。 - 执行
catch
语句块。将所生成的异常对象与try
后每一条catch
语句卡进行比较。如果catch
语句块的异常参数能够与当前产生的异常对象匹配,则执行该catch
语句块进行异常处理;否则,继续向下匹配其他catch
语句块。如果无匹配的catch
语句块。如果无匹配的catch
,则异常对象交由JVM进行默认处理,输出异常信息。 - 执行
finally
语句块。无论最终是否有匹配的catch
语句块,都会执行finally
语句块;如果没有finally
语句块,则忽略。
整个过程类似于方法传参,所生成的异常对象根据catch语句后面的参数进行匹配,依据Java面向对象向上转型的原理,所有异常对象都可以向其父类进行类型转换,所以所有的异常对象都可以所以Exception对象来接收。当
无需对异常进行细分处理时,就可以简单地实现异常处理;否则,就需要多重catch
语句对异常分别处理。
声明异常 throws 关键字
根据设计,不适合立即处理异常,而继续声明异常,交由上一级处理。throws 他不会立即处理异常而会继续声明异常。
throws关键字为方法可能抛出异常的声明,用在所声明的方法时,表示该方法可能抛出此类异常
语法:
pulbic void 方法名() throws 异常类型[,异常类型]{
//方法体
}
throws可以在方法定义处声明一类或多类异常,多类异常间使用逗号分隔。
示列10:
package abnormal;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test10 {
public static void main(String[] args) {
try {
} catch (Exception ex) {
System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
//输出完整异常信息
ex.printStackTrace();
}finally {
System.out.println("欢迎再来,预祝生意兴隆!");
}
}
public static void calculation() throws Exception{
Scanner input = new Scanner(System.in);
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
}
}
在以上代码中,使用throws Exception
声明calculation()
方法可能发生的异常。也可以在throws关键字后声明多个异常。例如
public static void calculation() throws ArithmeticException,InputMismatchException{
Scanner input = new Scanner(System.in);
System.out.println("请输入果实的采摘量(公斤):");
int weight = input.nextInt();
System.out.println("请输入果商数(家):");
int num = input.nextInt();
System.out.println("每家果商供应量" + weight / num + "公斤水果。");
}
示例10中,calculation()方法声明了异常,调用者有以下两种处理方式。
- 通过try-catch语句处理异常
- 通过throws语句继续声明异常,交由上一级处理。
抛出异常 throw 关键字
有时可能会出现以下情况
- 需要根据程序逻辑自定义异常类,这些异常类在java中并未提供,不能抛出。
- 根据业务需要自行选择异常抛出时机或自定义异常处理逻辑。例如:根据不同的逻辑判断条件而抛出异常。
以上情况下,需要throw关键字自行手动抛出异常,由调用者处理抛出异常的实际和异常处理逻辑
语法如下:
throw new 异常名 ([参数列表]);
例如:
throw new Exception();
注意:
用throw关键字抛出的只能为Throwable类或其子类的对象,
错误示例:
throw new String("exception");
使用throw关键字可以根据程序业务逻辑,自行抛出异常,如下所示
示列11
package abnormal;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test11 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.err.println("请输入你的年龄:");
int age = input.nextInt();
try {
Test11 test11 = new Test11();
System.err.println(test11.ShowTicketPrice(age));
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 根据年龄显示票价信息
*/
public String ShowTicketPrice(int age) throws Exception{
if (age<3){
throw new Exception("你录入的年龄有误,3岁以下儿童暂不能观影");
}else if (age >= 60||age <=6){
return "你可以购买半价票25元";
}else {
return "你需要购买全价票50元";
}
}
}
当录入年龄错误时
请输入你的年龄:1
java.lang.Exception: 你录入的年龄有误,3岁以下儿童暂不能观影
at abnormal.Test11.ShowTicketPrice(Test11.java:24)
at abnormal.Test11.main(Test11.java:14)
Process finished with exit code 0
当录入年龄成功时
请输入你的年龄:
6
你可以购买半价票25元
Process finished with exit code 0
自定义异常
当java异常体系提供的异常类型不能满足程序的需要时,可以自定义类
步骤
- 定义异常类,继承Exception类或RuntimeException类。
编写异常类的构造方法,并继承父类的实现。常见的构造方法有四种,可根据需求选择添加实现。构造方法如下所示
//构造方法 1 public Exception(){ super(); } //构造方法2 public Exception(String message){ super(); } //构造方法3 public Exception(String message,Throwable cause){ super(message,cause); } //构造方法4 public Exception(Throwable cause){ super(cause); }
- 实例化自定义异常对象,并使用throw关键字抛出 ,使用自定义异常实现示列12
package abnormal;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test11 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入你的年龄:");
int age = input.nextInt();
try {
Test11 test11 = new Test11();
System.out.println(test11.ShowTicketPrice(age));
} catch (AgeException ex) {
ex.printStackTrace();
}
}
/**
* 根据年龄显示票价信息
*/
public String ShowTicketPrice(int age) throws AgeException {
if (age < 3) {
throw new AgeException("你录入的年龄有误,3岁以下儿童暂不能观影");
} else if (age >= 60 || age <= 6) {
return "你可以购买半价票25元";
} else {
return "你需要购买全价票50元";
}
}
/**
*年龄异常
*/
public class AgeException extends Exception {
public AgeException() {
}
public AgeException(String message) {
super(message);
}
public AgeException(String message, Throwable cause) {
super(message, cause);
}
public AgeException(Throwable cause){
super(cause);
}
}
}
经验:
使用throw关键字可以抛出自定义异常,那么自定义异常有哪些应用场景?
- 项目开发一般由团队成员共同完成,为了统一对外的异常展示方式,可以使用自定义异常。
- 项目中因业务逻辑错误需要抛出异常,但一般是符合Java语法的,所以在Java中不会存在这类异常,例如:年龄异常、性别异常等等。
- 使用自定义异常可以隐藏底层异常,精准定位,异常信息更直观。自定义异常可以抛出明确的异常信息,根据异常名也可以区分其发生的位置,分别进行程序的修改