Category: Java

10.パッケージ

10章 パッケージ
1.パッケージとは
 パッケージとはクラスやインターフェースをひとまとめにしたものである。 
 例えば、ファイルが多くなってくると同じカテゴリのファイルは新しいフォルダを作成してそこに移動して、ファイルを整理する。Javaでも同じようなことができる。つまり、同じカテゴリのクラスやインターフェースは1つのパッケージにしている。
 ディレクトリと同じように、パッケージを階層構造にすることもできる。
 Javaでは標準で様々なクラスが用意されているが、これらも機能のカテゴリごとに分類され、パッケージ化されている。例えば、ネットワーク関係のクラスは「java.net」というパッケージに各クラスがしまわれている。これは、「java」パッケージという意味になる。そのほかにも「java.util」、「java.lang」など多くのパッケージが用意されている。
パッケージ  解説
java.lang Javaの中核を成す機能を担うクラス群
java.util ユーティリティ機能を提供するパッケージ
java.net ネットワーク機能を提供するパッケージ
java.io 入出力機能を提供するパッケージ
java.awt Javaの基本的なGUI機能(AWT:Abstract Window Toolkit)を提供するパッケージ
java.awt.event AWTの中でもイベントの機能(ボタンが押された場合など)を提供するパッケージ
 
2.パッケージの宣言 
 あるクラスをあるパッケージに格納するためにはいくつかの手続きが必要になる。一つ目はjavaファイルにパッケージ名を格納すること、二つ目はパッケージ名と同じディレクトリをつくり、そこにjavaファイルを格納することである。
1)Javaファイル(ソースファイル)の最初に
package packageName 
と記述する。 
packageNameはパッケージ名をあらわし、例えば下記のように記述する。
// entertainmentパッケージとして定義
package entertainment
// entertainmentパッケージの中のTVパッケージとして定義
package entertainment.TV
2)ディレクトリ
 パッケージ構成と同じディレクトリを構成する必要がある。つまり、上の例ではentertainmentパッケージとしたjavaファイルは、「entertainment」というディレクトリの中に格納する必要があり、entertainment.TVパッケージとしたjavaファイルは、「entertainment\TV」というディレクトリの中に格納する必要がある。
3)パッケージの指定 
 異なるパッケージのクラスを指定する場合は、「パッケージ名.クラス名」として参照する。詳しくは下記の例を参照。
4)例  
PackageDemo.java

public class PackageDemo{
    public static void main(String args[ ] ){
     // パッケージ内のクラスを参照する場合は「パッケージ名.クラス名」として参照する
        testPackage.A a = new testPackage.A( );
        testPackage.B b = new testPackage.B( );
        a.a1( );
        b.b1( );
     }
}
   

testPackage\A.java

// testPackageパッケージとして定義
package testPackage;

public class A{
     public void a1( ){
          System.out.println("a1");
     }
}
   

testPackage\B.java

// testPackageパッケージとして定義
package testPackage;

public class B{
  public void b1( ){

          System.out.println("b1");
     }
}
   

C:\java>javac PackageDemo.java

C:\java>java PackageDemo

a1
b1

C:\java>

 
3.クラスパス
 「entertainment.TV」というクラスを指定した場合、javaは以下のような場所を探す。
 1.カレントディレクトリの中の「entertainment」ディレクトリの中のTV.class
 2.Javaが標準で用意されているクラス郡の「entertainment」パッケージの中のTV.class
 しかし、これだけでは、自分で部品として使えるようにしたクラス群をパッケージとして作成した場合や、便利なパッケージをダウンロードしてきた場合、いちいち自分がこれから作ろうとするディレクトリにすべてコピーしなくてはならなくなる。これでは不便なので、一度部品として切り分けられるようにして作ったパッケージは、ある特定のディレクトリにおいておき、これを指定しておけば、いろいろなプログラムからそのパッケージをいつでも参照することができる。その設定がクラスパスである。
C:\java>set classpath=c:\java\mypackage;c:java\downloadpackage

C:\java>set classpath
CLASSPATH=c:\java\mypackage;c\java\downloadpackage

C:\java>

 クラスパスはUNIXやWindowsでは環境変数で設定する。
 このように設定すると、javaは
 1.カレントディレクトリの中の「entertainment」ディレクトリの中のTV.class
 2.c:\mypackage\entertainment\TV.class
 3.c:\downloadpackage\entertainment\TV.class
 4.Javaが標準で用意されているクラス郡の「entertainment」パッケージの中のTV.class
という手順でTV.classを探しに行く。
 
