Category: Java

8.継承

8章 継承
1.継承とは
 継承とは、あるクラス(A_Class)があるクラス(B_Class)の一種である場合、A_ClassはB_Classを継承しているという。そして、A_ClassはB_Classの子クラス・サブクラス・派生クラスといい、逆にB_ClassはA_Classの親クラス・スーパークラス・基底クラスという。
 これをJavaで定義すると以下のようになる。
class B_Class{
     :
     :
} 
 
class A_Class extends B Class 
   :
   :
}
 
 「一種である」というのは、下記の図形の例のように「楕円」や「線」や「多角形」は「図形」の一種であり、「円」は「楕円」の一種。「直線」は「線」の一種であるし、「三角形」や「四角形」は「多角形」の一種であるということである。

つまり、「三角形」や「四角形」は「多角形」を継承しているといい、「三角形」や「四角形」は「多角形」の子クラス(サブクラス)であるといい、「多角形」は「三角形」や「四角形」の親クラス(スーパークラス、基底クラス)である。

また、「正方形」は「菱形」と「長方形」の両方の特別な場合であり、このような継承の仕方を多重継承という。Javaのようにオブジェクト指向言語でも多重継承をサポートしていない。

 
 継承すると、親クラスのメンバー変数、メンバー関数は、その子クラスでも使用できるようになる。子クラスでは親クラスのメンバー関数をオーバーライトすることができるため子クラスで実相をより高機能化することができる。
 
2.スーパークラスのコンストラクタ
 クラスを継承して子クラスを作る場合、そのほとんどが親クラスのコンストラクタを明示的に呼び出す必要がある。つまり親クラスのデフォルトコンストラクタ以外を呼び出したい場合などである。
 StringVectorクラスと、ソース昨日付きVectorクラス(SortableStringVectorクラス)の場合を考えてみる。SortableStringVectorクラスのコンストラクタに、配列の大きさを指定すると、その引数を親クラスのコンストラクタに通知しなければならない。その場合は「super( )」を使う。このsuperは、コンストラクタの最初にしか用いることはできない。
Test.java
class Test{ 
    public static void main(String args[ ]){
         SortableStringVector vect = new SortableStringVector( );  
         String str;
          int      i = 0;
          while (true){
              str = readLine( );
              if( str.length( ) == 0 )
                  break;

              vect.at( i,str );
              i++;
           }

           vect.sort( );

           int n = vect.getLength( );
           for ( i = 0 ; i < n ; i++ ){
              str = vect.at( i );
              if( str.== null )
                  break;
              System.out.print(str);
           }
       }

           // 1行読み込んで、String型として返す
       public static String readLine( ){
          byte b[ ] = new byte[1024];
             try{
                 System.in.read(b);
                 } catch(Exception e){
                      return null;
                 }
 
                 int i;
                 for( i = 0 ; b[ i ] != 0 && i < b.length ; i++ );
                return new  String(b, 0, i);
                }
}
  

StringVector.java

public class StringVector{ 
     ///////////////////////////////////////////////////
     // 管理情報

    protected int m_size;                           // 配列サイズ
    protected String m_vector[ ];               // 配列

     // コンストラクタ:引数あり
     // StringVector オブジェクトが定義された時に自動的に呼び出される
    public StringVector( int sz );
       // 配列サイズを保存する
         m_size = sz ;

       // 配列の実体(size個のint配列)を自由記憶上に割り当てる
         m_vector = new String[m_size] ;
     }
  
  // コンストラクタ:引数なし
  // StringVector オブジェクトが定義された時に自動的に呼び出される
    public StringVector( ){
       // もう1つのコンストラクタを呼び出す
    this ( 100 );
     }

  // 代入
    public void at( int n,String value ){
          if ( n >= m_size || n < 0 ){
               System.err.println("invalid index");
               System.exit( 1 );       // 異常終了する
            }

            m_vector[n] = value;
            }

  // 取得
    public String at(int n){
           if ( n >= m_size || n < 0 ){
               System.err.println("invalid index");
               System.exit( 1 );       // 異常終了する
           }

               return m_vector[n];
     }
  // 現在の配列サイズを獲得する
      public int getLength( ){
               return m_size;
     }
}
  

