オブジェクト指向プログラミングa 第8回 |
●モジュールの作り方「Javaを学ぶ 第9回 (2003年1月 )」
※この資料は,「Javaを学ぶ 第9回 (2003年1月 )」の内容を簡単にしたもの。「Javaを学ぶ 第9回 (2003年1月 )」
に基づいた資料はこちら。
正常に動作するには,変数(フィールド)を外部から守らなくてはならない。
●「正しく動作するプログラムにおける変数のライフタイム」再掲
(1)まず,変数の設計図である「データ型」がある。「データ型」は「そのデータが何を表現しているか,何バイト使うか」
といった情報を持っている。たとえば,int型は「データは整数を表現していて,4バイト使う」という情報を持っている。
この「データ型」を元に,メモリ上に数バイト使用して変数が誕生する。なお,変数(variable)はインスタンス(instance)
とも言われる。インスタンスは「実例」とか「実体」という意味で,「データ型のひとつひとつの実例」というニュアンス
である。「住宅の一生」に例えれば,家の設計図を基に,メモリという敷地の一部をいくらか使って,その上に家を建てる
段階に当たる。
(2)変数(インスタンス)の誕生した直後は,生成される毎に中に入っている値が異なる。そのため,変数を使用する前に,適切
な初期値で変数を初期化しないとプログラムの正しい動作が期待できない。「住宅の一生」に例えれば,住宅のライフライ
ン(電気・水道・下水・ガス等々)をひいたり,家具を揃えたりカーペットや畳を敷いたりして住めるように仕上げる段階に
相当する。
(3)変数が初期化されたら,いよいよその変数を使う(変数に値を代入したり,変数に入ってる値を使ったり)段階に入る。この
とき,不正な操作で変数の値が勝手にいじられると,プログラムが正しく動作しなくなってしまう。このようなことを防ぐ
には,変数へのアクセスできるものを「正しい操作」だけに限定して,それ以外の「不正な操作・不正かもしれない操作」
からアクセスされるのを防がなくてはならない。「住宅の一生」に例えれば,住宅の中のものを勝手にいじられないように
セキュリティをかため,家族のメンバーだけが入れるようにし,家族以外の部外者の侵入を防ぐようにする,ということで
ある。
(4)変数が役目を終えるのは,その中に入っている値を最後に参照したときである。「住宅の一生」に例えれば,家の中を最後
に利用したときとなる。
(5)役目が終わり,もう使用されることがない変数は,消滅してもらわなくてはならない。なぜなら,使用されることがない変数
がそのまま居残り続けると,メモリという敷地の一部を占領しつづけることになる。このような「居残り変数」が多くなると
他の目的のために使用できるメモリ領域(空きメモリ)が不足してしまう。このように不必要な変数がメモリ上に居残り続けて,
空きメモリが不足する現象を「メモリリーク(memory leak,メモリ漏れ)」と言う。メモリリークはプログラムの動作不良
の代表的な原因である。メモリリークを防ぐために,必要の無くなった変数には消滅してもらい,その変数が占拠していた
メモリ領域を明け渡すようにする必要がある。Javaでは,不必要になった変数(いわば「ゴミ」)は,ガベージコレクション
(gabage collection,ゴミ集め)という仕組みによって自動的に調べられ,自動的に消滅するようになっている。「住宅の一生」
に例えれば,要らなくなった住宅を壊さずにそのまま放置すると土地を無駄使いして土地不足になるので,不必要な住宅を
壊して敷地を更地に直し,土地を他の用途に使えるようにすることに当たる。
※今回は特に,上図(3)の「適切な操作による値の変更」について解説する。フィールドは,クラスの外
部から勝手にいじられると,プログラムが誤動作することが多い。そこで,クラスの中の特定のメンバを,
クラスの外部(=他のクラス)から隠すことが出来るようになっている。
・こうして,「隠されたメンバ」を,無理矢理,クラスの外部(=他のクラス)から利用しようとすると,
コンパイル時にエラーになる。
・もちろん,「隠されたメンバ」は,そのクラスのメソッドからは自由に利用できる。
このように,クラスに公開された部分(公開部)と,隠された部分(非公開部)が出来ると,クラスは再利用
可能な部品(モジュール)としての性質を持ってくる。Javaでは,クラスはプログラムを構成する最少のモ
ジュールと言える。今回は,複数の関連素rくらすを集めた,より高レベルのモジュールである「パッケー
ジ」についても解説する。当然ながら,パッケージもモジュールであるからには,公開部と非公開部がある。
【モジュール】
|
|
|
【パッケージとアクセス指定】
●クラスのアクセス指定
(ex.1) class A {} // アクセス指定無しクラス
(ex.2) public class A {} // publicクラス
指定無しクラス | publicクラス | |
同一パッケージ内のクラスから | ○ | ○ |
別のパッケージ内のクラスから | × | ○ |
privateメンバ | 指定無し(デフォルトアクセス)メンバ | publicメンバ | |
同一クラス内から | ○ | ○ | ○ |
同一パッケージ内のクラスから | × | ○ | ○ |
別のパッケージ内のクラスから | × | × | ○ |
手順2)Person.java(package宣言無しバージョン)とMeibo.java (package宣言無しバージョン) を,
上図のjinjiディレクトリに保存し,下図のように
・それぞれ1行目にpackage宣言を書き加え
・Personクラス自体とのPersonのコンストラクタをpublicに指定
・Meiboのコンストラクタと,put()メソッド,printAll()メソッドをpublicに指定して
保存し直せ。
今,状況は次のようになっている。
手順3)先ほどのMeiboTest01.java (import宣言無しバージョン) に,下図(1)のように1行目にimport宣言を付けて,
保存する。保存場所はどこでもいいが, Z:¥ooprog など,いつもjavaのプログラムを作成しているディレク
トリでよだろう。
次に,このMeiboTest01.javaをコンパイルするわけだが,このプログラムはimport宣言にあるように,
com.tuisjava.jinji パッケージのMeiboクラスを利用する。
前述したように,パッケージ化したクラスを利用するには,コンパイル時とプログラム実行時に,
・パッケージの起点となるディレクトリ(Fig.10の例ではmyclassesディレクトリ)の場所
を,javacコマンドや,javaコマンドに教えておく必要がある。(ちなみに,コンパイルやプログラムの実行時
に必要になるクラスの場所を示すファイルパス(file path name)のことを,クラスパス(class path)と呼ぶ。ここ
で指定するパッケージの起点となるディレクトリの場所もクラスパスの一種である)
パッケージの起点となるディレクトリの場所をjavac/javaコマンドに教える方法には,次の2つがある。
i) コンパイル時やプログラム実行時に,そのつどパッケージの起点となるディレクトリの場所を指定する
ii) 環境変数CLASSPATHに,パッケージの起点となるディレクトリの場所を追加する
ここでは,i)の方法を紹介する。(ii)の方法についてはこちらを参照せよ)
コンパイルは,次のように行う。
(1) コマンドプロンプトを表示して,MeiboTest01.java を保存したディレクトリにcdコマンドで移動。
例:もし,MeiboTest01.java を Z:¥ooprog に保存しているなら,
> cd Z:¥ooprog
とする。
(2) 次のように,パッケージの起点となるディレクトリの場所を指定してMeiboTest01.java をコンパイルする。
> javac -cp "Z:\MyDocuments\myclasses" MeiboTest01.java
※-cpは,javacコマンドのクラスパスを指定するためのオプションである。javacコマンドのオプションについては
> javac -help
とすれば概要が表示される。
コンパイルがうまくいかない場合は,
・ファイル配置が,手順2のFig.10のようになっているか(ディレクトリ名の間違いなどにも注意)
・Meibo.javaに,正しくpackage宣言が書かれているか
・MeiboTest01.javaに,正しくimport宣言が書かれているか
を確認せよ。
コンパイルがうまくいくと,Meibo.classとPerson.classが jinji ディレクトリの中に出来ているはずであるので,
確認せよ(下図)。
ここで起こったことを順序立てて書くと次のようになる。
1)javacコマンドがMeiboTest01.javaをコンパイルするとき,必要になるMeiboクラスが見つか
らないので,-cpオプションで指定されたファイルパスによりパッケージ階層の起点であるディ
レクトリ(この場合,Z:\MyDocuments\myclasses)を割り出す。
2)次いで,パッケージ階層の起点ディレクトリ(myclasses)とimport宣言の内容から
Z:\MyDocuments\myclasses\com\tuisjava\jinji
ディレクトリにクラスファイルMeibo.classがあるのではないかと推測する。
3)しかし,そのjinjiディレクトリ内には,コンパイル済みのMeibo.classはまだ無い。そこで
javaコンパイラは,Meiboクラスを定義しているはずのソースファイルMeibo.javaを探す。そ
うして,jinjiディレクトリ内にMeibo.javaを見つけたjavaコンパイラは,Meibo.javaを
コンパイルしてjinjiディレクトリの中に,Meibo.classとPerson.classを生成したのである
(上図の矢印)。Personクラスについても同様である。
手順6)手順の5でコンパイルしたMeiboTest01.classを実行してみよう。コマンドプロンプトの現在位置
(カレントディレクトリ)にMeiboTest01.classがあることを確認せよ。実行コマンドは以下のよう
になる。クラスパスに,MeiboTest01.classの在処であるカレントディレクトリ(.)を追加している
ことに注意。なお,カレントパスの区切り文字はWindowsではセミコロン(;),Unixではコロン(:)が
使われる。
> java -cp "Z:\MyDocuments\myclasses;." MeiboTest01
※-cpは,javaコマンドのクラスパスを指定するためのオプションである。javaコマンドのオプションについては
> java -help
とすれば概要が表示される。