4.import
 testPackageパッケージ内のAクラスを指定するのに「testPackage.A」と記述した。このように「パッケージ.クラス名」と指定する方法を完全修飾名を指定するという。
 しかし、この方法は、わざわざパッケージを名をしてしなければならないので面倒である。そこで、あらかじめ「testPackage」を使うとJavaに指定してやれば、わざわざ完全修飾名で指定しなくてもすむ。つまり、C言語でいう「#include」のようなものである。Javaでは「#include」ではなく「import」というステートメントを使用する。
 importはjavaファイルのはじめに記述し、そのjava内で使用するクラスをimportの後に、「パッケージ名.クラス名」の形式で宣言する。そのパッケージ内のすべてのクラスを宣言したい場合は、クラス名を「*」として指定する。まだ、複数のパッケージを指定することもでき、その場合はimport行を何行も列挙する。
 以下に例を示す。左側は完全修飾名を指定して記述した場合で、右側はimportを使用して、使用するパッケージを宣言した例である。以下の二つの例は同じになる。
PackageDemo.java

public class PackageDemo{
    public static void main(String args[ ] ){
     // 完全修飾名
          testPackage.A a = new testPackage.A( );
          testPackage.B b = new testPackage.B( );
          a.a1( );
          b.b1( );
      }
}
   

PackageDemo.java

import testPackage.*;

public class PackageDemo{
     public static void main( String args[ ] ) {
     // 完全修飾名
          A a = new A( );
          B b = new B( );
          a.a1( );
          b.b1( );
      }
}

testPackage\B.java

// testPackageパッケージとして定義
package testPackage;

public class B{
  public void b1( ){
              System.out.println("b1");
     }
}
   

C:\java>javac PackageDemo.java

C:\java>java PackageDemo
a1
b1

C:\java>

 

7.クラス

7章 クラス
1.this キーワード
MyPoint.java

class MyPoint{ 
     double m_x; 
     double m_y;

     // コンストラクタ
     public MyPoint( ) {
       System.out.println("コンストラクタ1が呼ばれました");
          m_x = 10;
          m_y = 20;
     }

     // コンストラクタ2
     public MyPoint( double x, double y ) {
       System.out.println("コンストラクタ2が呼ばれました");
          m_x = x;
          m_y = y;
     }

     // 文字列に変換する
     public String toString( ) {
       return "X:"+m_x+" Y"+m_y;
     }

     // 距離を計算する
     public double calcDistance(MyPoint pnt ) {
       // Mathクラスは数学関係を担うクラス
        // Math.pow累乗を計算するメソッド
        // Math.sqrtはルートを計算するメソッド
        return Math.sqrt(Math.pow((m_x – pnt.m_x), 2) + Math.pow((m_y – pnt.m_y), 2));
      }
}

 まず、コンストラクタはインスタンスが作られるときに最初に呼ばれるメソッドである。MyPointクラスでは引数をとるコンストラクタと引数をとらないコンストラクタの2種類が用意されている。メソッドでもそうだが、引数の異なる同じ名前のメソッドを複数用意することをメソッド(コンストラクタ)をオーバーロードするという。
 引数なしのコンストラクタは変数「m_x」と「m_y」にそれぞれ10と20をを代入しているが、これは引数ありのコンストラクタに10,20を渡しても同じ結果が得られる。コンストラクタから同じクラス内の別のコンストラクタを呼び出すこともできる。もっと複雑なクラスになると、コンストラクタを複数用意する場合、ある一部を除いて同じことをする。このため、あるコンストラクタから別のコンストラクタを呼び出すと便利な場合が多い。その方法は以下のように「this」キーワードを用いる。ただし、コンストラクタの一番最初で使用しなければならない。  
MyPoint.java

class MyPoint{ 
     double m_x; 
     double m_y;

     // コンストラクタ
     public MyPoint( ) {
         this( 10,20);
        System.out.println("コンストラクタ1が呼ばれました");
//       m_x = 10;
//       m_y = 20;

     }

     // コンストラクタ2
     public MyPoint( double x, double y ) {
       System.out.println("コンストラクタ2が呼ばれました");
          m_x = x;
          m_y = y;
     }

     // 文字列に変換する
     public String toString( ) {
       return "X:"+m_x+" Y"+m_y;
     }

     // 距離を計算する
     public double calcDistance(MyPoint pnt ) {
       // Mathクラスは数学関係を担うクラス
        // Math.pow累乗を計算するメソッド
        // Math.sqrtはルートを計算するメソッド
        return Math.sqrt(Math.pow((m_x – pnt.m_x), 2) + Math.pow((m_y – pnt.m_y), 2));
      }
}

 calcDistanceメソッドでは、引数にMyPointクラスをもらっている。この場合、同じ名前の変数(m_x, m_y)が頻繁に出てくるため、プログラムを見直した場合、自分自身のインスタンスの変数か、引数でもらってきたインスタンスの変数が紛らわしくなることがある。この場合、thisというキーワードをつけて明示的に区別することができる。したがって、下記のように書き直すこともできる。(変数だけでなく、メソッドにもthisをつけることもできる。)