SortableStringVector.java

public class SortableStringVector extends StringVector{
     ///////////////////////////////////////////////////
     // 管理情報

     // コンストラクタ:引数あり
     // SortableStringVector オブジェクトが定義された時に自動的に呼び出される
    public SortableStringVector( int sz ){
              // 親クラスのコンストラクタ呼び出し
             super(sz);
     }
 
  // コンストラクタ:引数なし
  // StringVector オブジェクトが定義された時に自動的に呼び出される
    public SortableStringVector( ){
       // もう1つのコンストラクタを呼び出す
    this ( 100 );
     }

  // バブルソート
    void sort( ){
          int flag = m_size -1;
          int flag = m_size;
          for ( ; j > 0 && flag ! = 0 ; j– ){
                 int i = 0;
                   flag = 0;
                 for ( ; j > i ; i++ ){
                             if ( m_vector[ i ] ! = null && m_vector[i+1] != null && m_vector[i+1].compareTo(m_vector[i]) > 0 ){
                            String a = m_vector[i+1];
                           m_vector[ i+1 ] = m_vector[i];
                           m_vector[ i ] = a;
                           flag = 1;
                       }
                 }
           }
     }
}
  

C:\java>javac Test.java

C:\java>java Test
123
234
345
222

345
234
222
123

C:\java>

 このように作成されたSortableStringVectorは、その親クラスであるStringVectorクラスのメソッドを使用することもできるし、新たに加えたsortメソッドも利用できる。親クラスを進化させることができる。
 
3.継承とキャスト
 下記のようにAクラスがBクラスの親クラスだった場合、Aクラス用の変数にBクラスのインスタンスを代入することができる。しかし、逆はエラーになる。犬は哺乳類だが、哺乳類は犬ではない。
class A{
       :
       :     
}
 
class B extends A{
       :
       :     
}
 
class Test{
      public static void main(String args[ ] ){
            // これはOK
      A a = new B( );

            // これはエラー
      B b = new A( );
     }
}
  

 これまで、StringVectorクラスを作ってきたが、何もStringクラスに特化させる必要はなかったのである。次のようにすればStringクラス以外のクラスもベクターとして管理することができる。
 JavaではすべてのクラスがObjectクラスの子クラスである。最初から用意されているStringクラスはもちろん、これまで作ってきたStringVectorクラスやSortableVectorクラスも実はObjectクラスを継承している。

したがって、下記のようにStringVectorクラスをObjectVectorクラスに書き換えたベクタークラスを用いて今まで通りStringVectorのように使うことができる。

Test.java

class Test{
    public static void main( String args[ ] ){
        ObjectVector vect = new ObjectVector( );
        vect.at(0, "HogeHoge");
        vect.at(1, "HonyoHonyo");

          System.out.println(vect.at( 0 ));
          System.out.println(vect.at( 1 ));
     }
}
  

ObjectVector.java

// StringVectorStringをObjectに変更したベクタークラス
public class ObjectVector{
    //////////////////////////////////////////////
    // 管理情報

        protected int m_size;                // 配列サイズ
        protected Object m_vector[ ];  // 配列

    // コンストラクタ:引数あり
    // ObjectVector オブジェクトが定義された時に自動的に呼び出される
          public ObjectVector( int sz ){
    // 配列サイズを保存する
          m_size = sz ;
 
    // 配列の実体(size個のint配列)を自由記憶上に割り当てる
          m_vector = new Object[m_size] ;
        }

    // コンストラクタ:引数なし
    // ObjectVector オブジェクトが定義された時に自動的に呼び出される
          public ObjectVector(  ){
         // もう1つのコンストラクタを呼び出す
             this (100) ;
        }

