Thinking in Java---异常处理机制

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);
        }
    }
}

三.又一次抛出异常与异常链

有时候须要将捕获的异常又一次抛出,这样在语法上是非常easy的.但问题是假设直接抛出的话,这个异常对象中信息将仍然是原来异常对象中的信息.假设我们想要跟新一下这个信息,我们能够调用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");
       }
    }
}
时间: 2024-10-09 23:33:50

Thinking in Java---异常处理机制的相关文章

Java异常处理机制的秘密

一.结论 这些结论你可能从未听说过,但其正确性是毋庸置疑的,不妨先看看: 1.catch中throw不一定能抛回到上一层,因为finally中的return会抑制这个throw2.finally中throw一定能抛回上一层,因为此时其后的return不会被执行到(throw中断了正常的顺序流)3.在try/catch中return并不会直接返回上一层,而是先执行finally再返回 二.一段小程序 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

Java异常处理机制 —— 深入理解与开发应用

本文为原创博文,严禁转载,侵权必究! Java异常处理机制在日常开发中应用频繁,其最主要的不外乎几个关键字:try.catch.finally.throw.throws,以及各种各样的Exception.本篇文章主要在基础的使用方法上,进一步分析在开发中如何使用异常机制更合理. try-catch-finally try-catch-finally块的用法比较简单,使用频次也最高.try块中包含可能出现异常的语句(当然这是人为决定的,try理论上可以包含任何代码),catch块负责捕获可能出现的

Java 异常处理机制和集合框架

课程  Java面向对象程序设计   实验名称  异常处理机制.集合框架 班级    13级计三          学号  10503                姓名 一.实验目的 掌握面向对象程序设计技术 二.实验环境 1.微型计算机一台 2.WINDOWS操作系统,Java SDK,Eclipse开发环境 三.实验内容 1.Java异常处理机制涉及5个关键字:try.catch.finally.throw.throws,请理解每个关键字的作用,并在编写程序,使用运用这5个关键字,观察效果

Java异常处理机制就是程序代码执行过程

我也是通过各种方式在进行验证是否可以满足我们的需求,今天我就发现了一个问题.现在我们来一起说明一下,这个可能不算是bug,而应该需要我们记住就可以了. 对于一副灰度图像I,她的每一个像素点I(x,y)都有一个灰度值,一般情况下可能的灰度取值有2^8=256个(0,1,...,255).如果我们统计出灰度值r在I中出现的次数n,并对其进行归一化(n/N,N是所有灰度值出现次数的总和),这样我们就可以得到像素r在I中出现的概率p(r).如果对每一个可能的灰度取值r都做同样的处理,我们可以得到如图1左

Java异常处理机制很有意思

前言:在网络上看到一篇<深入理解Java异常处理机制>,看完感觉自己也有一点小想法,的确在很多Java学者的眼里,异常处理机制不就是try catch finally吗,有啥好理解,很多时候,我也经常忽略这方面的内容,感觉没什么有意思的,那么我们来扎个小马步吧 1.经过对原作者的例子改造 package mwq; public class T1 { public static void main(String[] args) { boolean b = true; try { b = tb1(

【转】深入理解java异常处理机制

深入理解java异常处理机制 1. 引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中的那么简单.听话.不信?那你看看下面的代码,"猜猜"它执行后的结果会是什么?不要往后看答案.也不许执行代码看真正答案哦.如果你的答案是正确,那么这篇文章你就不用浪费时间看啦. <span style="background-color: rg

如何正确使用Java异常处理机制

第一节 异常处理概述 第二节 Java异常处理类 2.1 Throwable 2.1.1 Throwable有五种构造方法 2.1.2 Throwable的所有成员方法 2.2 Error 2.3 Exception 2.4 RuntimeException 2.5 Checked Exception 2.6 Uncheck Exception 2.7 总结 第三节 Java异常处理执行流程探究 3.1 流程一 3.2 流程二 3.3 流程三 3.4 流程四 3.5 流程五 3.6 流程六 3.

java异常处理机制详解

java异常处理机制详解 程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我们还可以提供一定的应对预案.C语言中的异常处理是简单的通过函数返回值来实现的,但返回值代表的含义往往是由惯例决定的.程序员需要查询大量的资料,才可能找到一个模糊的原因.面向对象语言,比如C++, Java, Python往往有更加复杂的异常处理机制.这里讨论Java中的异常处理机制. 异常处理 Ja

Java异常处理机制:try...catch...的执行流程

Java异常处理机制:try...catch...的执行流程 在项目中遇到try...catch...语句,因为对Java异常处理机制的流程不是很清楚,导致对相关逻辑代码不理解.所以现在来总结Java异常处理机制的处理流程: 1.异常处理的机制如下: 在方法中用 try... catch... 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个不同类型的异常. 对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 声明异常,通过throw语句拋出异常,即由上层的调用方法

深入理解Java异常处理机制

1. 引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中的那么简单.听话.不信?那你看看下面的代码,"猜猜"它执行后的结果会是什么?不要往后看答案.也不许执行代码看真正答案哦.如果你的答案是正确,那么这篇文章你就不用浪费时间看啦. <span style="background-color: rgb(255, 255, 255