MyPoint.java

class MyPoint{ 
     double m_x; 
     double m_y;

     // コンストラクタ
     public MyPoint( ) {
         this( 10,20);
        System.out.println("コンストラクタ1が呼ばれました");
//       m_x = 10;
//       m_y = 20;

     }

     // コンストラクタ2
     public MyPoint( double x, double y ) {
       System.out.println("コンストラクタ2が呼ばれました");
          m_x = x;
          m_y = y;
     }

     // 文字列に変換する
     public String toString( ) {
       return "X:"+m_x+" Y"+m_y;
     }

     // 距離を計算する
     public double calcDistance(MyPoint pnt ) {
       // Mathクラスは数学関係を担うクラス
        // Math.pow累乗を計算するメソッド
        // Math.sqrtはルートを計算するメソッド
        return Math.sqrt(Math.pow((this.m_x – pnt.m_x), 2) + Math.pow((
this.m_y – pnt.m_y), 2));
      }
}

 
2.public,protected,private
 IntStackクラスでは、メンバー変数を変えられて誤動作する可能性がある。そこで、この変数をprivateにしたほうがバグの可能性が少なくなる。
IntStack.java

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

 private int m_sp;           //  スタックポインタ:次にプッシュする位置
 private int m_size;        //  スタックサイズ
 private int m_stack[ ];   //  スタック
   
   // コンストラクタ:引数あり
   // IntStack オブジェクトが定義された時に自動的に呼び出される
 private IntStack( int sz ){

          // スタックサイズを保存する
            m_size = sz ;

          // スタックの実体(size個のint配列)自由記憶上に割り当てる
            m_stack = sz ;

          // スタックポインタを初期化する
            m_sp = 0 ;
     }

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

     // プッシュ
     public void push( int value ) {
    if ( m_sp >= m_size ){
        System.err.println("stack overflow");
        System.exit(1) ;               // 異常終了する
      }

         m_stack[m_sp] = value ;
         m_sp = m_sp + 1 ;
      }

     // ポップ
     public int pop( ) {
            // アンダーフローのチェック
    if( m_sp <=0 ) {
           System.err.println("stack overflow");
           System.exit(1) ;               // 異常終了する
          }
         m_sp = m_sp – 1 ;
         return m_stack[m_sp] ;
          }
      // 現在のスタック長を獲得する
      public int getLength( ){
         return m_sp ;
    }

      // 指定位置(スタックトップからのオフセット)のスタック要素を覗き見る
      public int peek( ) {
          // オフセットのチェック
         if ( 0 >= m_sp ){
           System.err.println("stack overflow");
           System.exit(1) ;               // 異常終了する
    }
          // 指定位置の要素をコピーする
         return m_stack[m_sp-1];
    }
}

なお、以前のIntStackクラスでは、メンバー変数に対して、private,protected,publicいずれも指定していなかった。この場合、同じパッケージのクラスからのみ扱える(protectedはそれに加えてサブクラスでも扱える) 
今のところ、パッケージについては後述するが、パッケージを指定しない場合は、同じファイルに複数のクラスを作成した時に、同じファイル内で定義されているクラスのみ扱えるようになる。
修飾子 意味
未指定 同じパッケージ。パッケージ未指定の場合は、同じファイル内に定義したクラス。
public  すべてのクラス。
protected   同じパッケージ。サブクラス。
private  同一クラスのみ
 
3.静的変数・静的メソッド・静的ブロック
静的変数 これまでインスタンスを作ると、それに伴い変数も複数用意された。すなわち、インスタンス毎に変数が用意されたわけである。静的変数というのは、クラスに一つだけの変数で、複数のインスタンスを作っても一つしか作られない変数である。また、インスタンスを作らなくても使用できる変数である。主に定数として使う。  
 静的変数を作るには、変数宣言の前に「Static」とつける。
Test.java

class A{
     
int          a;
     
static int s;
}

class Test{
      public static void main( String args[ ]) {
          // インスタンスを作らなくても、静的変数は使える
         A.s = -10;
         
System.out.println("A.s=" + A.s);

             A a1 = new A( );
             A a2 = new A( );

          System.out.println("a1.a=" + a1.a + "\ta1.s=" + a1.s);

          a1.a = 10;
          a2.a = 20;
          a1.s = 100;
          a2.s = 200;
 

          System.out.println("a1.a=" + a1.a + "\ta1.s=" + a1.s);
          System.out.println("a2.a=" + a2.a + "\ta2.s=" + a2.s);
    }
}
 