    // 代入
          public void at( int n, Object value ){
                 if ( n >=  m_size || n < 0 ){ 
                  System.err.println("invalid index");
                  System.exit(1);          // 異常終了する
         }

         m_vector[n] = value;
    }

    // 取得
          public Object at(int n){
                 if ( n >= m_size || n < 0 ){
                  System.err.println("invalid index");
                  System.exit(1);          // 異常終了する
         }

             return m_vector[n] ;
        }

    // 現在の配列サイズを獲得する
          public int getLength( ){
                 return m_size;
       }
}
  

 
Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1,"HonyoHonyo");
 
             String str1 = vect.at(0);
             String str2 = vect.at(1);
      }
}
  

C:\java>javac Test.java
Test.java:7 Incompatible type for declaration.Explicit cast needed to convert 
java.lang.Object to java.lang.String.
          String str1 = vect.at(0);

Test.java:8 Incompatible type for declaration.Explicit cast needed to convert
          String str2 = vect.at(1);
2 errors

C:\java>

 これは、ObjectVectorクラスのatメソッドの戻り値がObjectクラスだからである。前述したようにStringクラスはObjectクラスの一種だが、ObjectクラスはStringクラスの一種ではないため、Objectクラスとして返された戻り値はStringクラスに代入できない。

 しかし、プログラムをみればお分かりのとおり、実際に戻ってくるのはStringクラスのインスタンスである。この場合は、下記のようにキャストする。

Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1,"HonyoHonyo");
 
             String str1 = (String)vect.at(0);
             String str2 = (String)vect.at(1);
      }
}
  

C:\java>javac Test.java

C:\java>

 では、Stringクラスのインスタンスが入っていないのに、Stringクラスにキャストしようとするとどうなるのろうか。次の例では、StringクラスではなくTestクラスのインスタンスをObjectVectorに入れて、Stringクラスにキャストして取り出そうとしている。  
Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1," new Test( ));
 
             String str1 = (String)vect.at(0);
             String str2 = (String)vect.at(1);
      }
}
  

C:\java>javac Test.java

C:\java>java Test.java
Exception in thread "main" java.lang.ClassCastException: Test
           at Test.main(Test.java:8) 

C:\java>

 文法的には間違ってないのでコンパイルは正常にできるが、実際に実行しようとした場合にキャストできないとエラーとなってしまう。

 このようなことにならないようにプログラマーは気をつけなければならないが、下記のようにinstanceof演算子を用いて、ミスしても大丈夫なようにするべきである。

Test.java

