1、基本概念
异常指程序运行过程中出现的非正常现象,例如用户输入错误、除数为零、需要处理的文件不存在、数组下标越界等。
在Java的异常处理机制中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。
所谓异常处理,就是指程序在出现问题时依然可以正确的执行完。
Java是采用面向对象的方式来处理异常的。处理过程:
1. 抛出异常: 在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。
2. 捕获异常: JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。
2、异常分类
-
Error
Error是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。
Error表明系统JVM已经处于不可恢复的崩溃状态中。我们不需要管它。
-
Exception
Exception是程序本身能够处理的异常,如:空指针异常(NullPointerException)、数组下标越界异常(ArrayIndexOutOfBoundsException)、类型转换异常(ClassCastException)、算术异常(ArithmeticException)等。
Exception类是所有异常类的父类,其子类对应了各种各样可能出现的异常事件。 通常Java的异常可分为:
-
RuntimeException 运行时异常
注意:
-
在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。
-
运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
-
-
CheckedException 已检查异常
这类异常在编译时就必须做出处理,否则无法通过编译。
-
-
Error和Exception区别
-
我开着车走在路上,一头猪冲在路中间,我刹车。这叫一个异常。
-
我开着车在路上,发动机坏了,我停车,这叫错误。系统处于不可恢复的崩溃状态。发动机什么时候坏?我们普通司机能管吗?不能。发动机什么时候坏是汽车厂发动机制造商的事。
-
3、异常处理
-
捕获异常:获异常是通过3个关键词来实现的:try-catch-finally。
1. try:
try语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该条语句中后面的代码。代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理。
一个try语句必须带有至少一个catch语句块或一个finally语句块 。
注意事项
当异常处理的代码执行结束以后,不会回到try语句去执行尚未执行的代码。
2. catch:
每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
- 常用方法,这些方法均继承自Throwable类 。
toString ()方法,显示异常的类名和产生异常的原因
getMessage()方法,只显示产生异常的原因,但不显示类名。
printStackTrace()方法,用来跟踪异常事件发生时堆栈的内容。 - catch捕获异常时的捕获顺序:
如果异常类之间有继承关系,在顺序安排上需注意。越是顶层的类,越放在下面,再不然就直接把多余的catch省略掉。 也就是先捕获子类异常再捕获父类异常。
2. finally:
n-有些语句,不管是否发生了异常,都必须要执行,那么就可以把这样的语句放到finally语句块中。
n-通常在finally中关闭程序块已打开的资源,比如:关闭文件流、释放数据库连接等。
try-catch-finally语句块的执行过程:
try-catch-finally程序块的执行流程以及执行结果比较复杂。
基本执行过程如下:
程序首先执行可能发生异常的try语句块。如果try语句没有出现异常则执行完后跳至finally语句块执行;如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。catch语句块可以有多个,分别捕获不同类型的异常。catch语句块执行完后程序会继续执行finally语句块。finally语句是可选的,如果有的话,则不管是否发生异常,finally语句都会被执行。
注意:
- 即使try和catch块中存在return语句,finally语句也会执行。是在执行完finally语句后再通过return退出。
- finally语句块只有一种情况是不会执行的,那就是在执行finally之前遇到了System.exit(0)结束程序运行。
- 常用方法,这些方法均继承自Throwable类 。
-
声明异常(throws子句)
当CheckedException产生时,不一定立刻处理它,可以再把异常throws出去。
注意事项
- 方法重写中声明异常原则:子类重写父类方法时,如果父类方法有声明异常,那么子类声明的异常范围不能超过父类声明的范围。
4、自定义异常
-
在程序中,可能会遇到JDK提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,这种情况下可以创建自己的异常类,即自定义异常类。
-
自定义异常类只需从Exception类或者它的子类派生一个子类即可。
-
自定义异常类如果继承Exception类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException类。
-
习惯上,自定义异常类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。
使用异常机制的建议
-
要避免使用异常处理代替错误处理,这样会降低程序的清晰性,并且效率低下。
-
处理异常不可以代替简单测试—只在异常情况下使用异常机制。
-
不要进行小粒度的异常处理—应该将整个任务包装在一个try语句块中。
-
异常往往在高层处理(先了解!后面做项目会说!) 。
5、练习题
一、选择题
1.以下关于异常的代码的执行结果是( C)。(选择一项)
public class Test {
public static void main(String args[]) {
try {
System.out.print("try");
return;
} catch(Exception e){
System.out.print("catch");
}finally {
System.out.print("finally");
}
}
}
A.try catch finally
B.catch finally
C.try finally
D.try
2.在异常处理中,如释放资源、关闭文件等由( C)来完成。(选择一项)
Atry子句
B.catch子句
C.finally子句
D.throw子句
3.阅读如下Java代码,其中错误的行是(AC )。(选择二项)
public class Student {
private String stuId;
public void setStuId(String stuId) throw Exception { // 1
if (stuId.length() != 4) { // 2
throws new Exception("学号必须为4位!"); // 3
} else {
this.stuId = stuId; //4
}
}
}
A.1
B.2
C.3
D.全部正确
4.下面选项中属于运行时异常的是(BC )。(选择二项)
A.Exception和SexException
B.NullPointerException和InputMismatchException
C.ArithmeticException和ArrayIndexOutOfBoundsException
D.ClassNotFoundException和ClassCastException
5.阅读如下Java代码,在控制台输入"-1",执行结果是(B)。(选择一项)
public class Demo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("请输入数字:");
try {
int num = input.nextInt();
if (num < 1 || num > 4) {
throw new Exception("必须在1-4之间!");
}
} catch (InputMismatchException e) {
System.out.println("InputMismatchException");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
A.输出:InputMismatchException
B.输出:必须在1-4之间!
C.什么也没输出
D.编译错误
二、简答题
-
Error和Exception的区别。
Error:
Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。Exception:
Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。 -
Checked异常和Runtime异常的区别。
1、运行时异常:包括RuntimeException及其所有子类。不要求程序必须对它们作出处理,比如InputMismatchException、ArithmeticException、NullPointerException等。即使没有使用try-catch或throws进行处理,仍旧可以进行编译和运行。如果运行时发生异常,会输出异常的堆栈信息并中止程序执行。
2、Checked异常(非运行时异常):除了运行时异常外的其他异常类都是Checked异常。程序必须捕获或者声明抛出这种异常,否则出现编译错误,无法通过编译。处理方式包括两种:通过try-catch捕获异常,通过throws声明抛出异常从而交给上一级调用方法处理。 -
Java异常处理中,关键字try、catch、finally、throw、throws分别代表什么含义?
try 用来指定一块预防所有“异常”的程序;
catch 子句紧跟在 try 块后面,用来指定你想要捕捉的“异常”的类型;
throw 语句用来明确地抛出一个“异常”;
throws 用来标明一个成员函数可能抛出的各种“异常”;
Finally 为确保一段代码不管发生什么“异常”都被执行一段代码;
-
throws和throw的区别。
throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
throws语句用在方法声明后面,表示再抛出异常,由该方法的调用者来处理。throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常。
throw是具体向外抛异常的动作,所以它是抛出一个异常实例。throws说明你有那个可能,倾向。
throw的话,那就是你把那个倾向变成真实的了。
三、编码题
- 编写程序接收用户输入分数信息,如果分数在0—100之间,输出成绩。如果成绩不在该范围内,抛出异常信息,提示分数必须在0—100之间。
要求:使用自定义异常实现。
package com.zry.day08.lx;
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入用户分数:");
double grade=scanner.nextDouble();
Grade g=new Grade();
try {
g.setGrade(grade);
g.printGrade();
} catch (IllegalGradeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class IllegalGradeException extends Exception{
public IllegalGradeException() {
super();
// TODO Auto-generated constructor stub
}
//带有详细信息的构造器,信息存储在message中
public IllegalGradeException(String message) {
super(message);
}
}
class Grade{
private double grade;
public double getGrade() {
return grade;
}
public void setGrade(double grade) throws IllegalGradeException {
if(grade<0 || grade>100) {
throw new IllegalGradeException("分数必须在0-100之间");
}
this.grade=grade;
}
public void printGrade() {
System.out.println("成绩为:"+grade);
}
}
-
写一个方法void isTriangle(int a,int b,int c),判断三个参数是否能构成一个三角形, 如果不能则抛出异常IllegalArgumentException,显示异常信息 “a,b,c不能构成三角形”,如果可以构成则显示三角形三个边长,在主方法中得到命令行输入的三个整数, 调用此方法,并捕获异常。
package com.zry.day08.lx; import java.util.Scanner; public class Test2 { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.print("请输入边长a:"); int a=sc.nextInt(); System.out.print("请输入边长b:"); int b=sc.nextInt(); System.out.print("请输入边长c:"); int c=sc.nextInt(); isTriangle(a, b, c); } public static void isTriangle(int a ,int b,int c) { if(a<=0 ||b<=0 ||c<=0) { throw new IllegalArgumentException("a,b,c不能构成三角形(a,b,c可能为0或者负数)"); }else { if(a+b<=c || a+c<=b || b+c<=a) { throw new IllegalArgumentException("a,b,c不能构成三角形"); }else { System.out.printf("三角形边长:a=%d,b=%d,c=%d",a,b,c); } } } }
-
编写一个计算N个学生分数平均分的程序。程序应该提示用户输入N的值,如何必须输入所有N个学生分数。如果用户输入的分数是一个负数,则应该抛出一个异常并捕获,提示“分数必须是正数或者0”。并提示用户再次输入该分数。
package com.zry.day08.lx;
import java.util.Scanner;
public class Test3 {
public static void main(String[] args) {
System.out.println("请输入学生人数:");
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
GradeAvg g=new GradeAvg(n);
g.avgGrade();
}
}
class IllegalGradeException extends Exception{
public IllegalGradeException() {
super();
// TODO Auto-generated constructor stub
}
public IllegalGradeException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
class GradeAvg{
int num;
double sum;
double avg;
public GradeAvg(int num) {
super();
this.num = num;
}
public void avgGrade() {
int n=num;
while(n!=0) {
System.out.println("请输入一个成绩:");
Scanner s=new Scanner(System.in);
double temp=s.nextDouble();
if(temp<0) {
try {
throw new IllegalGradeException("分数必须是正数或者0");
} catch (IllegalGradeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
n--;
sum+=temp;
}
}
avg=sum/num;
System.out.printf("%d个学生的平均分为:%.2f",num,avg);
}
}