C:\java>javac Test.java

C:\java>java Test
A.s=-10
a1.a=0 a1.s=-10
a1.a=10 a1.s=200
a2.a=20 a2.s=200

C:\java>

上記の例では、a2.sに200を代入しているにもかかわらず、a1.sも200になっている。性的変数は、複数のインスタンスを作成しても、全てのインスタンスが同じ変数を共通で使用するためである。 
静的メソッド 
 静的メソッドは、インスタンスに作用するメソッドではなくクラスに作用するメソッドである。つまり、静的変数のみ使用することができ、通常のインスタンス変数を扱うことはできない。また、同一クラスの他の制的メソッドを呼び起こすことはできるが、通常のインスタンスメソッドを呼び起こすことはできない。(通常のインスタンスメソッドから静的メソッドを呼び起こすことはできる。)

 静的メソッドは、クラスに作用するメソッドであるからインスタンスが作られてなくても使用する事ができる。
 静的メソッドを作るには、メソッド宣言の前に「static」をつけるだけである。 
Test.java

class A{
     
int          a;
     
static int s;

      // 静的メソッド
      static void hoge( ){
         s = -10;

              // 静的変数から、インスタンス変数にアクセスできないからコンパイルエラー
//    a = -10;
     }
}

class Test{
       
public static void main( String args[ ]) {
              // インスタンスを作らなくても、静的変数は使える
           System.out.println("A.s=" + A.s);

          A a = new A( );

          a.a = 10;
          a.s = 100;

          System.out.println("a.a=" + a.a + "\ta.s=" + a.s);
    }
}
 

C:\java>javac Test.java

C:\java>java Test
A.s=0
A.s=-10
a.a=10 a.s=100

C:\java>

静的ブロック
 インスタンス変数を初期化するためにコンストラクタがあるように、静的変数を初期化するために静的ブロックというものがある。これは、そのクラスが使用される直前に呼び出される。クラスを作っている段階では、どのようにそのクラスが使われるかわからないので、どのような順番で実行されてもいいように作らなければならない。つまり、自分のクラスの静的変数の初期化のみを行うのが一般的である。

 静的ブロックは「static」の後にブロックを作る。
Test.java

class A{
     
int          a;
     
static int s;

      // 静的ブロック
     
static {
           System.out.println("Aクラスの静的ブロック");
     }
}

      // 静的ブロック
     
static {
           System.out.println("Testクラス静的ブロック");
     }

     public static void main( System args[ ] ) {
          
System.out.println("mainメソッド実行開始");

             A.s = 10;
     }
}

Test.java

class A{
     
int          a;
     
static int s;

      // 静的ブロック
     
static {
           System.out.println("Aクラスの静的ブロック");
     }
}
class Test{
      // 静的ブロック
     
static{
           System.out.println("Testクラスの静的ブロック");

             A.s = 10;
     }

     public static void main(String args[ ]) {
           System.out.println("mainメソッド実行開始");
     }
}

C:\java>javac Test.java

C:\java>java Test
Testクラス静的ブロック
mainメソッド実行開始
Aクラスの静的ブロック

C:\java>

C:\java>javac Test.java

C:\java>java Test
Testクラス静的ブロック
Aクラスの静的ブロック
mainメソッド実行開始

C:\java>

 Aクラスの使用する場所が異なると、実行される順番がかわる。
 
4.ローカル変数
 ローカル変数 メンバー変数(インスタンス変数や静的変数)はクラス定義内で定義している。ローカル変数とはこれまでにも何度か登場しているが、メソッド内などブロック内(中括弧)で定義されている変数である。
class Test{
     public static void main(String args[ ]){
           int i;    // これがローカル変数
  }
}
 
class Test{
     public static void main(String args[ ]){
           int i;
       
       
    // 不確定な変数を使用することはできないのでコンパイルエラー
     System.out.println( i );
  }
}
C:\java>javac Test.java
Test.java:6: Variable i may not have been initialized.
          System.out.println( i );
                                      ^
1 error

C:\java>

class Test{
     public static void main(String args[ ]){
           int i;
       
       
    // 不確定な変数を使用することはできないのでコンパイルエラー
     System.out.println( i );
  }
}
 ローカル変数は、定義されたブロック内でしか使用することができない。変数を使用できる範囲のことをスコープという。Cではブロックの最初でしか変数を定義できなかったが、Javaではブロックのどこでも宣言できる。しかし宣言したブロックを出ると、スコープの範囲外になり、その変数は使用できない。  
class Test{
     public static void main(String args[ ]) {
     System.out.println("mainメソッド");

           // ブロックの途中でも宣言できる
          
int i = 0;
       
           for( ; i < 5 ; i++ ){
                int j = 0;
                // ブロック内であればその中のブロックの中でも使用できる
       System.out.println( i );
     }
           // ローカル変数は[ j ]はスコープを出ているのでコンパイルエラー
     System.out.println( j );
  }
}

C:\java>javac Test.java
Test.java:14: Undefined variable: j
          System.out.println( j );
                                      ^
1 error

C:\java>

 また、特例として、for文の初期設定で変数を宣言することができる。この場合、そのfor文(を含むブロック)がスコープになる。 
class Test{
     public static void main(String args[ ]) {
     System.out.println("mainメソッド");

           // for文の初期設定でもローカル変数を宣言できる
           for ( int i = 0 ; i < 5 ; i++ ){
                // ローカル変数[i]はfor文(ブロック)でのみ有効だから、ここではOK!
       System.out.println( i );
     }
         
// ローカル変数[i]はスコープを出ているのでコンパイルエラー
       System.out.println(i);
     }
}

C:\java>javac Test.java
Test.java:11: Undefined variable: j
          System.out.println( i );
                                      ^
1 error

C:\java>

 ローカル変数は、メンバー変数と同じ名前で使用する事ができる。この場合、ローカル変数のスコープ内ではローカル変数が仕様されスコープを外れるとメンバー変数が使用される。つまり、そのスコープ内では、メンバー変数が隠されることになる。この場合、インスタンス変数の場合は「this」キーワードを、静的変数の場合は「クラス名」を使用することで、隠されているメンバー変数にアクセスできる。
class Test{
      // 静的変数として[m]を定義
     
static int m;

      public static void main(String args[ ]) {
           System.out.println("m=" + m);

             {
                
// 静的変数とし同じ[m]を定義
       
int m = 10;
                    System.out.println("m=" + m);
 
                 // 静的変数の場合は[クラス名.]を使用する
  
          System.out.println("Test.m=" + Test.m);
           }

         System.out.println("m=" + m);
     }
}

class Test{
      // インスタンス変数として[m]を定義
     
int m;

     private void hoge( ){
          System.out.println("m=" + m);
         {
                  m = 1; // this.m =1 と同じ

          // インスタンス変数とし[m]を定義     }
        
int m = 10;
        
      System.out.println("m=" + m);

         // インスタンス変数の場合は[this.]を使用する
      System.out.println("this.m=" + this.m);

             }

     System.out.println("m=" + m);
             }

          private static void main(String args[ ]) {
               new Test().hoge( );
             }
}

C:\java>javac Test.java

C:\java>java Test
m=0
m=10
Test.m=0
m=0

C:\java>

C:\java>javac Test.java

C:\java>java Test
m=0
m=10
this.m=1

m=1

C:\java>

class Test{
     public static void main(String args[ ] ) {
           int a = 0;
           {
               // これはエラー
               int a = 10;
        }
     }
}
 
C:\java>javac Test.java

Test.java:6: Variable 'a' is already defined in this method.
                iint a = 10;
                      ^
1 error

C:\java>

 
5.メソッドの引数
 メソッドへ引数を渡す場合、渡した先のメソッドで値を変えられたらどうなるのでしょう。
class Test{
     public static void main(String args[ ] ) {
           int a = 10;
           increment( i );

        System.out.println( i );
     }
    private static void increment ( int i ) {
        i++;
    
}
}

 
C:\java>javac Test.java

C:\java>java Test
10

C:\>

 
 上の例のように、incrementメソッドに10が代入されている変数[i]を渡して、メソッドの中で値を変えても、呼び出し元に戻ってきてもその値はかわっていません。
 では、渡す引数が配列だった場合はどうだろう。
class Test{
     public static void main(String args[ ]) {
           int array[ ] = {  1,2,3,4,5,6,7,8,9,10 };
        increment(array);

           for( int i = 0 ; i < array.length ; i++ )
       System.out.println( "array[" + i + "]=" + array[i] );
    
}

     private static void increment( int[ ] array) {
           for int i =  0 ; i < array.length ; i++ ) 
         array[i]++;
   }
}

C:\java>javac Test.java

C:\java>java Test
array[0]=2
array[1]=3
array[2]=4
array[3]=5
array[4]=6
array[5]=7
array[6]=8
array[7]=9
array[8]=10
array[9]=11

C:\>

配列の場合は、配列の中身が変わってしまっている。では、もうひとつ引数がクラスのインスタンスだった場合はどうだろう。
class Int{
     Int i;

     // コンストラクタ
    public Int( int n) {
{        i = n;
    }
     // コンストラクタ
     public Int( ) {
         this(0);
  }

     public void increment( ) {
           i++;
  }
     public void decrement( ) {
           i–;
  }
     public int intValue( ) {
    return i;
     }
     public String toString( ) {
    return "" + i;
     }
}

class Test{
     public static void main(String args[ ]) {
         Int i = new Int( );

         System.out.println(i.toString( ));
         increment(i);
        
System.out.println(i.toString( ));
    }

     private static void incremet( Int i ) {
         i.increment( );
    }
}

 
C:\java>javac Test.java

C:\java>java Test
0
1

C:\>

 
 このように、インスタンスの場合も値が変わってしまう。
 参照渡しというのは、インスタンスそのものを渡すのでメソッドの先で値を変えるとそれが呼び出しもとにも反映される。 実はJavaではint型やdouble型などのプリミティブ型ではコピー渡しをする。

 配列にしてもインスタンスにしてもその中身はメモリのどこかに格納されているわけだが、そのアドレスが引数として呼び出し先に渡される。呼び出し先では渡されたアドレスがさしているインスタンスや配列を変えてしまえば呼び出し元に戻ってきてもその変更が影響される。

 

 呼び出し先ではどうなるか。
class Test{
     public static void main(String args[ ]) {
           int array[ ] = {  1,2,3,4,5,6,7,8,9,10 };
        increment(array);

           for( int i = 0 ; i < array.length ; i++ )
       System.out.println( "array[" + i + "]=" + array[i] );
    
}

     private static void increment( int[ ] array) {
         array = new int[10];
                   forint i =  0 ; i < array.length ; i++ ) 
                         array[i] = i+2;
   }
}

C:\java>javac Test.java

C:\java>java Test
array[0]=1
array[1]=2
array[2]=3
array[3]=4
array[4]=5
array[5]=6
array[6]=7
array[7]=8
array[8]=9
array[9]=10

C:\>

class Int{
     int i;

    // コンストラクタ
        public
i;

    // コンストラクタ
        public
Int( ){
           this(0);
       }

     public void increment( ) {
      i++;
   }
     public void decrement( ) {
      i–;
   }
     public int intValue( ) {
      return i;
   }
     public String toString( ) {
      return "" + i;
   }
}

class Test{
     public static void main(String args[ ]){
          Int i = new Int( );

         System.out.println(i.toString( ));
        
increment( );
         System.out.println(i.toString( ));
    }

     private static void increment(Int i ){
         i = new Int(i.intValue() + 1);
    }
}

C:\java>javac Test.java

C:\java>java Test
0

0

C:\>

 このように、呼び出し先でnewしなおした場合は、結果は反映されていない。変数は、本来(配列の実体など)が格納されているアドレスを持っているに過ぎず、引数はそのアドレスのコピーが渡されるからである。つまり、newしなおして、アドレスが変更されても、変更されたアドレスはコピーにしか過ぎず、呼び出し元はnewする前の古いアドレスを持っているからである。

 

 
6.null
 下記のように変数だけ宣言してnewせずに使用するとコンパイルエラーとなる。
class Test{
     public static void main(String args[ ]) {
           String s;
           System.out.println( s );
     }
}
C:\java>javac Test.java

Test.java:4 Variable s may not have been initialized.
           System.out.println( s );

C:\java>

これはインスタンスを入れるための入れ物(変数)であるsには、まだインスタンスが入っていないのに使用しようとしたためである。インスタンスを使うためにはインスタンスをつくらなければならない。
逆に、使い終わったインスタンスは、使い終わったことを示すために、入れ物(変数)にインスタンスがないことを示す値を代入する。これが、「null」である。
class Test{
     public static void main(String args[ ]) {
           String s = new String("abcdefg") ;
           String.out.println(s);

            // 使い終わったことを示す
           s = null ;
     }
}

C:\java>javac Test.java

C:\java>java Test
abcdefg

C:\java>

 このnullは配列にも同じように使用する。
class Test{
      public static void main(String args[ ]){
           int a[ ] = new int [10];

              // 使い終わったことを示す
           a = null ;
       }
}
C:\java>javac Test.java

C:\java>java Test
1
C:\>

 使い終わったnullを代入するというのは、強制ではない。例えば上の例でnullを代入しなくてもエラーはでないしバグもおきない。またプログラムが終了すれば、自動的に使用していたメモリーも解放するたメモリーリーク(メモリにごみが残る現象)もない。
 しかし、javaではガーベージコレクターというメモリーを掃除するきのうがある。これは、常時いらないインスタンスを消去する。使い終わったインスタンスをnullに置き換えないとそのインスタンスが使用中なのかわからないため掃除することができない。したがって、nullを代入することが推奨される。
 また、nullは使い終わったことを示すだけではなく、インスタンスがないことを示すので、使い終わったことを示すだけではなく「インスタンスがない」という事をしめすような使い方をすることもできる。
 例えば、引数にint型のnをもらい、アルファベットのn番目の文字をString型で返すメソッドをつくる。該当する文字がない場合は、nullを返す。
class Test{
      private static String alphabet( int n ){
           String s ;
             if( n < 1 | | n  > 26 ){
                // 該当する文字がない場合はnullを返す
      
       s = null ;
             }
          else{
           char ch[ ] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V'',W','X','Y','Z'} ; 

                 // char配列chのn-1番目から1文字分の文字列を作る
      
          s = new String(ch, n-1, 1);
           }
          return s;
     }

      private static void print(String s){
            if ( s == null )
                 System.err.println("nullです");
            else
                 System.out.println(s);
    
}

      public static void main(String args[ ]){
           int n = 0;
           String s;

           s = alphabet(0);
           print(s);

           s = alphabet(26);
           print(s);
     }
}

C:\java>javac Test.java

C:\java>java Test
nullです
Z

C:\java>1

 
7.インナークラス(内部クラス)
 インナークラスは、あるクラスの中で定義するクラスである。あるクラスの中でのみ使用するデータ群をまとめてしまうのに便利である。 例えば、int型の数字10個からなるデータがあるとする。10個ものデータを別個に管理するのは面倒なので、int型10個の数値を1つのクラスとしてまとめてしまうことにする。このデータをDataクラスとする。しかし、このDataクラスは他のクラスからは参照されないとすると、publicスコープでDataクラスを宣言する必要はないのでインナークラスとして宣言する。もし、複数の人で1つのプログラムを作っていたとする。publicなスコープにDataクラスを宣言すると他の人はDataクラスというクラスを作る事ができなくなってしまう。作ってしまうとバグになってしまう。したがって、できるだけpublicなスコープでクラスなどは作らないようにするべきなのである。
class Test{
     // インナークラス
    
class Data{
          int a1,a2,a3,a4,a5,a6,a7,a8,a9,a10; 
     }

    private static void print(String args[ ]){
            Data data = new Data( );
     }
}

 
8.クラスの包含
 クラスの包含とは、どのようにあるクラスが別のクラスのインスタンスを持つかという関係を表す。
class A_Class{
      B_Class B;

     // コンストラクタ
    
public A_Class( ){
          B = new B_Class( )
     }
}

class A_Class{
      B_Class pB;
}

A_Classのインスタンスが存在する間はB_Classのインスタンスも存在する。逆にB_Classのインスタンスがなくなってしまえば、A_Classは機能しなくなるような場合に用いられる。

このような場合はコンストラクタでインスタンスが作られる場合が多い。

A_Classのインスタンスが存在する間も、B_Classのインスタンスが存在する場合と存在しない場合があるような時に用いられる。

この場合、インスタンスは適当なところで作られる。

例)自動車クラスはエンジンクラスを包含するなど。
(この場合、エンジンがなくなれば自動車ではなくなる)

  
例)自動車クラスは運転手クラスを包含するなど。
(この場合、運転手がいてもいなくても、自動車は自動車であることにかわりはない。)
 

14-4.Vector

 
 

4. Vector 
Vectorは可変長の配列です。配列は一度newするとその大きさを変更することはできない。もしサイズを大きくしたい場合は新しいサイズでnewしなおし、System クラスのarraycopyメソッドを用いてコピーする必要があった。Vectorクラスでは、サイズを気にせず必要な時にサイズを変更できる配列が提供される。 
ただし、Vectorで管理できるのはクラスのインスタンスと配列のみで、プリミティブ型は格納できない。例えばint型の数字を格納したい場合は後述するIntegerクラスのインスタンスにして格納する必要がある。 

import java.util.Vector

メンバー
protected
int
capacityIncrement

VectorのサイズがVectorの容量を超えるときに自動的に増やされる量

protected
 int
elementCount

Vector オブジェクト内の有効な要素数

protected
 Object
elementData

Vectorの要素が格納される配列バッファ

コンストラクタ
Vector() 

空のVectorを作成し、その内部データ配列のサイズが10で、その標準的な増分がゼロであるようにする

