JPIN#6.5 パッケージと継承
パッケージ
パッケージとはJavaのクラスをまとめて分類するための仕組みである
クラス数が大量にある場合、それらを機能などで分類してまとめておくとクラスの管理が容易になる
クラスの先頭で「package」と宣言することで、そのクラスが分類されるパッケージを指定することができる
パッケージの宣言・使い方
クラスの先頭で「package」と宣言することで、そのクラスが分類されるパッケージを指定することができる
package pack; public class A{ }
パッケージが未指定だと全てデフォルトパッケージに分類される
パッケージを指定した場合、そのクラスのソースファイルは指定パッケージと同じ名前のフォルダに格納する必要がある
仮にファイルを移動した場合は、ソース内のパッケージ宣言も合わせて変更する必要がある
同一パッケージ内のクラスにはクラス名のみでアクセスが可能である
しかし、パッケージ外のクラスにアクセスする場合はインポート文によって対象のクラスの場所を明示する必要がある
今までの講義ではパッケージは指定していなかったのですべてデフォルトパッケージに移動していた
ではインポート文とはどうやって宣言するのか・・・
import pack.A; public class B{ }
と宣言する
これでパッケージpackにあるAクラスを使用することができる
複数のパッケージから成るものは
package packA.packB.packC.C;
と宣言することができる
ちなみにこのpackBはpackAのサブパッケージという
コードに書いてみよう
では実際にこのコードを書いてみよう
まずはパッケージAにAクラスを作る
package packA; public class A { public static void print() { System.out.println("packA"); } public void print2() { System.out.println("instance"); } }
次にpackAのサブパッケージにpackBを作る
package packA.packB; public class B { public static void print() { System.out.println("packA.packB"); } }
次にpackBのサブパッケージpackCを作る
package packA.packB.packC; public class C { public void print() { System.out.println("packA.packB.packC"); } }
最後にこれらのクラスを使用するmainクラスを作る
package main; import packA.A; import packA.packB.B; import packA.packB.packC.C; public class Main { public static void main(String[] args) { A.print(); A a = new A(); a.print2(); B.print(); C c = new C(); c.print(); } }
では順番にメモリの動きを見てみよう
まず最初にパーマネント領域にこれらのクラスが移されて、static修飾子がついているものがstatic領域にコピーされる
A ,Bの各printメソッドとMainクラスのmainメソッドがコピーされた
この時各パッケージ毎に異なるstatic領域にそれぞれコピーされるものとする
ここからスタック領域にmainパッケージのmainメソッドが読み込まれていく
このmainメソッドがあるmainクラスはpackAにあるAとpackA.packBにあるBとpackA.packB.packCにあるCにアクセスすることができる
A.print(); A a = new A(); a.print2();
まずA.print()でstatic領域にあるAクラスのprintメソッドを実行する
次にAクラスのコピーをヒープ領域に作り、そのアドレスをaに記憶させる
この時にコッピーされるのはダイナミック修飾子であるprint2メソッドのみコピーされる
次の行でaに書いてあるprint2メソッドを実行している
Bはstatic領域のみなので.print()でprintメソッドを実行
Cにはstatic領域がないので
C c = new C(); c.print();
でヒープ領域にコピーしてprintメソッドを使う
このようにパッケージ毎にクラスを分けたりアクセスしたりすることができる
継承
継承によって、何か新しく機能を作る際に前に作った機能と同じ機能がある場合に、共通部分は一つにまとめて繰り返し利用できるようになる
つまり、何度も同じようなプログラムを書く必要がなく、作業の効率化、ソースコード全体を見やすくさせることができる
継承をすることで読み込むソースコードの量を減らすこともできるし、メモリの無駄遣いも減るだろう
継承の書き方
javaでは継承したいクラスがあった場合、以下のように書く
public class A extends B{ hoge(){} }
これでAはBを継承したとなる
Bクラスが以下のものとしよう
public class B{ int num; String s; public method(){} }
すると継承したAクラスはnumもsもmethodメソッドも使用することができる
もちろんにAクラスにあるhogeメソッドもそのまま使える
つまりAクラスはBクラスの一部と捉えることができるので
B b = new A();
と宣言することができる
継承したAクラスはB型の変数にアドレスを格納することができる
コードを書いてみよう
まずは継承元のStudentクラスを用意する
public class Student{ public static String name; public int id; public static void test(String subject) { name=subject; } }
次に継承先のSchoolクラスを用意する
public class School extends Student { public static String subject2="A"; public void W() { System.out.println(id); } public static void main(String[] args){ Student s1 = new School(); s1.id = 100; System.out.println(s1.id); test(subject2); System.out.println(name); School s2 = new School(); s2.W(); } }
まず最初にstatic修飾子のものがstatic領域にコピーされる
その中にmainメソッドがあるのでmainメソッドをスタック領域で起動する
まず最初にStudent型のShoolクラスのコピーをヒープ領域に作ってs1に記憶させる
次にそのクラスの変数idに100を代入して出力している
次にtestメソッドを起動している
これは継承でStudentクラスにあるメソッドを使えるのでコピーなどをする必要はなくこのように使える
ここではsubject2("A")を引数として渡している
testメソッドではsubject2をnameに代入している
そしてそれを出力する
最後にSchool型のSchoolクラスのコピーをヒープ領域に作り、s2に記憶させる
このクラスにあるWメソッドを起動する
ここでこのクラスのidは0なので0が出力される
これで終了する
気づいた人もいるかもしれないが、変数もメソッドも継承することはできるがmainメソッドは基本的にはstatic領域にあるため、直接継承されたものを使いたい場合は継承するものも事前にstatic領域に入れておく必要がある
そうでない場合はクラスのコピーを作って使用するしかない
もちろん継承先でダイナミックメソッド内では継承元がダイナミック修飾子のものを使うことができる