java的异常处理机制可以使程序有极好的容错性,让程序更加的健壮.所谓的异常,就是指的阻止当前方法或作用域继续执行的问题,,当程序运行时出现异常时,系统就会自动生成一个Exception对象来通知程序.这样就极大的简化了我们的工作. 
当然java的异常对象有很多种,下面这幅图显示了java异常类的继承体系. 

从图片中可以看到java将所有的非正常情况分成了两种: 异常(Exception)和错误(Error),他们都继承Throwable父类.Error错误一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误等.这种错误无法恢复或不可捕获,将导致应用程序中断,通常应用程序无法处理这些错误,因此也不应该试图用catch来进行捕获.而对于Exception我们是可以进行捕获并处理的.下面从几个方面对异常处理机制进行介绍.

一.异常处理的一般格式:

         try{
///可能会抛出异常的代码
}
catch(Type1 id1){
//处理Type1类型异常的代码
}
catch(Type2 id2){
///处理type2类型异常的代码
}

try块中放置可能会发生异常的代码(但是我们不知道具体是哪种异常).如果异常发生了,try块抛出系统自动生成的异常对象,然后异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序,然后进行catch语句执行(不会在向下查找).看起来和switch语句比较类似.除了上面列出的那些异常类,其实我们也可以自己定义异常,然后处理他们.定义异常最重要的是异常类的名字,最好做到望名知义.另外异常处理中经常出现的throws和throw这两个关键字;throws表示的是当前程序不处理这个异常将其抛给调用这个程序的上一级程序执行,如果是main()方法则传给JVM虚拟机处理.throw表示的是显式的抛出一个异常对象.下面的代码中用到了上面提到的知识点.

 ///自己定义一个简单的异常类,只需要声明以下继承关系
///也可以自己编写构造器,但这不是必要的
public class SimpleException extends Exception{
///public SimpleException();
///public SimpleException(String str){
///super(str)};
} package lkl; import java.util.Scanner; public class ExceptionTest { ///声明该程序不处理SimpleException异常而是将其丢出
public void f() throws SimpleException{
System.out.println("Throws SimpleException from f()");
///丢出一个SimpleException异常对象
throw new SimpleException();
}
///多个try语句会依次执行,即使上一个try语句发生异常,下一个try语句任然会执行
public static void main(String[] args){
ExceptionTest et =new ExceptionTest();
try{
et.f();
}///处理try中丢出的SimpleException异常
catch (SimpleException sn){
System.out.println("Catch it");
//调用异常类的printStackTrace()方法,默认是打印到标准错误流的
///现在我们显示指定输出到标准输出流
///会打印从方法调用处直到异常抛出处的方法调用序列,栈顶是最后一次调用的而栈底是第一次掉用的
sn.printStackTrace(System.out);
}
try{
int[] a= new int[10];
Scanner sc = new Scanner(System.in);
System.out.println("请输入k: ");
int k=sc.nextInt();
sc.close();
System.out.println(a[k]);
}
///处理数组下标越界的异常
catch(ArrayIndexOutOfBoundsException ae){
System.out.println("Catch ArrayIndexOutOfBoundsException");
ae.printStackTrace(System.out);
}
}
}

二.打印异常信息 
异常类的基类Exception中提供了一组方法用来获取异常的一些信息.所以如果我们获得了一个异常对象,那么我们就可以打印出一些有用的信息,最常用的就是void printStackTrace()这个方法,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每个元素都表示栈中的一帧.元素0是栈顶元素,并且是调用序列中的最后一个方法调用(这个异常被创建和抛出之处);他有几个不同的重载版本,可以将信息输出到不同的流中去.下面的代码显示了如何打印基本的异常信息:

 package lkl;

 ///测试异常类Exception的常用方法
public class ExceptionMethods { public static void main(String[] args){
try{
throw new Exception("My Exception");
}///通过捕获异常类的基类Exception就可以捕获所有类型的异常
catch(Exception e){
System.out.println("Caught Exception");
System.out.println("getMessage(): "+e.getMessage());
System.out.println("getLocalizedMessage(): "+e.getLocalizedMessage());
System.out.println("toString(): "+e.toString());
System.out.println("printStackTrace(): ");
e.printStackTrace(System.out);
}
}
}

