11章 例外
1.例外とは
 例外とは予期しなかった処理のことをいう。例えば、配列の中身の平均値を求めるメソッドを作ろうとした場合、かきのようになる。
class class Test{
    public static void main(String args[ ] ){
            int array[ ] = new int[5];

           array[0] = 10;
           array[1] =  5;
           array[2] =  2;
           array[3] =  4;
         array[4] =  9;
 
             String.out.println("average="+average(array));
      }

    public static int average(int[ ] array){
            int total = 0;
          for( int i = 0 ; i < array.length ; i++ )
               total += array[ i ];

          return total / array.length;
    }
}
   

C:\java>javac Test.java

C:\java>java Test
average=6

C:\java>

ところが、配列の長さが0だった場合はaverageメソッドの中で「0で割る」という計算が発生してしまいエラーになってしまう。
class class Test{
    public static void main(String args[ ] ){
            // int array[ ] = new int[5];
         
int array [ ] = new int [0];
/*
           array[0] = 10;
           array[1] =  5;
           array[2] =  2;
           array[3] =  4;
         array[4] =  9;

/* 
             System.out.println("average="+average(array));
      }

    public static int average( int[ ] array ){
            int total = 0;
          for( int i = 0 ; i < array.length ; i++ )
               total += array[ i ];

          return total / array.length;
    }
}
   

C:\java>javac Test.java

C:\java>java Test
Exception in thread "main" java.lang.ArithmeticException: / by zero
          at Test.average(Test.java:20) 
          at Test.main(Test.java:12)

C:\java>

 このように予期せぬエラーが発生するとJavaではそのスレッドが終了してしまう。この例のように単一スレッドの場合はプログラムが終了してしまう。 
 このエラーメッセージにある「Exception」というのが例外をあらわしており、「in thread "main"」がスレッドをあらわしている。そしてその後の「java.langArithmeticException:/ by zero」が例外の内容をあらわしていて、ここではゼロで割ったという例外であることがわかる。次の行からどの部分で例が起ったかをあらわしている。Test.javaの20行目、Testクラスのaverageメソッドで起っていることがわかり、さらに次の行を見ると、Test.averageメソッドはTest.javaの12行目(Testクラスのmainメソッド)から呼び出されていることがわかる。
 
2.例外が起きたら
 例外が起きた場合に、アプリケーションが終了してしまっては困る。そこでプログラマーは例外を予測して、例外が起きた場合に適当な処理をさせる必要がある。
 例えば平均を求めるメソッドの場合、引数で与えられた配列がnewし忘れたなどで、配列の実体がなかった場合などは、 NullPointerExceptionという名前の例外が発生する。平均を求めるメソッドでは、この2つの例外が発生する可能性があるので、例外が起きた場合専用の処理をするようにする。
public class Test{
    public static void main(String args[ ] ){
            // int array[ ] = new int[5];
         
int array [ ] = new int [0];

          System.out.println("average="+average(array));
    }

    public static int average( int[ ] array ){
            try {
                int total = 0 ;
                for ( int i = 0 ; i < array.length ; i++ )
                      total += array[ i ];

                return total / array.length;
          }catch(NullPointerException e){
                System.out.println("newし忘れてませんか?");
          }catch(ArithmeticException e){
                System.out.println("配列が空です。");
          }
      return 0;

     }
}

C:\java>javac Test.java

C:\java>java Test
配列が空です 
average=0

C:\java>

 上記のように、例外が発生すると思われる部分を
try{
    // 例外が発生する可能性のあるブロック
}catch(例外の種類1 param1){
   // 例外の種類1が発生した場合に実行される部分
}catch(例外の種類2 param1){
   // 例外の種類2が発生した場合に実行される部分
}finally{
   // 例外が起きても起きなくても実行される部分
}
 上記の構文を用いた場合、Jvmはtryブロックを実行する。もしtryブロックの中で例外が発生しなければ、catchブロックは実行せずに、finallyブロックを実行する。 
もし、tryブロック実行中に例外が発生したら、発生した場所から例外の内容に応じてcatchブロックが実行される。この場合、tryブロックはすべてが実行されるわけではなく、例外が発生した部分よりも下の部分は実行されずにcatchブロックにジャンプする。上記の例で、averageメソッドの引数にnullが渡された場合、for分のarray.lengthでNullPointerExceptionという例外が発生する。したがって、forの中身は実行されずにNullPointerExceptionをキャッチしているブロックに処理が移行する。
 なお、finallyブロックは省略することができる。catchブロックは1つ以上必要になる。
 
3.例外の種類
ClassNotFoundException  呼び出そうとしたクラスのクラスファイルが無かった場合
CloneNotSupportedException オブジェクトを複製するためにObjectクラスのcloneメソッドが呼び出されたが、そのオブジェクトのクラスがCloneableインターフェイスを実装していないことを示すためにスローされる。
DataFormatException  データ形式エラーが発生したことを通知する
IOException  なんらかの入出力例外の発生を通知するシグナルを発生させる。
サブクラスとしてEOFException,FileNotFoundExceptionなどがある。
PrinterException 印刷システムに例外的な状態が発生したことを示すために使用される。
サブクラスとしてPrinterAbortException,PrinterIOExceptionがある。
RuntimeException  JVMの通常の処理でスローすることができる各種の例外のスーパークラス。
ArithmeticException  RuntimeExceptionのサブクラス。
算術計算で例外的条件が発生した場合にスローされる。例えば、「ゼロで除算される」整数はこのクラスのインスタンスをスローする。
ArrayStoreException RuntimeExceptionのサブクラス。
不正な型のオブジェクトをオブジェクトの配列に格納しようとしたことを示すためにスローされる。
ClassCastException RuntimeExceptionのサブクラス。
あるオブジェクトを継承関係にないクラスにキャストしようとしたことを示すためにスローされる。
IllegalArgumentException  RuntimeExceptionのサブクラス。
不正な引数、または不適切な引数をメソッドに渡したことを示すためにスローされる。
IndexOutOfBoundsException  RuntimeExceptionのサブクラス。
ある種のインデックス(配列、文字列、ベクトルなど)が範囲外であることを示すためにスローされる。サブクラスにArrayIndexOutOfBoundsException,StringIndexOutOfBoundsExceptionがある。
NegativeArraySizeException RuntimeExceptionのサブクラス。
負のサイズを持った配列をアプリケーションが作成しようとした場合にスローされる。
NoSuchElementException RuntimeExceptionのサブクラス。
この列挙にそれ以上の要素がないと、EnumerationのnextElementメソッドによってスローされる。
NullPointerException RuntimeExceptionのサブクラス。
オブジェクトが必要な場合に、アプリケーションがnullを使おうとするとスローされる。
SecurityException RuntimeExceptionのサブクラス。
セキュリティマネージャによってスローされ、セキュリティ違反を示す。
サブクラスにAccessControlException,RMISecurityExceptionがある。
NumberFormatException IllegalArgumentExceptionのサブクラス。
アプリケーションが文字列を数値型に変換しようとしたとき、文字列の形式が正しくない場合にスローされる。
 
4.catchブロックの検索
 tryブロック内で例外が発生した場合は、tryブロックの次に記述されているcatchブロックに対応する例外処理が書かれているかを検索する。しかし、対応する例外処理が書かれていなければ(対応する例外をキャッチするcatchブロックがない場合)、そのtryブロックを囲っているさらに上のtryブロックにかかれているcatchブロックを検索する。つまり、例外処理が起きたメソッドを呼び出している呼び出し元のtry〜catchブロックなどを探す。そこにも発生した例外をキャッチするcatchブロックが無ければ、さらに呼び出し元を検索する。
 可能な限りさかのぼってもキャッチされない場合は、そのスレッドはJVMによって強制的に終了されてしまう。もし単一スレッドであった場合は、アプリケーションが終了する。
public class Test{
    public static void main(String args[ ] ){
          try{
            a( );
       }catch (NullPointerException e){
            System.err.println(e);
                }

            System.out.println("アプリケーションは正常に終了しました");
          }

          public static void a( ){
              try{
                    b( );
          }catch(ArithmeticException e){
                  System.err.println(e);
          }
  }

      public static void b( ){
             int array[ ] = new int[2];
           // ArrayIndexOutOfBoundsExceptionを発生
           array[3] = 10;
          }
  }
 

C:\java>javac Test.java

C:\java>java Test
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
           at Test.b(Test.java:22)
           at Test.a(Test.java:12)
           at Test.main(Test.java:3)
           
C:\java>

 例えば、NullPointerExceptionはRuntimeExceptionのサブクラスで、RuntimeExceptionはExceptionのサブクラスなので下記のように記述する。 
public class Test{
    public static void main(String args[ ] ){
          try{
            int.array[ ] = null;
            array[0] = 10;
        }catch(NullPointerException e{
            System.err.println("NullPointerException");
        }catch(RuntimePointerException e{
            System.err.println("RuntimeException");
        }catch(Exception e){
            System.err.println("Exception");
           }
     }
 
C:\java>javac Test.java

C:\java>java Test
NullPointerException
           
C:\java>

 ところが、下記の例では、スーパークラスのExceptionから記述しようとしたため、すべての例外は最初のExceptionクラスのcatchブロックでキャッチされてしまう。キャッチブロックは一回の例外で一度しか処理しないため、その下のRuntimeExceptionやExceptionやNullPointerExceptionではキャッチされない。したがって、RuntimeExceptionやNullPointerExceptionのキャッチブロックは処理されることがないため、コンパイルエラーになる。
public class Test{
    public static void main(String args[ ] ){
          try{
            int.array[ ] = null;
            array[0] = 10;
        }catch(Exception e{
            System.err.println("Exception");
        }catch(RuntimeException e{
            System.err.println("RuntimeException");
        }catch(NullPointerException e){
            System.err.println("NullPointerException");
           }
     }
}
 
C:\java>javac Test.java
Test.java:8 catch not reached.
           }catch(RuntimeException e){
            ^
Test.java:10: catch not reached.
           }catch(NullPointerException e){
            ^           
2 errors

C:\java>

 
5.throw
 例外は受け取るだけでなく、呼び出し元に例外処理を任せることもできる。これを「例外を投げる」という。例えば、そのキャッチブロックで例外処理をしただけではダメで、呼び出しもとのキャッチブロックでも例外処理をしなければならないような場合に使う。
 下記の例ではaメソッド内で例外処理をした後、throwステートメントを用いて、例外を上に投げている。
public class Test{
  public static void main(String args[ ] ){
        try{
              a( );
        }catch(RuntimeException e){
            System.err.println("mainメソッド内で例外を処理しました("+e+")");
        }
        System.out.println("アプリケーションは正常に終了しました");
    }

  public static void a( )
        try{
             
int array[ ] = new int[2];
           // ArrayIndexOutOfBoundsExceptionを発生
           array[3] = 10;
        }catch(RuntimeException e){
            System.err.println("aメソッド内で例外を処理しました("+e+")");
         throw e;
        }
   }
}
   
C:\java>javac Test.java

C:\java>java Test
aメソッド内で例外を処理しました(java.lang.ArrayIndexOutOfBoundsException)
aメソッド内で例外を処理しました(java.lang.ArrayIndexOutOfBoundsException)
アプリケーションは正常に終了しました

C:\java>

 上記の例では、生成されたRuntimeExceptionのインスタンスをthrowで上に投げている。throwステートメントは生成されたExceptionを投げるだけでなく、プログラマーが明示的にnewした例外などを上に投げることもできる。投げることができるのは、Throwableクラスのサブクラスだけである。Stringクラスなどのインスタンスは投げることができない。ThrowableクラスのサブクラスはExceptionクラス(すべての例外のスーパークラス)と、Errorクラスである。
public class Test{
  public static void main(String args[ ] ){
        int array[ ] = new int[2];
        try{
              a(array, 10);
        }catch(RuntimeException e){
               System.err.println("mainメソッド内で例外を処理しました("+e+")");
        }
    }

  public static int a(int array[ ],   int i ){
        if( i < 0 || i >= array.length ){
               System.out.println("ArrayIndexOutOfBoundsExceptionを投げます");
           throw new ArrayIndexOutOfBoundsException( );
        }

        System.out.println("aメソッドは正常に終了します");
         return array[ i ];
   }
}
   

C:\java>javac Test.java

C:\java>java Test
ArrayIndexOutOfBoundsExceptionを投げます
mainメソッド内で例外を処理しました(java.lang.ArrayIndexOutOfBoundsException)

C:\java>

 
6.throws
 例外を投げる可能性があるメソッドを作成した場合は、他のプログラマーが見てもすぐに例外が投げられる可能性があることがすぐわかるように、明示しておくべきである。明示するためには、例外を投げる可能性があるコンストラクタやメソッドの宣言時にthrowステートメントを用いる。
public class Test{
  public static void main(String args[ ] ){
        int array[ ] = new int[2];
        try{
              a(array, 10);
        }catch(RuntimeException e){
               System.err.println("mainメソッド内で例外を処理しました("+e+")");
        }
    }

  public static int a(int array[ ],   int i ) throws ArrayIndexOutOfBoundsException{
        if( i < 0 || i >= array.length ){
               System.out.println("ArrayIndexOutOfBoundsExceptionを投げます");
           throw new ArrayIndexOutOfBoundsException( );
        }

        System.out.println("aメソッドは正常に終了します");
         return array[ i ];
   }
}
   

C:\java>javac Test.java

C:\java>java Test
ArrayIndexOutOfBoundsExceptionを投げます
mainメソッド内で例外を処理しました(java.lang.ArrayIndexOutOfBoundsException)

C:\java>

 投げる例外が複数ある場合は、カンマ区切で列挙する。
 throwステートメントは記述しなくても問題ないが、特に複数人でプログラミングする場合など、自分以外の人にわかりやすくするために記述することが好ましい。また、1人でプログラムしていても、しばらくたつと忘れてしまう場合もあるため、記述することを勧める。
 
7.エラーと例外
 throwできるクラスはThrowableクラスのサブクラスだけである。ThrowableクラスにはErrorクラスとExceptionクラスの2つがある。一般にアプリケーションの使い方をユーザーが間違えた場合、ファイルをセーブしようとしたのにディスクがいっぱい・通信の失敗など一般に予期することができるようなエラーはJavaではエラーではなく例外として処理する。逆にJVMの重大なエラーを示す。例えばメモリー不足、スタックオーバー、内部エラー、不正アクセスなど、プログラマーやユーザーのせいではないエラークラスとする。したがって、一般にはエラークラスのサブクラスを作成することはできるが一般にはしない。
 Throwableクラス、Errorクラス、Exceptionクラスのコンストラクタとメソッドを紹介する。これらのサブクラスを作る方法は、必要なメソッドをオーバーライドする必要がある。
Throwableクラスのメソッドの概要
Throwable( )
 エラーメッセージ文字列としてnullを指定してThrowableを新しく構築する
Throwable(String message )
 指定されたエラーメッセージを持つ、Throwableを新しく構築する 
Throwableクラスのメソッドの概要 
Throwable fillInStackTrace( )
 実行スタックトレースを埋め込む。
String getLocalizedMessage( )
 Throwableの地域対応された記述を作成する。
String getMessage( )
 このThrowableの地域対応された記述を作成する。
Void printStackTrace( )
 Throwableとそのバックトレースを標準エラーストリームに出力する。
Void printStackTrace(PrintStream s)
 Throwableとそのバックトレースを指定された印刷ストリームに出力する。
Void getStackTrace(PrintWriter s)
 このThrowableとそのバックトレースを指定されたプリントライターに出力する。
String toString( )
 Throwableオブジェクトの短い記述を返す。
Errorクラスのコンストラクタの概要
Error( )
 詳細メッセージを指定しないでErrorを構築する。
Error(String s)
 指定された詳細メッセージを持つErrorを構築する。 
Errorクラスのコンストラクタの概要
Exception( )
 詳細メッセージを指定しないでExceptionを構築する。
Exception(String s)
 指定された詳細メッセージを持つExceptionを構築する。  
Throwable,Error,Exception 

public Throwable( )
public Error( )
public Exception( )

 エラーメッセージ文字列としてnullを指定してThrowable,Error,Exceptionを新しく構築する。

Throwable,Error,Exception 

public Throwable(String message)
public Error(String message)
public Exception(String message)

 指定されたエラーメッセージを持つ、Throwable,Error,Exceptionを新しく構築する。
 パラメータ:message-エラーメッセージ。エラーメッセージはgetMessage( )メソッドによる取り出しのために保存される。

getMessage 

public string getMessage( )

 このThrowableオブジェクトのエラーメッセージ文字列を返す。
 戻り値:エラーメッセージ文字列突きで作成された場合は、このThrowable,Error,Exceptionオブジェクトのエラーメッセージ文字列。エラーメッセージなしで作成された場合はnull。

toString 

public String toString( )

 Throwable オブジェクトの短い記述を返す。このThrowable,Error,Exceptionオブジェクトがエラーメッセージ文字列付きで作成された場合、結果は次の3つの文字列を連結したものとなる。
 ・オブジェクトの実際のクラス名
 ・": "(コロン、スペース)
 ・オブジェクトに対するgetMessage( )メソッドの結果
 Throwable,Error,Exceptionオブジェクトがエラーメッセージ文字列なしで作成された場合は、オブジェクトの実際のクラス名が返される。
 戻り値:
    このThrowable,Error,Exceptionの文字列表現
 オーバーライド:
    クラスObject内のtoString

printStackTrace
public void printStackTrace( )

 Throwable,Error,Exceptionとそのバックトレースを標準エラーストリームに出力する。
       java.lang.ArithmeticException: / by zero
                            at Test.average(Test.java:20)
                           at Test.main(Test.java:12)

8.独自の例外
 Exceptionクラスを継承して独自の例外クラスを作成することもできる。これはアプリケーション特有の例外を作成したい場合などに使用される。
 独自のエラークラスを投げるメソッドでは、必ずキャッチするか、throwsステートメントで、独自の例外を投げる可能性があることを明示しなければならない。
// 独自の例外クラス
class MyException extends Exception{
                public MyException( ){
                               super( );
              }
}

public class Test{
       public static void main(String args[ ]) {
                try{
                 a( );
               }
     }

       public static void a( ) throws MyException{
              throw new MyException( );
          }
}

C:\java>javac Test.java

C:\java>java Test
mainメソッド内で例外を処理しました(MyException)
           
C:\java>

 上記の例で、throw MyExceptionやmainメソッド内のcatchを忘れると、コンパイルエラーになる。
 ただし、独自の例外がRuntimeExceptionのサブクラスの場合は、この制限は適用されない。
// 独自の例外クラス
class MyException extends Exception{
                public MyException( ){
                               super( );
              }
}

public class Test{
       public static void main(String args[ ]) {
                try{
                 a( );
               } catch(MyException e){
                 System.err.println("e");
            }
  }

       public static void a( ){
              throw new MyException( );
          }
}

// 独自の例外クラス
class MyException extends RuntimeException{
                public MyException( ){
                               super( );
              }
}
public class Test{
     public static void main(String args[ ]){
          try{
               a( );
              } catch(MyException e ){
       System.err.println(e);
              }
       }

     public static void a( ){
          throw new MyException( );
       }
}

C:\java>javac Test.java
Test.java:11: Exception MyException is never thrown
in the body of the corresponding try statment.
            }catch(MyException e){
           
Test.java:17: Exception MyException must be caught,
or it must be declared in the throws clause of this method.
          throw new MyException( );

2 errors

C:\java>

C:\java>javac Test.java

C:\java>