异常引入:
程序中的异常
示例:给出除数和被除数,求商
如果除数为0,出异常
如果除数或者被除数不是数字,出异常
public class TestException {
public static void main(String[] args) {
int a,b,c;//a:被除数 b:除数 c:商
Scanner scanner = new Scanner(System.in);
System.out.println("请输入被除数");
a = scanner.nextInt();
System.out.println("请输入除数");
b = scanner.nextInt();
//求商
c = a / b;
System.out.println("商为:"+c);
scanner.close();
}
}
这里输入了字符a,虚拟机直接炸了,这里有异常没有处理。
异常概念
异常分类
Error
Error类层次描述了Java运行时系统内部错误和资源耗尽错误,一般指与JVM或动态加载等相关的问题,如虚拟机错误,动态链接失败,系统崩溃等。
这类错误是我们无法控制的,同时也是非常罕见的错误。所以在编程中,不去处理这类错误。
打开JDK包:java.lang.Error,查看他的所有子类
注:我们不需要管理Error!
Exception
所有异常类的父类,其子类对应了各种各样可能出现的异常事件。
Exception分类
运行时异常Runtime Exception(unchecked Exception)
可不必对其处理,系统自动检测处理
一类特殊的异常,如被 0 除、数组下标超范围等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大
检查异常 Checked Exception
必须捕获进行处理,否则会出现编译错误
注意:只有Java提供了Checked异常,体现了Java的严谨性,提高了Java的健壮性。同时也是一个备受争议的问题。
异常处理的分类:
1. try-catch-finally 直接处理异常
2. throw 抛出异常
throws 声明异常
try-catch
catch块中如何处理异常
输出用户自定义异常信息
System.err.println(“除数不能为零。”);
System.err.println(“被除数和除数必须是整数。”);
调用异常对象的方法输出异常信息
toString ( )方法,显示异常的类名和产生异常的原因
void printStackTrace() 输出异常的堆栈信息
String getMessage()返回异常信息描述字符串,是printStackTrace()输出信息的一部分
继续向上抛出异常
throw e
try-catch-finally
在try-catch块后加入finally块,可以确保无论是否发生异常,finally块中的代码总能被执行
无异常 try-finally
有异常 try-catch-finally
通常在finally中关闭程序块已打开的资源,比如:文件流、释放数据库连接等。
finally块中语句不执行的唯一情况
异常处理代码中执行System.exit(1)退出Java虚拟机
Finally块的具体执行过程
执行try或catch中代码
遇到return/throw,先执行finally中语句块
执行return/throw
多重catch
一段代码可能会引发多种类型的异常
当引发异常时,会按顺序来查看每个 catch 语句,并执行第一个与异常类型匹配的catch语句
执行其中一条 catch 语句后,其后 catch 语句将被忽略
在安排catch语句的顺序时,首先应该捕获最特殊的异常, 然后再逐渐一般化,即先子类后父类
抛出或声明异常
声明异常throws
当Checked Exception产生时,不一定立刻处理它,可以再把异常Throws出去
如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以逗号隔开
子类声明的异常范围不能超过父类声明范围
父类没有声明异常,子类也不能
不可抛出原有方法抛出异常类的父类或上层类
手动抛出异常throw
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要手工创建并抛出。
在捕获一个异常前,必须有一段代码先生成异常对象并把它抛出。这个过程我们可以手工做,也可以由JRE来实现,但是他们调用的都是throw子句。
注意抛出运行时异常和Checked异常的区别
抛出Checked异常,该throw语句要么处于try块中,要么方法签名中石油throws抛出
抛出运行时异常,没有以上要求
自定义异常
在程序中,可能会遇到任何标准异常类都没有充分的描述清楚的问题,这种情况下可以创建自己的异常类
从Exception类或者它的子类派生一个子类即可
习惯上,定义的类应该包含2个构造器:一个是默认构造器,另一个是带有详细信息的构造器
例子:
//自定义异常(检查类的性别异常)
@SuppressWarnings("serial")
public class GenderException extends Exception{
public GenderException() {
super("性别只能设置为男或者女");
}
/*public GenderException(String message) {
super(message);
}*/
@Override
public String getMessage() {
return super.getMessage();
}
}
测试:
学生
public class Student {
private String name;
private int age;
private String gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) throws GenderException {
if(gender.equals("男") || gender.equals("女")){
this.gender = gender;
}else{
throw new GenderException();
}
}
}
测试类:
/**
* 自定义异常
* 自定义异常类 extends Exception{
* }
* 自定义异常类 extends RuntimeException{
* }
*
* @author Administrator
*
*/
public class Test1 {
public static void main(String[] args) {
Student student = new Student();
try {
student.setGender("不男不女");
} catch (GenderException e) {
System.out.println(e.getMessage());
System.out.println(e.getLocalizedMessage());
}
}
}
异常链
使用场合举例
在分层结构中,将底层原始异常直接传递给用户是一种不负责的表现
对于普通用户,底层异常专业性强,不友好,对他们没有什么帮助
对于恶意用户,可以利用抛出的异常信息作为破坏的线索
解决方案:异常转译
使用catch块捕获异常,并throw一个新的更友好的业务异常
可以保证底层的异常信息不扩散到用户界面,避免暴露更多的实现细节
异常转译也称为异常链
- 需求:
- 输入被除数、除数、求商,并打印结果
- 问题1:输入类型不匹配的时候,直接抛出异常( java.util.InputMismatchException)
- 提示问题过于专业
- 问题2:0不能作为除数( java.lang.ArithmeticException: / by zero)
- 提示专业
-
- v1.3 使用异常解决问题,多个catch
-
- 多个catch之间有顺序要求: 子类在前,父类在后
- 一个catch可以存在多个异常 InputMismatchException | IndexOutOfBoundsException e
public class TestException4 {
public static void main(String[] args) {
int a,b,c = 0;//a:被除数 b:除数 c:商
Scanner scanner = new Scanner(System.in);
try {
System.out.println("请输入被除数");
a = scanner.nextInt();
System.out.println("请输入除数");
b = scanner.nextInt();
//求商
c = a / b;
System.out.println("商为:"+c);
}catch (ArithmeticException e) {
System.out.println("0不能做除数");
//终止虚拟机(finally中的代码,不会被执行)
//System.exit(1);
return;
}
catch (InputMismatchException | IndexOutOfBoundsException e) {
System.out.println("输入类型不匹配");
return;
}
catch (Exception e) {
System.out.println("其他异常");
System.out.println(e.getMessage());//获取错误信息
return;
}finally {
//finally中 扫描器的关闭,流的关闭,数据的关闭,socket的关闭
scanner.close();
System.out.println("谢谢使用");
//在方法结束前执行
}
}
}
小结
异常类型
一张图
异常处理
五个关键字(try, catch, finally, throws, throw)
先逮小的,再逮大的
自定义异常
注意
try-finally也可以呀
遇到了return和throw还会执行finally语句;遇到System.exit()就不会执行finally语句了
throw了运行时异常,则方法声明中不需要throws
如果在编程中写的东西有异常,1,可以用try catch 捕获异常,弹出友好提示
2,可以把异常抛出,也就是用throws声明,throw抛出,不处理,告诉别人这个方法有异常,让别人用的时候处理。
编码题:
1、编写程序接收用户输入分数信息,如果分数在0—100之间,输出成绩。如果成绩不在该范围内,抛出异常信息,提示分数必须在0—100之间。
要求:使用自定义异常实现
/**
* 分数范围异常类型
* @author Administrator
*
*/
public class ScoreScopeException extends RuntimeException{
public ScoreScopeException() {
super();
}
public ScoreScopeException(String message) {
super(message);
}
}
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("请输入分数:");
int score = scanner.nextInt();
if (score < 0 || score > 100) {
throw new ScoreScopeException("分数必须在0-100之间");
}
System.out.println("分数为:" + score);
} catch (ScoreScopeException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
2、写一个方法void isTriangle(int a,int b,int c),判断三个参数是否能构成一个三角形, 如果不能则抛出异常IllegalArgumentException,显示异常信息 “a,b,c不能构成三角形”,如果可以构成则显示三角形三个边长,在主方法中得到命令行输入的三个整数, 调用此方法,并捕获异常。
public class Test {
public static void main(String[] args) {
//输入三条边
System.out.println("请输入三条边长");
Scanner reader = new Scanner(System.in);
int a = reader.nextInt();
int b = reader.nextInt();
int c = reader.nextInt();
//判断是否是三角形
isTriangle(a, b, c);
}
private static void isTriangle(int a, int b, int c) {
//三条边都不能是负数
if (a < 0 || b < 0 || c < 0) {
throw new IllegalArgumentException("三条边不能是负数");
}
//判断是否构成三角形
if (a + b > c && a + c > b && b + c > a) {
System.out.println("三角形的边长分别为" + "a=" + a + " " +
"b=" + b + " " + "c=" + " " + c);
} else {
throw new IllegalArgumentException(a + "," + b + "," + c
+ "不能构成三角形!");
}
}
}