三.重新抛出异常与异常链 
有时候需要将捕获的异常重新抛出,这样在语法上是很简单的.但问题是如果直接抛出的话,这个异常对象中信息将仍然是原来异常对象中的信息.如果我们想要跟新一下这个信息,我们可以调用fillInStackTrace()方法,这个方法返回一个Throwable对象,它是通过将当前调用栈信息填入原来那个对象而得到的.调用fillStackTrace()的那一行就成了异常的新的发生地.如果我们在捕获了异常之后抛出了另外一种异常,那么原来异常发生点的信息会丢失,得到与fillInStackTrace()一样的方法. 
所谓的异常链指的是在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息给保留下来.java中的Throwable子类在构造器中都可以接受一个cause(因由)对象作为参数.这个参数其实就是原先的异常对象,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也可以通过异常链追踪到异常最初发生的位置.但是在Throwable的子类中,只对Error,Exception,RuntimeException这三个子类提供了带cause因子的构造器,如果要把其它类型的异常链接起来,就应该使用initCause()方法而不是构造器了.

四.使用finally进行清理 
引入finally语句的原因是我们希望一些代码总是能得到执行,无论try块中是否抛出异常.这样异常处理的基本格式变成了下面这样:

 try{
///可能会抛出异常的代码
}
catch(Type1 id1){
//处理Type1类型异常的代码
}
catch(Type2 id2){
///处理type2类型异常的代码
}
finally{
///总是会执行的代码
}

其实catch和finally语句也可以只有一个出现.下面的代码显示了finally语句总能运行

 package lkl;

 public class FinallyTest extends Exception{

     public static int count=0;
public static void main(String[] args){
while(true){
try{
//无论是不是抛出异常,finally语句都会执行
if(count++==0){
throw new MyException();
}
System.out.println("No exception");
}
catch(MyException me){
System.out.println("MyException");
}
finally{
System.out.println("In finally clause");
if(count==2) break;
}
}
}
}

下面这份代码说明就算在try中有return语句,finally语句仍会得到执行,看起来就像有多个返回点.

 package lkl;

 public class MultipleReturns {

     public static void f(int i){
System.out.println("Initialization that requires cleanup");
try{///在函数reutrn之前,finally回先执行
System.out.println("Point 1");
if(i==1) return ;
System.out.println("Point 2");
if(i==2) return ;
System.out.println("Point 3");
if(i==3) return ;
System.out.println("Point 4");
if(i==4) return;
}
///在上面的return之前,会先执行finally语句
finally{
System.out.println("Perferming cleanup");
}
}
public static void main(String[] args){
for(int i=1;i<=4;i++)
f(i);
}
}

那么总是会执行的finally语句用来干嘛?在java中我们主要用来清理除内存外的其它一些资源,这些资源包括:已经打开的文件或网络连接,在屏幕上画的图像等.

五.异常处理与继承 
最后一个问题是关于异常处理和继承的.当异常处理和继承一起发生的时候,我们需要关注的问题就是父类构造器抛出异常和子类构造器抛出异常的关系,在子类中重写父类方法是怎样声明异常等问题.总结一下其实也就是二个规则: 
1).子类重写基类方法抛出的异常必须不比原基类方法抛出的异常类型更大. 
2).虽然在语法上对子类构造器的异常声明没有规定,但是子类构造器的异常说明必须包括基类构造器的异常说明.(因为基类的构造器总是会被调用). 
下面的代码对此进行了演示:

 public class Foul extends BaseballException{
} public class PopFoul extends Foul{
} public class BaseballException extends Exception{
} public class Strike extends BaseballException{ } public class RainedOut extends StormException{ } public interface Storm { public void event() throws RainedOut;
public void rainHard() throws RainedOut;
} public abstract class Inning { public Inning() throws BaseballException{}
///定义抛出异常,但是实际上并没有
public void event() throws BaseballException{};
//声明抛出两个异常
public abstract void atBat() throws Strike ,Foul; public void walk() {}
} public class StormyInning extends Inning implements Storm{ public StormyInning() throws BaseballException,RainedOut{}
public StormyInning(String s)
throws Foul,BaseballException{}
//因为基类方法中没有抛出异常而子类覆盖后的方法抛出了
///所以编译报错
//void walk() throws PopFoul{} //接口不能给基类方法添加异常类型
//public void event() throws RainedOut{} ///重写的方法抛出和基类方法一样的异常是可以的
public void rainHard() throws RainedOut{} //基类的方法抛出了异常,而重写的方法不抛出也是可以的
public void event() {} //重写的方法抛出基类异常的子类也是可以的
public void atBat() throws PopFoul{} public static void main(String[] args) {
try{///下面的两句代码中构造器中可能抛出RainedOut,BaseballException异常
///atBat()方法可能抛出PopFoul异常.所以需要处理三种异常
StormyInning si = new StormyInning();
si.atBat();
}
catch(PopFoul e){
System.out.println("Pop Foul");
}
catch(RainedOut e){
System.out.println("Rained Out");
}
catch(BaseballException e){
System.out.println("Generic baseball");
}
try{//这段代码进行了向上转型,所以处理上面的三种情况外
///还必须处理基类atBat()方法抛出的Strike异常
Inning i = new StormyInning();
i.atBat();
}
catch(Strike e){
System.out.println("Strike");
}
catch(PopFoul e){
System.out.println("Pop Foul");
}
catch(RainedOut e){
System.out.println("Rained Out");
}
catch(BaseballException e){
System.out.println("Generic baseball");
}
}
}