Vector(int initialCapacity) 

指定された初期容量、および増加量がゼロである、空のVectorを作成する 

Vector(int initialCapacity,int capacityIncrement) 

指定された容量と増加量で空のVectorを作成する 

メソッド
 
void addElement(Object obj)
      指定の要素をVectorの最後に追加し、サイズを1増やす
int capacity()
      Vectorの新しい容量を返す
Object clone()
      Vectorをコピーします。中身のObjectが複製されるのではなく参照がコピーされるだけ
boolean contains(Object elem)
      指定されたオブジェクトがVectorの要素であるかどうかを判定する。判定はそのObjectクラスのequalsメソッドが利用され、含まれていればtrueが返される。
boolean containsAll(Collection c)
      Vectorが指定の Collection 内のすべての要素を保持する場合に true を返す
void copyInto(Object[] anArray)
      Vectorの要素を、指定された配列にコピーする
Object elementAt(int index)
      指定されたインデックスの要素を返す
Enumeration elements()
      Vectorの要素のリストを返す
boolean equals(Object o)
      指定された Object がVectorと等しいかどうかを比較する。2つのVectorが同じ順序で同じ参照を持っていればtrueが返される。
Object firstElement()
      Vectorの最初の要素(インデックス0の項目)を返す
int indexOf(Object elem)
      equalsメソッドを使って等しいかどうかを判定しながら、指定された引数と同じ内容の要素を先頭から検索します。見つからない場合は-1が返される。
int indexOf(Object elem,int index) 
      equalsメソッドを使って等しいかどうかを判定しながら、指定された引数と同じ内容の要素を指定されたindexから検索する。見つからない場合は-1が返される。
void insertElementAt(Object obj, int index)
      Vectorの指定されたindexに、指定されたオブジェクトを要素として挿入する
boolean isEmpty()
      Vectorが要素を持たなければtrueを返す
Object lastElement()
      Vectorの最後の要素を返す
int lastIndexOf(Object elem)
      Vectorを末尾から検索して、指定されたオブジェクトと同じ内容の要素のインデックスを返し、見つからない場合は-1が返される。
int lastIndexOf(Object elem, int index)
      指定されたインデックスから後向きに指定のオブジェクトを検索し、検出されたインデックスを返す。見つからない場合は-1が返される。
void removeAllElements()
      Vectorからすべての要素を削除し、サイズを0に設定する
boolean removeElement(Object obj)
      最初に検出された(最小インデックスの)引数の要素をVectorから削除する
void removeElementAt(int index)
      指定されたインデックスの要素を削除する
void setElementAt(Object obj, int index)
      Vectorの指定されたindexの要素に、指定されたオブジェクトを設定する
void setSize(int newSize)
      Vectorのサイズを設定する
int size()
      Vectorの要素数を返す
List subList(int fromIndex, int toIndex)
      この List の、fromIndex(これを含む)とtoIndex(これを含まない)の間の部分のビューを返す
String toString()
      各要素のString 表現を保持している、Vectorの文字列表現を返す
void trimToSize()
      Vectorの容量を、現在の容量にまで減らす
   
sample) Systemクラスのarraycopyメソッドと、標準出力、標準エラー出力の例
class Test {
    public static void main(Stirng args[ ]) {
         int i ;
         char c ;
         Character ch;
         Vector vect = new Vector( );

       //文字をVectorに追加していく
       for( c = 0 ; c < 26 ; c++ )
            //プリミティブ型(ここではchar型)はそのままではVectorに入れられない
            //そこで、Charactor型にする必要がある。
            vect.addElement( new Character( ( char ) ( 'A' + c ) ) );

         //配列の中身を表示
         disp(vect);

               //’Q’という文字は何番目に入っている?
           c = 'Q' ;
           i  = vect.index0f(new Character(c) ) ;
           System.out.println( i >=0 ?
                                     c + " は " + i + " 番目に入っています。 ":
                                     c + " はvectの中に入っていません。 ")

               //10番目は何?
           i = 10;
           ch = (Character)vect.elementAt( i );
           System.out.println( i + "番目には " + ch + " が入っています");
     }

       //配列の中身を表示するメソッド
     public static void disp(Vector vect){
          System.out.print("vector ");
          int nSize = vect.size( )

             //配列の中身表示
          for( int i = 0 ; i < nSize ; i++ )
               System.out.print(vect.elementAt( i ) );

             //改行
          System.out.println( ) ;
    }
}

C:\Java\>java Test
vector ABCDEFGHIJKLMNOPQRSTUVWXYZ
Qは16番目に入っています。
10番目にはKが入っています

C:\Java\>