Java例外(Exception)処理機構のサンプルコード共有

黄舟
リリース: 2017-03-16 10:32:46
オリジナル
1687 人が閲覧しました

この記事では、主に Java 例外 (Exception) 処理メカニズムの詳細な説明に関する関連情報を紹介します。必要な方は、

1 を参照してください。 例外

の定義は、『Javaプログラミング思考』で次のように定義されています: 例外: 現在のメソッドまたはスコープの実行継続を妨げる問題。 Java には例外処理メカニズムがありますが、例外は決して「通常の」態度で見るべきではないことは明らかです。厳密に言えば、例外はある意味でのエラー、問題であり、プログラムが失敗する可能性があります。 Java が例外処理メカニズムを提案する理由は、プログラムに何か異常があることを開発者に伝えるためです。

私が初めて Java を学んだとき、この例外が何を意味するのか、なぜこのメカニズムがあるのか​​、いつも混乱していたのを覚えています。しかし、知識が蓄積されるにつれて、私は徐々にその異常性について少し感じるようになりました。例外の使用法を説明する例を示します。


public class Calculator
 {
  public int devide(int num1,
int num2)
 {
    //判断除数是否为0
    if(num2
 == 0)
 {
      throw new IllegalArgumentException("除数不能为零");
    }
     
    return num1/num2;
  }
}
ログイン後にコピー

このクラスの除算演算メソッドを見てください。初心者の場合は、パラメーターが正しいかどうかを考慮せずに、計算結果を直接返すことができます (もちろん、それは許されます。誰もがこれを行います)。 ) の)。ただし、クレードル内でプログラムが失敗する可能性のある「兆候」をできるだけ徹底的に摘み取る必要があるため、パラメータの正当性をチェックする必要があります。このうち、パラメータチェックによりスローされるパラメータ不正例外は、このメソッドの異常事態です。通常の状況では、電卓は正しく使用されますが、誤って除数を 0 に割り当ててしまう可能性は排除されません。これまでこの状況を考慮したことがなく、ユーザーの数学的基礎が不十分だった場合は、大失敗です。しかし、この状況を以前に検討したことがあれば、このエラーは制御できる範囲内にあることは明らかです。

2. 異常なリテラシーの行動

今日、誰かとチャットしているときにジョークを目にしました。 世界で最も本当の依存とは、あなたが努力していて、私がそれに応えているときです。どんなに怒られても、黙って耐えて静かに対処します。 ほとんどの初心者は、Java の例外は try...catch.... であると感じています。はい、これが最もよく使用され、最も実用的です。私の感覚では、Java の例外は「try...catch...」から発生します。

まず、Java の例外システムについて理解しましょう:

Throwable クラスは、Java 言語のすべてのエラーまたは例外のスーパー クラスです (これがスローされるすべてのものです)。これには、Error と Exception の 2 つのサブクラスがあります。

エラー: 合理的なアプリケーションが検出すべきでない重大な問題を示すために使用されます。この状況は大きな問題であり、あなたが対処するには大きすぎるので、心配しないで放っておいてください。たとえば、VirtualMachineError: このエラーは、Java 仮想マシンがクラッシュするか、動作を継続するために必要なリソースが不足するとスローされます。この例外が存在するとしても、いつ、どのように処理すればよいでしょうか? ? JVM に任せてください。JVM ほどプロフェッショナルなものはありません。

例外: 適切なアプリケーションがキャッチしたい条件を示します。例外は 2 つのカテゴリに分類されます。1 つは CheckedException、もう 1 つは UncheckedException です。これら 2 つの例外の主な違いは、CheckedException は try...catch... を使用して明示的にキャプチャする必要があるのに対し、UncheckedException はキャプチャする必要がないことです。通常、UncheckedException は RuntimeException とも呼ばれます。 「Effective Java」では、回復可能な状態にはチェック例外 (CheckedException) を使用し、プログラム エラーには実行時例外 (RuntimeException) を使用します (回復不可能で大きな間違いがあったことを意味します) と指摘しています。