转载:http://blog.csdn.net/acm_lkl/article/details/46422645

Java编程思想学习(九) 异常处理的更多相关文章

  1. [Java编程思想-学习笔记]第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  2. Java编程思想学习(八) 内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿 ...

  3. java编程思想--学习心得

    学习Java编程思想,需要了解语言特性,对于各种名词,能够借助项目代码,解释其含义,不借助搜索工具,明白其在什么样场景下使用,会带来什么样的问题,能否避免这类问题. 学习的过程,与软件开发相同,一样是 ...

  4. Java编程思想学习(一)----对象导论中多态的理解

    1.1抽象过程 1)万物皆对象. 2)程序是对象的集合,他们通过发送消息来告知彼此所要求做的. 3)每个对象都有自己的由其他对象所构成的存储. 4)每个对象都拥有其类型. 5)某一特定类型的所有对象都 ...

  5. Java编程思想 学习笔记1

    一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...

  6. [Java编程思想-学习笔记]第1章 对象导论

    1.1  抽象过程 Java是一门面向对象的语言,它的一个优点在于只针对待解问题抽象,而不用为具体的计算机结构而烦心,这使得Java有完美的移植性,也即Java的口号"Write Once, ...

  7. Java编程思想学习(五)----第5章:初始化与清理

    随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一. C++引入了构造嚣(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构造器,并 ...

  8. Java编程思想学习(十) 正则表达式

    正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...

  9. Java编程思想 学习笔记12

    十二.通过异常处理错误  Java的基本理念是“结构不佳的代码不能运行”. Java中的异常处理的目的在于通过使用少于目前数量的代码来简化大型.可靠的程序的生成,并且通过这种方式可以使你更加自信:你的 ...

随机推荐

  1. Java Executor并发框架(二)剖析ThreadPoolExecutor运行过程

    上一篇从整体上介绍了Executor接口,从上一篇我们知道了Executor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newScheduledThrea ...

  2. View (二) 自定义属性 自定义属性的格式详解

    自定义属性格式一共有十种: 1. reference:参考某一资源ID. 2. color:颜色值. 3. boolean:布尔值. 4. dimension:尺寸值. 5. float:浮点值. 6 ...

  3. Linux ncurses编写 FlapyBird 第一步

    /* * flapybird.h * * Created on: 2016年9月15日 * Author: jon */ #include <curses.h> #include < ...

  4. js实现复制功能

    JS 点击复制Copy 1.实现点击按钮,复制文本框中的的内容 1 <script type="text/javascript"> 2 function copyUrl ...

  5. 027医疗项目-模块二:药品目录的导入导出-导入功能的Action的编写

    前一篇文章我们写了Service层,这篇文章我们写一下Action层. 实现的功能: 1:我们先下载模板:然后按照模板里面的规则,插入数据.比如存在d盘. 2:然后浏览找到那个文件,上传上去. 然后把 ...

  6. 07Spring_bean属性的依赖注入-重点@Autowriter

    在spring2.5 版本,没有提供基本类型属性注入 ,但是spring3.0引入注解@Value 所以在Spring3.0中属性的注入只可以这么写.

  7. Lambda表达式关于like问题(未解决)

    参考文章: http://stackoverflow.com/questions/3616215/like-in-lambda-expression-and-linq 1. c=>c.name. ...

  8. 【转】【C#】C# 垃圾回收机制

    摘要:今天我们漫谈C#中的垃圾回收机制,本文将从垃圾回收机制的原理讲起,希望对大家有所帮助. GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由 ...

  9. [QoS]cisco3560限速配置案例-收集于网工泡泡

    网络中常用到这些:CISCO和H3C-MAC过滤+端口限速+端口镜像+端口隔离 不同的方式不同的思想:嘎嘎 其他各个厂商的限速链接:http://pan.baidu.com/s/1hrIMoSG 密码 ...

  10. JavaScript及其异步实现

    由于javascript本身是单线程模型,这里主要通过Callbacks,Listeners,Control Flow Libraries ,Promises四种方式来实现异步操作. Referenc ...