异常处理

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  阅读(220)  评论(2编辑  收藏  举报