一般的な RuntimeException には、IllegalArgumentException、IllegalStateException、NullPointerException、IndexOutOfBoundsException などが含まれます。 CheckedException は枚挙にいとまがありません。プログラムの作成プロセス中に、try...catch... によってキャッチされる例外はすべて CheckedException です。 IOException と io パッケージ内のそのサブクラス、これらは CheckedException です。

3つ。 例外の使用

例外の使用のこの部分は主にデモンストレーション コードであり、コードを書く過程で遭遇するものはすべて (もちろんごく一部です)、他の人にインスピレーションを与えたいと思いませんか!

例 1. この例では、主に 2 つのメソッドを比較することにより、例外が発生した後のコードの実行フローを示します。


りー


public static void testException2()
 {
    int[]
 ints = new int[]
 { 1,
2,
3,
4 };
    System.out.println("异常出现前");
    System.out.println(ints[4]);
    System.out.println("我还有幸执行到吗");//
 发生异常以后,他后面的代码不能被执行
  }
ログイン後にコピー

  首先指出例子中的不足之处,IndexOutofBoundsException是一个非受检异常,所以不用try...catch...显示捕捉,但是我的目的是对同一个异常用不同的处理方式,看它会有什么不同的而结果(这里也就只能用它将就一下了)。异常出现时第一个方法只是跳出了try块,但是它后面的代码会照样执行的。但是第二种就不一样了直接跳出了方法,比较强硬。从第一个方法中我们看到,try...catch...是一种"事务性"的保障,它的目的是保证程序在异常的情况下运行完毕,同时它还会告知程序员程序中出错的详细信息(这种详细信息有时要依赖于程序员设计)。

例2. 重新抛出异常


public class Rethrow
 {
  public static void readFile(String
 file) throws FileNotFoundException
 {
    try {
      BufferedInputStream
 in = new BufferedInputStream(new FileInputStream(file));
    }
catch (FileNotFoundException
 e) {
      e.printStackTrace();
      System.err.println("不知道如何处理该异常或者根本不想处理它,但是不做处理又不合适,这是重新抛出异常交给上一级处理");
      //重新抛出异常
      throw e;
    }
  }
   
  public static void printFile(String
 file) {
    try {
      readFile(file);
    }
catch (FileNotFoundException
 e) {
      e.printStackTrace();
    }
  }
   
  public static void main(String[]
 args) {
    printFile("D:/file");
  }
}
ログイン後にコピー

  异常的本意是好的,让我们试图修复程序,但是现实中我们修复的几率很小,我们很多时候就是用它来记录出错的信息。如果你厌倦了不停的处理异常,重新抛出异常对你来说可能是一个很好的解脱。原封不动的把这个异常抛给上一级,抛给调用这个方法的人,让他来费脑筋吧。这样看来,java异常(当然指的是受检异常)又给我们平添很多麻烦,尽管它的出发点是好的。

例3. 异常链的使用及异常丢失

定义三个异常类:ExceptionA,ExceptionB,ExceptionC


public class ExceptionA
extends Exception
 {
  public ExceptionA(String
 str) {
    super();
  }
}
 
public class ExceptionB
extends ExceptionA
 {
 
  public ExceptionB(String
 str) {
    super(str);
  }
}
 
public class ExceptionC
extends ExceptionA
 {
  public ExceptionC(String
 str) {
    super(str);
  }
}
ログイン後にコピー

异常丢失的情况:


public class NeverCaught
 {
  static void f()
throws ExceptionB{
    throw new ExceptionB("exception
 b");
  }
 
  static void g()
throws ExceptionC
 {
    try {
      f();
    }
catch (ExceptionB
 e) {
      ExceptionC
 c = new ExceptionC("exception
 a");
      throw c;
    }
  }
 
  public static void main(String[]
 args) {
      try {
        g();
      }
catch (ExceptionC
 e) {
        e.printStackTrace();
      }
  }
 
}
/*
exception.ExceptionC
at
 exception.NeverCaught.g(NeverCaught.java:12)
at
 exception.NeverCaught.main(NeverCaught.java:19)
*/
ログイン後にコピー

为什么只是打印出来了ExceptionC而没有打印出ExceptionB呢?这个还是自己分析一下吧!

上面的情况相当于少了一种异常,这在我们排错的过程中非常的不利。那我们遇到上面的情况应该怎么办呢?这就是异常链的用武之地:保存异常信息,在抛出另外一个异常的同时不丢失原来的异常。


public class NeverCaught
 {
  static void f()
throws ExceptionB{
    throw new ExceptionB("exception
 b");
  }
 
  static void g()
throws ExceptionC
 {
    try {
      f();
    }
catch (ExceptionB
 e) {
      ExceptionC
 c = new ExceptionC("exception
 a");
      //异常连
      c.initCause(e);
      throw c;
    }
  }
 
  public static void main(String[]
 args) {
      try {
        g();
      }
catch (ExceptionC
 e) {
        e.printStackTrace();
      }
  }
 
}
/*
exception.ExceptionC
at
 exception.NeverCaught.g(NeverCaught.java:12)
at
 exception.NeverCaught.main(NeverCaught.java:21)
Caused
 by: exception.ExceptionB
at
 exception.NeverCaught.f(NeverCaught.java:5)
at
 exception.NeverCaught.g(NeverCaught.java:10)
...
 1 more
*/
ログイン後にコピー

这个异常链的特性是所有异常均具备的,因为这个initCause()方法是从Throwable继承的。

例4. 清理工作

清理工作对于我们来说是必不可少的,因为如果一些消耗资源的操作,比如IO,JDBC。如果我们用完以后没有及时正确的关闭,那后果会很严重,这意味着内存泄露。异常的出现要求我们必须设计一种机制不论什么情况下,资源都能及时正确的清理。这就是finally。


public void readFile(String
 file) {
    BufferedReader
 reader = null;
    try {
      reader
 = new BufferedReader(new InputStreamReader(
          new FileInputStream(file)));
      //
 do some other work
    }
catch (FileNotFoundException
 e) {
      e.printStackTrace();
    }
finally {
      try {
        reader.close();
      }
catch (IOException
 e) {
        e.printStackTrace();
      }
    }
  }
ログイン後にコピー

例子非常的简单,是一个读取文件的例子。这样的例子在JDBC操作中也非常的常见。(所以,我觉得对于资源的及时正确清理是一个程序员的基本素质之一。)

Try...finally结构也是保证资源正确关闭的一个手段。如果你不清楚代码执行过程中会发生什么异常情况会导致资源不能得到清理,那么你就用try对这段"可疑"代码进行包装,然后在finally中进行资源的清理。举一个例子:


public void readFile()
 {
    BufferedReader
 reader = null;
    try {
      reader
 = new BufferedReader(new InputStreamReader(
          new FileInputStream("file")));
      //
 do some other work
     
      //close
 reader
      reader.close();
    }
catch (FileNotFoundException
 e) {
      e.printStackTrace();
    }
catch (IOException
 e) {
      e.printStackTrace();
    }
  }
ログイン後にコピー

我们注意一下这个方法和上一个方法的区别,下一个人可能习惯更好一点,及早的关闭reader。但是往往事与愿违,因为在reader.close()以前异常随时可能发生,这样的代码结构不能预防任何异常的出现。因为程序会在异常出现的地方跳出,后面的代码不能执行(这在上面应经用实例证明过)。这时我们就可以用try...finally来改造:


public void readFile()
 {
    BufferedReader
 reader = null;
    try {
      try {
        reader
 = new BufferedReader(new InputStreamReader(
            new FileInputStream("file")));
        //
 do some other work
 
        //
 close reader
      }
finally {
        reader.close();
      }
    }
catch (FileNotFoundException
 e) {
      e.printStackTrace();
    }
catch (IOException
 e) {
      e.printStackTrace();
    }
  }
ログイン後にコピー

及早的关闭资源是一种良好的行为,因为时间越长你忘记关闭的可能性越大。这样在配合上try...finally就保证万无一失了(不要嫌麻烦,java就是这么中规中矩)。

再说一种情况,假如我想在构造方法中打开一个文件或者创建一个JDBC连接,因为我们要在其他的方法中使用这个资源,所以不能在构造方法中及早的将这个资源关闭。那我们是不是就没辙了呢?答案是否定的。看一下下面的例子:


public class ResourceInConstructor
 {
  BufferedReader
 reader = null;
  public ResourceInConstructor()
 {
    try {
      reader
 = new BufferedReader(new InputStreamReader(new FileInputStream("")));
    }
catch (FileNotFoundException
 e) {
      e.printStackTrace();
    }
  }
   
  public void readFile()
 {
    try {
      while(reader.readLine()!=null)
 {
        //do
 some work
      }
    }
catch (IOException
 e) {
      e.printStackTrace();
    }
  }
   
  public void dispose()
 {
    try {
      reader.close();
    }
catch (IOException
 e) {
      e.printStackTrace();
    }
  }
}
ログイン後にコピー

这一部分讲的多了一点,但是异常确实是看起来容易用起来难的东西呀,java中还是有好多的东西需要深挖的。

四. 异常的误用

对于异常的误用着实很常见,上一部分中已经列举了几个,大家仔细的看一下。下面再说两个其他的。

例1. 用一个Exception来捕捉所有的异常,颇有"一夫当关万夫莫开"的气魄。不过这也是最傻的行为。


 public void readFile(String
 file) {
    BufferedReader
 reader = null;
    Connection
 conn = null;
    try {
      reader
 = new BufferedReader(new InputStreamReader(
          new FileInputStream(file)));
      //
 do some other work
       
      conn
 = DriverManager.getConnection("");
      //...
    }
catch (Exception
 e) {
      e.printStackTrace();
    }
finally {
      try {
        reader.close();
        conn.close();
      }
catch (Exception
 e) {
        e.printStackTrace();
      }
    }
  }
ログイン後にコピー

  从异常角度来说这样严格的程序确实是万无一失,所有的异常都能捕获。但是站在编程人员的角度,万一这个程序出错了我们该如何分辨是到底是那引起的呢,IO还是JDBC...所以,这种写法很值得当做一个反例。大家不要以为这种做法很幼稚,傻子才会做。我在公司实习时确实看见了类似的情况:只不过是人家没有用Exception而是用了Throwable。

例2. 这里就不举例子了,上面的程序都是反例。异常是程序处理意外情况的机制,当程序发生意外时,我们需要尽可能多的得到意外的信息,包括发生的位置,描述,原因等等。这些都是我们解决问题的线索。但是上面的例子都只是简单的printStackTrace()。如果我们自己写代码,就要尽可能多的对这个异常进行描述。比如说为什么会出现这个异常,什么情况下会发生这个异常。如果传入方法的参数不正确,告知什么样的参数是合法的参数,或者给出一个sample。

例3. 将try block写的简短,不要所有的东西都扔在这里,我们尽可能的分析出到底哪几行程序可能出现异常,只是对可能出现异常的代码进行try。尽量为每一个异常写一个try...catch,避免异常丢失。在IO操作中,一个IOException也具有"一夫当关万夫莫开"的气魄。

五.总结

总结非常简单,不要为了使用异常而使用异常。异常是程序设计的一部分,对它的设计也要考究点。

以上がJava例外(Exception)処理機構のサンプルコード共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート