JPIN#7
復習
安易にstaticを使うと変数が共有されてしまうので他のクラスとの扱いが難しくなる
並列処理(マルチスレッド)をするときに共有してしまうと挙動がおかしくなるのでなるべく使いたくない
その分staticは定数で使うことが多い
定数はfinal修飾子を使うと変数が定数となる
public final static int NUM=10;(定数は全て大文字にしがち)
とするとstatic領域にint型の定数NUM(10)となる
パッケージ
クラスを分類整理したいときに用いるのがパッケージである
また、名前空間の付与の特性がある
名前空間の付与とはクラスの数が増えてくるとクラスの名前が被ってしまうことがある
もちろんそれは実行されない
昔は番号管理をしていたがわかりにくい
そこでクラス名=名、パッケージ名=姓とすることで見分けをつけれるようにした
これが名前空間の付与という
また、パッケージ名+クラス名のことを完全修飾クラス名(FQCN)という
javaではこれでしかクラスを扱うことができない
なのでパッケージが必ず必要になる
デフォルトパッケージ
何も宣言されていないときはデフォルトパッケージに分類される
これは名前のないパッケージであり、学習用パッケージという
このデフォルトパッケージの内部から外部のパッケージのクラスへのアクセスはできるが、他のパッケージからデフォルトパッケージにはアクセスができない
なのでデフォルトパッケージは使われるものではなく、使う方のパッケージとなる
パッケージの名称について
ドメイン名をパッケージの名前にして管理することが多い
sample.co.jp→jp.cp.sample.プロジェクト名.機能.クラス名
とすることが多い
最後にファイルの整理の目的がある
一つのフォルダの中にいろいろなフォルダがあるとわかりにくいし探しにくいのでパッケージでファイルを分類する
また、以下のルールがある
・javaは完全修飾クラス名でしか扱えない
・クラス名とファイル名を一致させる
つまりファイル名はFQCNで書かないといけないことになるがそれでは見づらい
「jp.cp.sample.プロジェクト名.機能.クラス名」はディレクトリ構造にすれば、クラス名は機能フォルダの中にあればいい
こうすることで整理しやすく、検索もしやすくなる
ちなみに「packA.packB」みたいな書き方をするが、「packA」と「packA.packB」は何も関係ない
名称に同じ単語が入っているだけで全く別のパッケージである
クラスパス(class path)
環境変数pathとはOSに対してプログラムファイルがある場所を教える役割がある
もしこれがなければOSは端から端まで探してしまうのでとても時間がかかる
JVMが探す場所を教えるのがclass pathである
指定がなければカレントディレクトリから探してしまう
情報隠蔽
パッケージ+アクセス修飾子で情報隠蔽ができる
今までpublic(全公開)とprivate(非公開)で隠蔽してきた
それに加えてprotectedと何もないもの(default)がある
この二つをパッケージアクセスといい同じパッケージの中で公開するのがprotectedと何もないものである
アクセス修飾子「なし」が標準とされている
これを使って公開する範囲を狭めることで隠蔽することができる
要は使って欲しくないものを隠すことができる
全てのクラス、メソッドにpublicをつける必要はない
コードを書こう
まずパッケージcom.sampleにAクラスを制作する
package com.sample; public class A { public A() { System.out.println("A"); } }
次に他のパッケージcom.testを用意する
package com.test; public class Main { public static void main(String[] args) { }
MainクラスでAクラスを使いたいときは二通りの表現がある
まずはAクラスの完全修飾クラス名を唱える方法である
package com.test; public class Main { public static void main(String[] args) { com.sample.A a = new com.sample.A(); }
これでAクラスは使用できる
次にimportを使う方法である
package com.test; import com.sample.A; public class Main { public static void main(String[] args) { A a = new A();//importした場合(完全修飾クラス名を省略しただけ) }
importで使用したいAクラスのパッケージを指定するとこのようになる
ただこれは完全修飾クラス名を省略しているだけに過ぎない
また、パッケージcom.sample.testにAクラスを作り、それを使いたい場合は完全修飾クラス名で宣言しなければならない
なぜならimportで両方のAクラスが使えるようにしてもAと称えるだけではどっちのパッケージのAクラスか分からないのである
package com.test; import com.sample.A; public class Main { public static void main(String[] args) { A a = new A();//importした場合(完全修飾クラス名を省略しただけ) com.sample.test.A aa = new com.sample.test.A();//同じクラス名なのでこう書く必要がある } }
継承
差分プログラミング
ある似たようなプログラムがある
そのプログラムには共通の処理が入っている
この場合共通部分を取り出して各プログラムからそれを使うことで処理が減るし、修正も楽になる
これを差分プログラミングという
共通をスーパークラス、差分をサブクラスという
javaではextendsをクラス名の後に入れることでスーパークラスを使用することができる
public class B extends A(){ }
ここでAはスーパークラスであり、Bはサブクラスである
これを宣言するとヒープ領域にはAクラスをBクラス両方のコピーがそれぞれ作られる
そしてBはAのメソッドと変数を使用することができるということになる
しかし、以下のものは継承されない
・private
・コンストラクタ
また、今まではthisでそのクラスの変数を指定していたわけだが、superをつけるとスーパークラスの変数を使えるようになる
return super.num;
のように使う
public class B extends A{ public B(){ super();//この部分はextendsで自動生成されている、この部分は通常見えない System.outprintln("B"); } }
extendsをするとsuper();が自動で生成されているのでスーパークラスのコンストラクタが自動的に使われることになる
継承のデメリット
共通部分を出すことによって共通部分を変えることで全ての差分プログラミングが変わってしまうことである
特に膨大な差分プログラミングに影響があれば非常に面倒なことになる
継承の種類
実装継承・・・コードを引き継ぐ
意味継承・・・ポリフォーフィズム(今度出てくるらしい)
実装継承のメリットは開発生産性の向上ができる
コードを多く書く必要はない
しかしスーパークラスは固定化されるので必ず変更がないものしかスーパークラスにできない
コードを書いてみよう
まずはAクラス
public class A { public A() { System.out.println("A"); } }
続いてBクラス
public class B extends A{//Aというクラスを拡張したBクラスを公開 public B() { System.out.println("B"); } }
最後にMainクラス
public class Main { public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ B b = new B(); } }
このようにクラスを作成した場合、出力はどうなるのか・・・
答えは
A
B
となる
これはAクラスを継承したBクラスが宣言された時点でAクラスとBクラスがヒープ領域に生成されており、Bクラスでは最初にsuperが唱えられているのだ(コードでは見えない)
その結果Aのコンストラクタが動いた後にBのコンストラクタが作用する流れになる
オーバーライド(override)
オーバーライドはメソッドの再定義を意味する
サブクラスでスーパークラスのメソッドや変数を変えたいときに使う
サブクラスでスーパークラスと同じ名前のメソッドを宣言して内容を書き換えたい内容にするとサブクラスで作られたが優先的に使われる
もしスーパークラスのメソッドを使いたいときはsuperを付ければOK
継承は何回もできるわけだが最後にオーバーライドしたものが優先される
あくまで書き換えているわけではなくてそれぞれでメソッドを作られていて、最後のサブクラスが優先されるという認識でいいと思う
継承とオーバーライドを使用するのはいかにエラーや修正をしていくときに特定しやすく、修正の手間がなくなるようにするものである
コードを書こう
まずはAクラス
public class A { public void hello() { System.out.println("hello"); } }
続いてBクラス
public class B extends A{//Aというクラスを拡張したBクラスを公開 @Override public void hello() { System.out.println("hi"); } public void bye() { System.out.println("byebye"); } }
最後にMainクラス
public class Main { public static void main(String[] args) { B b = new B(); b.hello(); b.bye(); } }
ここではBクラスでのhelloメソッドが唱えられている
なのでMainではBクラスのhelloが優先されて実行される
もしAクラスのhelloメソッドを実行したい場合は、superを付ければ良い
最後に・・・
どんな設計でメリットとデメリットがある
メリットデメリットを天秤にかけて設計することが重要である
使用するときは必ずデメリットも把握しておくこと!