10.Object | |
Objectクラスはすべてのクラスの親クラスである。このクラスで定義されているメソッドはすべてサブクラスであるすべてのクラスで使用できる。逆に言えばこのクラスのメソッドは必要に応じてサブクラスでオーバーライドする必要があるということである。特にequalsメソッド、toStringメソッド、cloneメソッドはオーバーライドすることをおすすめする。 |
|
import java.langObject |
|
コンストラクタ | |
Object() 通常このクラスのインスタンスを作成する必要はない。 |
|
メソッド | |
pretected Object |
clone() 自分自身の複製を作成する。例えばメンバー変数がint型などのプリミティブ型の場合はそれらをコピーするだけでよいが、メンバー変数に他のクラスのインスタンスがある場合、そのインスタンスの参照先をコピーするだけでよいか、それともそのインスタンスの複製をそのクラスのcloneメソッドを呼び出して作成する必要があるかは、クラスによって異なり、これはプログラマに任される。 cloneメソッドを実装した場合はclone可能であることを示すために、Cloneableインターフェースをインプリメントする。逆にCloneableをインプリメントしていないクラスのcloneメソッドを呼ぼうとした場合にはCloneNotSupportedExceptionが投げられる。 |
boolean | equals(Object obj) 引数で与えられたインスタンスと自分自身が同じならばtrueを返す。クラスによって同じという概念が異なる場合がある。例えばメンバー変数にあるクラスが定義されている場合はその参照先が同じ(==演算子を使用する)ならば「同じ」と定義するのか、その中身が同じ(そのクラスの equalsメソッドを呼び出す)ならば「同じ」と定義するのかは、プログラマに任される。 |
String | toString() このクラスの文字列表記を定義します。例えばデバッグ情報などを表示させたい場合には、このクラスのメンバー変数の値をすべて文字列に変換する。 オーバーライドしない場合は、クラス名とそのインスタンスのハッシュコード(16進数)が返されます。ハッシュコードとは、すべてのインスタンスに固有の数字で、実行される度に異なる値になることもある。プラットフォームによって異なりますが、Windowsの場合はそのインスタンスが生成されたメモリアドレスがそのままハッシュコードになる。 |
void | notify() このオブジェクトのモニターで待機中のスレッドを 1 つ再開する。詳しくは スレッド間通信参照 |
void | notifyAll() このオブジェクトのモニターで待機中のスレッドをすべて再開する。詳しくは スレッド間通信参照 |
void | wait() こ他のスレッドがこのオブジェクトの notify() メソッドまたは notifyAll() メソッドを呼び出すまで、現在のスレッドを待機させる。詳しくは スレッド間通信参照 |
void | wait(long timeout) こ他のスレッドがこのオブジェクトの notify() メソッドまたは notifyAll() メソッドを呼び出すか、指定された時間(単位はミリ秒)が経過するまで、現在のスレッドを待機させる。詳しくは スレッド間通信参照 |
Category: Develop
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{ |
|
testPackage\A.java
// testPackageパッケージとして定義 public class A{ |
|
testPackage\B.java
// testPackageパッケージとして定義 public class B{ System.out.println("b1"); } } |
|
C:\java>javac PackageDemo.java C:\java>java PackageDemo a1 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 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{ |
PackageDemo.java
import testPackage.*; public class PackageDemo{ |
testPackage\B.java
// testPackageパッケージとして定義 public class B{ |
|
C:\java>javac PackageDemo.java
C:\java>java PackageDemo C:\java> |
|
7.クラス
7章 クラス | |
1.this キーワード | |
MyPoint.java
class MyPoint{ // コンストラクタ // コンストラクタ2 // 文字列に変換する // 距離を計算する |
|
まず、コンストラクタはインスタンスが作られるときに最初に呼ばれるメソッドである。MyPointクラスでは引数をとるコンストラクタと引数をとらないコンストラクタの2種類が用意されている。メソッドでもそうだが、引数の異なる同じ名前のメソッドを複数用意することをメソッド(コンストラクタ)をオーバーロードするという。 引数なしのコンストラクタは変数「m_x」と「m_y」にそれぞれ10と20をを代入しているが、これは引数ありのコンストラクタに10,20を渡しても同じ結果が得られる。コンストラクタから同じクラス内の別のコンストラクタを呼び出すこともできる。もっと複雑なクラスになると、コンストラクタを複数用意する場合、ある一部を除いて同じことをする。このため、あるコンストラクタから別のコンストラクタを呼び出すと便利な場合が多い。その方法は以下のように「this」キーワードを用いる。ただし、コンストラクタの一番最初で使用しなければならない。 |
|
MyPoint.java
class MyPoint{ // コンストラクタ // コンストラクタ2 // 文字列に変換する // 距離を計算する |
|
calcDistanceメソッドでは、引数にMyPointクラスをもらっている。この場合、同じ名前の変数(m_x, m_y)が頻繁に出てくるため、プログラムを見直した場合、自分自身のインスタンスの変数か、引数でもらってきたインスタンスの変数が紛らわしくなることがある。この場合、thisというキーワードをつけて明示的に区別することができる。したがって、下記のように書き直すこともできる。(変数だけでなく、メソッドにもthisをつけることもできる。) | |
MyPoint.java
class MyPoint{ // コンストラクタ // コンストラクタ2 // 文字列に変換する // 距離を計算する } } |
|
2.public,protected,private | |
IntStackクラスでは、メンバー変数を変えられて誤動作する可能性がある。そこで、この変数をprivateにしたほうがバグの可能性が少なくなる。 | |
IntStack.java
public class IntStack{ private int m_sp; // スタックポインタ:次にプッシュする位置 // スタックサイズを保存する // スタックの実体(size個のint配列)自由記憶上に割り当てる // スタックポインタを初期化する // コンストラクタ:引数なし // プッシュ m_stack[m_sp] = value ; // ポップ // 指定位置(スタックトップからのオフセット)のスタック要素を覗き見る |
|
なお、以前のIntStackクラスでは、メンバー変数に対して、private,protected,publicいずれも指定していなかった。この場合、同じパッケージのクラスからのみ扱える(protectedはそれに加えてサブクラスでも扱える) 今のところ、パッケージについては後述するが、パッケージを指定しない場合は、同じファイルに複数のクラスを作成した時に、同じファイル内で定義されているクラスのみ扱えるようになる。 |
|
修飾子 | 意味 |
未指定 | 同じパッケージ。パッケージ未指定の場合は、同じファイル内に定義したクラス。 |
public | すべてのクラス。 |
protected | 同じパッケージ。サブクラス。 |
private | 同一クラスのみ |
3.静的変数・静的メソッド・静的ブロック | |
静的変数 これまでインスタンスを作ると、それに伴い変数も複数用意された。すなわち、インスタンス毎に変数が用意されたわけである。静的変数というのは、クラスに一つだけの変数で、複数のインスタンスを作っても一つしか作られない変数である。また、インスタンスを作らなくても使用できる変数である。主に定数として使う。 静的変数を作るには、変数宣言の前に「Static」とつける。 |
Test.java
class A{ A a1 = new A( ); System.out.println("a1.a=" + a1.a + "\ta1.s=" + a1.s); a1.a = 10; System.out.println("a1.a=" + a1.a + "\ta1.s=" + a1.s); |
||||||
C:\java>javac Test.java
C:\java>java Test C:\java> |
上記の例では、a2.sに200を代入しているにもかかわらず、a1.sも200になっている。性的変数は、複数のインスタンスを作成しても、全てのインスタンスが同じ変数を共通で使用するためである。 | |
静的メソッド 静的メソッドは、インスタンスに作用するメソッドではなくクラスに作用するメソッドである。つまり、静的変数のみ使用することができ、通常のインスタンス変数を扱うことはできない。また、同一クラスの他の制的メソッドを呼び起こすことはできるが、通常のインスタンスメソッドを呼び起こすことはできない。(通常のインスタンスメソッドから静的メソッドを呼び起こすことはできる。) 静的メソッドは、クラスに作用するメソッドであるからインスタンスが作られてなくても使用する事ができる。 静的メソッドを作るには、メソッド宣言の前に「static」をつけるだけである。 |
|
Test.java
class A{ class Test{ A a = new A( ); a.a = 10; System.out.println("a.a=" + a.a + "\ta.s=" + a.s); |
|
C:\java>javac Test.java
C:\java>java Test C:\java> |
|
静的ブロック インスタンス変数を初期化するためにコンストラクタがあるように、静的変数を初期化するために静的ブロックというものがある。これは、そのクラスが使用される直前に呼び出される。クラスを作っている段階では、どのようにそのクラスが使われるかわからないので、どのような順番で実行されてもいいように作らなければならない。つまり、自分のクラスの静的変数の初期化のみを行うのが一般的である。 静的ブロックは「static」の後にブロックを作る。 |
|
Test.java
class A{ public static void main( System args[ ] ) { A.s = 10; |
Test.java
class A{ |
C:\java>javac Test.java
C:\java>java Test C:\java> |
C:\java>javac Test.java
C:\java>java Test 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); { System.out.println("m=" + m); |
class Test{ // インスタンス変数として[m]を定義 int m; private void hoge( ){ System.out.println("m=" + m); { m = 1; // this.m =1 と同じ // インスタンス変数とし[m]を定義 } System.out.println("m=" + m); // インスタンス変数の場合は[this.]を使用する System.out.println("m=" + m); private static void main(String args[ ]) { |
C:\java>javac Test.java
C:\java>java Test C:\java> |
C:\java>javac Test.java
C:\java>java Test 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. 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 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++ ) |
|
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{ System.out.println(i.toString( )); private static void incremet( Int i ) { |
|
C:\java>javac Test.java
C:\java>java Test 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++ ) private static void increment( int[ ] array) { |
|
C:\java>javac Test.java
C:\java>java Test C:\> |
|
class Int{ int i; // コンストラクタ public i; // コンストラクタ public void increment( ) { System.out.println(i.toString( )); private static void increment(Int i ){ |
|
C:\java>javac Test.java
C:\java>java Test 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. C:\java> |
|
これはインスタンスを入れるための入れ物(変数)であるsには、まだインスタンスが入っていないのに使用しようとしたためである。インスタンスを使うためにはインスタンスをつくらなければならない。 逆に、使い終わったインスタンスは、使い終わったことを示すために、入れ物(変数)にインスタンスがないことを示す値を代入する。これが、「null」である。 |
|
class Test{ public static void main(String args[ ]) { String s = new String("abcdefg") ; String.out.println(s); // 使い終わったことを示す |
|
C:\java>javac Test.java
C:\java>java Test 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 |
|
使い終わった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文字分の文字列を作る private static void print(String s){ public static void main(String args[ ]){ s = alphabet(0); s = alphabet(26); |
|
C:\java>javac Test.java
C:\java>java Test 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[ ]){ |
|
8.クラスの包含 | |
クラスの包含とは、どのようにあるクラスが別のクラスのインスタンスを持つかという関係を表す。 | |
class A_Class{ B_Class B; // コンストラクタ |
class A_Class{ B_Class pB; }
|
A_Classのインスタンスが存在する間はB_Classのインスタンスも存在する。逆にB_Classのインスタンスがなくなってしまえば、A_Classは機能しなくなるような場合に用いられる。
このような場合はコンストラクタでインスタンスが作られる場合が多い。 |
A_Classのインスタンスが存在する間も、B_Classのインスタンスが存在する場合と存在しない場合があるような時に用いられる。
この場合、インスタンスは適当なところで作られる。 |
例)自動車クラスはエンジンクラスを包含するなど。 (この場合、エンジンがなくなれば自動車ではなくなる) |
例)自動車クラスは運転手クラスを包含するなど。 (この場合、運転手がいてもいなくても、自動車は自動車であることにかわりはない。) |