class Test{
    public static void main(String args[ ] ){
         ObjectVector vect = new ObjectVector( ){
         vect.at(0,"HogeHoge");
         vect.at(1," new Test( ));
 
             String str1 = null ;
             String str2 = null ;
             Object obj; = null ;

          obj = vect.at(0);
          if( obj instanceof String )
              str1 = ( String )obj;

          obj = vect.at(1);
          if( obj instanceof String )
              str2 = ( String )obj;
    }
}
  

C:\java>javac Test.java

C:\java>java Test

C:\java>

 
4.スコープ 
 同じ変数や関数名を、親クラス、子クラスで定義するとどのようになるのだろうか。コンパイラは次のような順番で、目的とする変数や関数を検索する。
 派生クラスをつくり、その派生クラスのメンバー関数での例をしめす。
  ・派生クラスのメンバーメソッド内で定義した変数
  ・派生クラスのメンバー変数
  ・親クラスのメンバー変数 
// 親クラス
class Base{
    public    int a;
    private   int b;
}
 
// 子クラス
class Deriv extends Base{
    public    int b;
    private   int c;

    public void member_func_deriv( ){
             a = 1;               // 派生クラスにはないので、親クラスのaを参照
            b = 2;               // 派生クラスのbを参照
            c = 3;               // 派生クラスのcを参照
 
           super.a = 10;     // Baseクラスのaを参照
//      super.b = 20;     // Baseクラスのbを参照しようとするが、privateなのでエラー

     public static void main(String args[ ]){
           Deriv d = new Deriv( );

            d.a = 10;               // 派生クラスにはないので、親クラスのaを参照
            d.b = 10;               // 派生クラスのbを参照
            d.c = 10;               // 派生クラスのcを参照しようとするが、privateなのでエラー

//      d.super.a = 10;       // このような使い方はできない
  }
}
 
  

 
5.クラスの作成 
クラスの構築
 
・領域が確保される
 ・派生クラスの引数リストの一致するコンストラクタを呼び出す
 ・superがあれば、親クラスの引数リストの一致するコンストラクタを呼び出す
 ・superがなければ、親クラスの引数リストのないコンストラクタ(デフォルトコンストラクタ) を呼び出す
 ・派生クラスのコンストラクタの続きが実行される
※親クラスが他のクラスの派生クラスならば、再帰的に繰り返される
//親クラス
class Base{
    public Base( ){
             System.out.println("Base:Base( )");
      }
    public Base( int i ){
             System.out.println("Base:Base( int )");
    }
}

//子クラス
class Deriv extends Base{
    public Deriv( ){
             System.out.println("Deriv::Deriv( )");
    }
    public Deriv( int i ){
          super ( i );
             System.out.println("Deriv::Deriv( )");
    }

    public static void main( String args[ ]  ){
             System.out.println("main");
          Deriv d1 = new Deriv( );
          Deriv d2 = new Deriv( 1 );
    }
}
 

C:\java>javac Deriv.java

C:\java>java Deriv
main
Base::Base( )
Deriv::Deriv( )
Base::Base( int )
Deriv::Deriv( int )

C:\java>

 
6.abstarct 
 abstractとはメソッドの定義だけしておき、そのクラスでは実装せずに子クラスで実装するためのもである。abstractを含むクラスは完成していないクラスとなるために、インスタンスは作られない。子クラスでそのメソッドを実装し、子クラスのインスタンスを作る。
 abstractなクラスは、「class」の前に「abstract」をつけないと、コンパイルエラーになってしまう。
//親クラス
abstract class Base{
    abstract
public void func( );

//子クラス
class Deriv extends Base{
    public void func( ){
         System.out.println("Deriv::func");

    }

    public void main( String args[ ]){
          Base base1 = new Deriv( );      // OK
          Base base2 = new Base( );      // エラー
    }
}

基底クラスに純粋仮想関数が複数ある場合、すべての純粋仮想関数を派生クラスで実装しなければ派生クラスもまた抽象クラスになる。 
//親クラス
abstract class Base{
    abstract
public void func1( );
    abstract public void func2( );

//子クラス
abstract class Deriv extends Base{
    public void func1( ){
         System.out.println("Deriv::func1");
    }
}

//さらに子クラス
class DDeriv extends Deriv{
    public void func2( ){
         System.out.println("DDeriv::func2");
    }
    public void main( String args[ ] ){
          Base base1 = new DDeriv( );      // OK
          Base base2 = new Deriv( );       // エラー
          Base base3 = new Base( );       // エラー
    }
}

 抽象クラスは概念として導入する場合が多い。つまりお絵かきソフトを作ろうと考えた場合、図形データはObjectVectorクラスに入れる。図形データは以下のように作る。
//図形クラス(抽象クラス)
abstract class Zukei{
   
protected int x1, x2, y1, y2;

    //描画する関数
  public abstract void draw( );

    //セーブする関数
  public abstract void save( String filename);
}

//四角形クラス
class Rectangle extends Zukei {
    
//描画する関数(このクラスで実装する)
  public void draw( ){
         :
         :
    }

    //セーブする関数(このクラスで実装する)
    public void save( String filename){
         :
         :
    }
}

//直線クラス
class Line extends Zukei {
    
//描画する関数(このクラスで実装する)
  public void draw( ){
         :
         :
    }

    //セーブする関数(このクラスで実装する)
    public void save( String filename ){
         :
         :
    }
}

//円クラス
class Circle extends Zukei {
    
//描画する関数(このクラスで実装する)
  public void draw( ){
         :
         :
    }

    //セーブする関数(このクラスで実装する)
    public void save( String filename ){
         :
         :
    }
}

class Draw{
     public static void main( String args[ ]){
     ObjectVector vect = new ObjectVector( );
     vect.at(0, new Line( ));
     vect.at(1, new Circl( ));
    }
}

 
7.継承と包含
 新しいクラスを作成しようとした際には、次の3つの選択肢がある。
  1.まったくゼロから作る。
  2.既存のクラスを継承して、派生クラスとして作る。
  3.既存のクラスを包含して新しく作る。
 

11.例外

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>

 
 
 
 
 
 
 

9.インターフェース

9章 インターフェース
1.インターフェースとは
 ひし形クラスと長方形クラスの両方の特徴を継承した正方形クラスのようなものを多重継承という。しかし、Javaでは多重継承の概念は取り入れられていません。これを解決するためにインターフェースという機能をサポートしている。
 インターフェースとは、定数とメソッドの宣言だけを集めたものである。宣言だけで実際のメソッド本体などは実装しない。あるクラスを作ろうとした場合に、「インターフェースをインプリメントする」と、そのクラスはインターフェースで宣言したメソッドと変数を必ず実装しなければならなくなる。
 つまり、MyInterfaceというインタフェースを作り、その中で「func1」というメソッドを宣言したとする。そして、MyClassを作ろうとした場合にMyInterfaceをインプリメントした場合、MyClassの中にはかならずfunc1というメソッドを実装する必要がある。
 一般的に継承は継投を表し、インターフェースは機能を表す。しかし、具体的な機能を決めるものではなく、このような機能があることを明示するためのものである。したがって、インターフェースはJavaのポリモーフィズムを実現する1つの手段であるといえる。
 ここで、例を示す。犬クラスは哺乳類クラスのサブクラスである。犬にも秋田犬クラス、ゴールデンレトリバークラスやブルドッククラスなどがサブクラスとして存在するかもしれない。これらは動物の種類の系統でサブクラスを作ってきている。また犬には、盲導犬や麻薬捜査犬、救助犬といった分類もあるが、これらはそれぞれ、盲人を誘導する機能を有するとか麻薬を見つけるとか救助するといった機能を表す。つまり、犬の種類とは関係ない。したがって、これらの機能)(インタフェース)をインプリメントした犬クラスが盲導犬クラスというように定義できるのである。
 今の世の中では盲導象や盲導猫というのは存在しないが、もしかすると将来盲導猫というものができたとすれば、猫クラスのサブクラスで、盲導インターフェースをインプリメントして盲導猫クラスを作ることができる。  
  
2 インターフェースの宣言 
interface interfaceName{
    type name = value
    type name = value                                           // 変数
    type method( paramType name… );           // メソッド
}
   

interface Shape2D{
     // 面積を計算する
  double getArea( );
}

 上記のようなShepe2Dインターフェースをインプリメントしたクラスは、必ずgetAreaメソッドを実装しなければならない。下記にその使用例を示す。 
 // 図形クラス
abstract class Shape{
    abstract void Display( );
}

// 円クラス
class Circle extends Shape implements Shape2D{
    int x;
    int y;
    int r;

    Circle ( int x, int r ){
        this.x = x;
        this.x = y;
        this.x = r;

    }

    void Display( ){
         System.out.println("x=" + x + ", y=" + y + ", r=" + r);
    }

    // 面積を計算する
    public double getArea( ){
         return r*r*Math.PI ;    // Math.PI:円周率
    }
}

    // 長方形クラス
class Rect extends Shape implements Shape2D{
        int x1;
       int y1;
       int x2;
       int y2;

     Rect ( int x1, int y1, int y2 ){
               this.x1 = x1;
               this.y1 = y1;
               this.x2 = x2;
               this.y2 = y2;
       } 

       void Display( ){
              System.out.println("x1=" + x1 + ", y1=" + y1 + ", x2=" + y2 + ", y2=" + y2);
       }

       // 面積を計算する
       public double getArea( ){
            // Math.abs : 絶対値を求めるメソッド
              return Math.abs(x1-x2) * Math.abs(y1-y2);
       }
}

 Shapeクラスは図形クラスであり、図形というからには何らかの形で表示する必要がある。表示方法は円や三角形、長方形など図形によって異なる。そこで、Shapeクラスでは実装できずサブクラスで実装する必要があるため、abstractになっている。
 Shapeクラスのサブクラスとして、ここではCircleクラス(円クラス)とRectクラス(長方形クラス)を定義している。ここでは、そのクラスの機能として面積を計算するためのShape2Dインターフェースをインプリメントしている。
  
3 インターフェースの参照 
Test.java
public class Test{
      public static void main(String args[ ]){
         Shape2D array[ ] = new Shape2D[5];

          array[0] = new Circle(5,3,2);
          array[1] = new Rect(5,3,20,7);
          array[2] = new Circle(3,1,1);
          array[3] = new Rect(0,0,3,3);
          array[4] = new Circle(-5,2,2);
 
          double area = 0;    // 面積 
          area += array[0].getArea( );
          area += array[1].getArea( );
          area += array[2].getArea( );
          area += array[3].getArea( );
          area += array[4].getArea( );
  

          System.out.println("面積の合計="+area);
      }
}
  

C:\java>java Test
面積の合計=97.27433388230814

C:\java>

  
4 インターフェースと修飾子 
 クラスにはpublic,protected,privateといった修飾子がある。インターフェースにもこれらの修飾子を使うことが可能である。もちろん、インターフェースに含まれる変数やメソッドにも用いることも可能である。
public interface Shape2D{
     // 面積を計算する
   public double getArea( );
}
  
5 インターフェースの継承 
 クラスには継承という機能がある。インターフェースにも継承という機能がある。クラスは多重継承ができないが、インターフェースは多重継承をすることができる。その場合、extendsの後に継承したいインターフェースをカンマで区切って記述する。
public interface Interface extends Interface1,Interface2{
     // 面積を計算する
   public double method( );
}
  
6 インターフェースとinstaceof演算子
 クラスはinstanceof演算子で、インスタンスがそのクラスのインスタンスか否かを判定することができる。インターフェースも同様にインスタンスが指定されたインターフェースをインプリメントかどうかをinstanceof演算子を用いて調べることができる。
 下記の例は、面積の合計を求める先ほどのサンプルをinstanceof演算子を用いて、円の面積の合計と長方形の面積の合計をそれぞれ算出するように書き換えたものである。
Test.java

public class Test{
   public static void main(String args[ ]){
         Shape2D array[ ] = new Shape2D[5];

         array[0] = new Circle(5,3,2);
         array[1] = new Rect(5,3,20,7);
         array[2] = new Circle(3,1,1);
         array[3] = new Rect(0,0,3,3);
         array[4] = new Circle(-5,2,2);

         double areaCircle = 0;    // 円の面積
         double areaRect   = 0;    // 長方形の面積

    for( int i = 0 ; i < array.length ; i++ ){
              if( array[i] instanceof Circle )
                  areaCircle += array[i].getArea( );

              else
                  areaRect   += array[i].getArea( );
        
}

         System.out.println("円面積の合計="areaCircle);
         System.out.println("長方形面積の合計="areaRect);
         System.out.println("面積の合計="+ (areaCircle + areaRect));
     }
}

C:\java>java Test
円面積の合計=28.274333882308138
長方形面積の合計=69.0
面積の合計=97.27433388230814

C:\java>