异常处理

20145217 《Java程序设计》第5周学习总结(1)

教材学习内容总结

程序中总有一些意想不到的状况所引发的错误,我们都知道Java是面向对象的编程,Java中的错误也已对象的方式呈现为java.lang.Throwable的各种子类。第八章内容主要讲述的是Java中对异常情况的处理方法。

8.1trycatch语法

Java中所有错误都会包装为对象,如果你愿意,可以尝试try执行程序并捕捉catch代表错误的对象后做一些处理。使用了trycatch语法,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类及其子类异常,如NullPointerExceptionIndexOutOfBoundsException等, 这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的, 程序应该从逻辑角度尽可能避免这类异常的发生。 非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。 如IOExceptionSQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
  • errorexceptionThrowable的唯二子类。error表示恢复不是不可能但很困难的情况下的一种严重问题,比如说内存溢出,不可能指望程序能处理这样的情况。 exception表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
  • 使用trycatch捕捉对象时也要注意,如果父类异常对象在子类异常对象前被捕捉,则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

trycatch语法还可以搭配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

参考资料

posted @ 2016-04-03 14:47  joke-bright  阅读(256)  评论(2)    收藏  举报