异常处理
20145217 《Java程序设计》第5周学习总结(1)
教材学习内容总结
程序中总有一些意想不到的状况所引发的错误,我们都知道Java是面向对象的编程,Java中的错误也已对象的方式呈现为java.lang.Throwable的各种子类。第八章内容主要讲述的是Java
中对异常情况的处理方法。
8.1try
、catch
语法
Java
中所有错误都会包装为对象,如果你愿意,可以尝试try
执行程序并捕捉catch
代表错误的对象后做一些处理。使用了try
、catch
语法,Jvm
会尝试执行try
区块中的程序代码,如果发生错误,执行流程会跳离错误发生点,然后比对catch
括号声明的类型,是否符号被抛出的错误对象类型,若果是的话,就执行catch
区块中的程序代码。
对于求几个数平均数的程序Average.java
:
package cc.openhome;
import java.util.Scanner;
public class Average {
public static void main(String[] args) {
Scanner console=new Scanner(System.in);
double sum=0;
int count=0;
while (true){
double number = console.nextDouble();
if (number==0)
break;
sum += number;
count++;
}
System.out.printf("平均%.2f%n",sum/count);
}
}
如果过用户正确输入数据,就会产生正确结果。但如果用户一不小心输入错误,就会出现错误信息:
InputMismatchException
表示不符合Scanner
对象预期,因为Scanner
对象预期下一个字符串本身要代表数字。正像前文所说的,Java中所有的错误都会被打包对象,这时候可以尝试try
捕捉catch
代表错误的对象进行一些处理。即将代码中对应文段改为:
try{
Scanner console=new Scanner(System.in);
double sum=0;
int count=0;
while (true){
double number = console.nextDouble();
if (number==0)
break;
sum += number;
count++;
}
System.out.printf("平均%.2f%n",sum/count);
}catch(InputMismatchException ex){
System.out.println("必须是数字");
}
这时当输入错误信息时会出现:
当然也可以在产生错误时更友好的显示错误,将代码段修改为
while (true){
try{
double number = console.nextDouble();
if (number==0)
break;
sum += number;
count++;
}catch(InputMismatchException ex){
System.out.printf("略过非数字输入%s%n",console.next());
}
}
输入同样信息,输出结果为:
8.2异常继承结构
当程序设计本身出了错误,此时建议使用Exception
或其子类实例来表现,所以通常称错误处理为异常处理。
- 运行时异常都是
RuntimeException
类及其子类异常,如NullPointerException
、IndexOutOfBoundsException
等, 这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的, 程序应该从逻辑角度尽可能避免这类异常的发生。 非运行时异常是RuntimeException
以外的异常,类型上都属于Exception
类及其子类。 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。 如IOException
、SQLException
等以及用户自定义的Exception
异常,一般情况下不自定义检查异常。 error
和exception
是Throwable
的唯二子类。error
表示恢复不是不可能但很困难的情况下的一种严重问题,比如说内存溢出,不可能指望程序能处理这样的情况。exception
表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。- 使用
try
和catch
捕捉对象时也要注意,如果父类异常对象在子类异常对象前被捕捉,则catch
子类对象的区块将永远不会被执行,编译程序会检查出错。
Java
是唯一采用受检异常的语言,这有两个目的:一是文件化,二是提供编译程序信息。
8.3throws
用法
如果设计流程中发生异常,而设计时并没有充足的信息知道该如何处理,那么可以抛出异常,让调用方法的客户端来解决。为了告诉编译程序这个事实,必须使用 throws
声明此方法会抛出的异常类型或父类型,才会通过编译。在流程中抛出异常,就直接跳离原有流程,可以抛出受检或非受检异常。若果抛出受检异常则表示认为客户端有能力且应处理异常,此时必须要throws
声明;若果抛出非受检异常则表示认为客户端调用方法时机出错,抛出异常要求是要求客户端修正这个漏洞再来调用方法 ,此事也就不用throws
声明。
throws
是用来声明一个方法可能抛出的所有异常信息;throw
则是指抛出的一个具体的异常类型。- 通常在一个方法(类)的声明处通过
throws
声明方法(类)可能抛出的异常信息,而在方法(类)内部通过throw
声明一个具体的异常信息。 throws
通常不用显示的捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法;throw
则需要用户自己捕获相关的异常,而后在对其进行相关包装,最后在将包装后的异常信息抛出。
8.4堆栈追踪
再多重方法调用下,异常发生点可能是在某个方法之中,若想要得知异常发生的根源,以及多重方法调用下异常的堆栈传播,可以利用异常对象自动收集的堆栈追踪来获得相关信息,StackTraceDemo.java
:
package cc.openhome;
public class StackTraceDemo {
public static void main(String[] args) {
try{
c();
}catch (NullPointerException ex){
ex.printStackTrace();
}
}
static void c(){
b();
}
static void b(){
a();
}
static String a(){
String text = null;
return text.toUpperCase();
}
}
用printStackTrace()
在控制台显示显示堆栈追踪:
如果要取得个别的堆栈元素进行处理,则可以使用getStackTrace()
,这会返回StackTraceElenment
数组。
要善用堆栈追踪的前提是代码中不能有私吞异常行为,例如在捕捉异常后什么都不做:
try{
···
}catch(FileNotFoundException ex){}
如果要让堆栈起点成为重抛异常的地方,可以使用fillInStackTrace()
方法,这个方法会重新装填堆栈,将起点设为重抛异常的地方,返回Throwable
对象。StackTraceDemo3.java
:
package cc.openhome;
public class StackTraceDemo3 {
public static void main(String[] args) {
try{
c();
}catch (NullPointerException ex){
ex.printStackTrace();
}
}
static void c(){
try{
b();
}catch(NullPointerException ex){
ex.printStackTrace();
Throwable t=ex.fillInStackTrace();
throw (NullPointerException)t;
}
}
static void b(){
a();
}
static String a(){
String text = null;
return text.toUpperCase();
}
}
显示结果为:
8.5关于assert
断言结果一定是成立或者不成立,预期结果与实验结果相同,断言成立否则不成立。使用断言:
- 断言客户端调用程序方法前,已经准备好某些前置条件。
- 断言客户端调用程序方法后,具有方法承诺的结果。
- 断言对象某个时间点下的状态。
- 使用断言取代批注。
- 断言程序流程中绝对不会执行到的程序代码部分。
8.6使用finally
try
、catch
语法还可以搭配finally,无论程序是因为异常而中止或其它方式返回终止的,都会执行finally
区块。FinallyDemo.java
:
package cc.openhome;
public class Finallydemo {
public static void main(String[] args) {
System.out.println(test(true));
}
public static int test(boolean flag){
try{
if(flag){
return 1;
}
}finally{
System.out.println("finally···");
}
return 0;
}
}
结果为:
可见函数会将finally
块会先执行完,再将值返回。
8.7尝试关闭资源语法
尝试关闭资源语法可套用的对象,必须操作java.lang.AutoCloseable
接口。AutoCloseDemo.java
:
package cc.openhome;
public class AutoClosableDemo {
public static void main(String[] args) {
try(Resource res=new Resource()){
res.doSome();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
class Resource implements AutoCloseable{
void doSome(){
System.out.println("做一些事");
}
@Override
public void close()throws Exception{
System.out.println("资源被关闭");
}
}
结果为:
尝试关闭资源语法也可以同时关闭两个以上的对象资源,中间要用分号隔开。AutoCloseDemo2.java
:
package autoclosabledemo2;
public class AutoClosableDemo2 {
public static void main(String[] args) {
try(ResourceSome some=new ResourceSome();
ResourceOther other =new ResourceOther()){
some.doSome();
other.doOther();
}catch(Exception ex){
ex.printStackTrace();
}
}
}
class ResourceSome implements AutoCloseable{
void doSome(){
System.out.println("做一些事");
}
@Override
public void close()throws Exception{
System.out.println("资源Some被关闭");
}
}
class ResourceOther implements AutoCloseable{
void doOther(){
System.out.println("做其他事");
}
@Override
public void close()throws Exception{
System.out.println("资源Other被关闭");
}
}
运行结果为:
在try括号中,越后面撰写的对象资源会越早被关闭。
教材学习中的问题和解决过程
教材中很多地方讲解不详细,我参考了网上大量的资料,才初有体会,如网名为Glory的一片博客(http://www.cnblogs.com/ggzss/archive/2011/08/18/2145017.html):
代码调试中的问题和解决过程
代码遇到了不小的问题,有点看不大懂了呢,于是便在小组中发了帖子:
上传代码到git:
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第三周 | 300/600 | 2/6 | 20/50 | |
第四周 | 300/900 | 2/8 | 16/66 | |
第五周 | 100/1200 | 1/10 | 8/82 |