インスタンス

クラスは設計図、設計図を元に実体を作って使います。そこで、クラスを元に実体を作る作業をインスタンス化と呼び、出来上がった実体がインスタンスです。」と多くの教材は説明します。

ではなぜ、そうしなければいけないのでしょう
そうすると何が有難いのでしょう。

設計図を元に物を作ると、出来上がるのは全て同じものです。
設計図は寸分違わない物を作るための指示書です。
この様なプログラムの再利用も必要でしょう。関数やサブルーチンなどは何度呼んでも同じ動きをします。ですから、設計図のようなプログラムの再利用はオブジェクト指向プログラミング以前のプログラミング言語でも可能でした

ソフトウェアの利用現場を考えてみましょう。
以前のプログラム言語ではできなかったプログラムの再利用とは、元のプログラムを利用して少しずつ違ったプログラムを瞬時に作ることです。関数やサブルーチンなど以前からの仕組みで出来なかったことは、まさにこの事です。「クラスは設計図」という説明では説明が付きません。

このコースではclassは同じ種類の集合を分類したものと説明しました。つまり、概念、コンセプトです。概念から物を作ると、作るたびに概念の解釈が少しづつ変わり、似てはいても違う物が出来るはずです。
つまり、classを元に実体を作るときも、似てはいても少し違う実体を作りたいのです。この仕組みがインスタンス化です。

例えば、クラスの説明ページで示した、チケット販売システムを思い出してください。
A席チケット販売システムも、B 席チケット販売システム、S席チケット販売システムだって基本的にはほとんど変わりません。チケットの価格と販売される席の数が違うだけでしょう。つまり、属性は多少違っても、振舞いはほぼ同じです。

これを関数で作ろうとすれば、ほとんど同じ関数を三つ作ることになり、何かを修正しようとすれば三つの関数それぞれに同じ修正を加えなければいけません。これがclassとinstanceならばclassを直すだけで全てのinstanceが同じように変わります。ましてclassを再コンパイルするだけで再リンクは不要ですから、ソフトウェアを使用しながら機能の変更が可能です。(テストは事前に十分行わなければいけませんが)

以上が、面倒な手間に見えるインスタンス化の有難さです。

次は、このインスタンス化を行うためのコンストラクタです。

書式

修飾子 コンストラクタ名 (引数リスト ) {
   処理
}

三つの条件

コンストラクタ名はクラス名と同一
戻り値型は記述しない
newと一緒にしか使えない(インスタンス生成時以外は呼び出しができない)

※クラス名がメソッド名と同じでも戻り値型を記述している場合は、通常のメソッドとして解釈されます。
※インスタンス生成時の記述で引数リストが対応していないと、コンパイルエラーが発生します。

独自にコンストラクタを定義すると、デフォルトコンストラクタは作られません。にも拘わらす、引数なしのコンストラクタ(デフォルトコンストラクタと同一)を呼ぶとコンパイルエラーになります。

多少違ったインスタンスを作るために、コンストラクタのオーバーロードを使います。また、共通部分の記述を簡略化するためオーバーロードしたコンストラクタから、ほかのコンストラクタを呼び出すためにthis(引数リスト);が使えます。なお、スーパークラスのコンストラクタを呼び出す場合にはsuper(引数リスト);を使います。

クラスが継承関係にある場合、スーパークラス分のインスタンスから生成しなければいけません。そこで、サブクラスのコンストラクタの先頭でスーパークラスのコンストラクタを呼出す必要があります。もし、プログラマーが明示的にスーパークラスのコンストラクタを呼出さないと、コンパイラは引数なしのスーパークラスのコンストラクタを呼出すコードを自動的に追加します。もし、スーパークラスに(独自のコンストラクタを記述したため)引数なしのコンストラクタが存在しないとコンパイルエラーが発生します。
※何かの処理の後でスーパークラスのコンストラクタを呼出すとコンパイルエラーになります。

また、サブクラスでコンストラクタがオーバーロードされていて、明示的にスーパークラスのコンストラクタを呼び出した後にthis();を使ってオーバーロードした別のコンストラクタを呼出すと、別のコンストラクタ側でもスーパークラスのコンストラクタを呼ぶので、スーパークラスのコンストラクタが2回呼出されることになります。このような事態を避けるため、スーパークラスのコンストラクタを呼び出した後にthis();を記述するとコンパイルエラーが発生します。
※スーパークラスのコンストラクタをインスタンス化の間に二回以上呼ぶとコンパイルエラーになります。

アクセス修飾子
コンストラクタには全てのアクセス修飾子が指定できます

public
どのクラスからでも対象のクラスをインスタンス化できる。
protected
継承関係にあるサブクラスや同一パッケージ内のクラスだけが対象のクラスをインスタンス化できる。
package default
同一パッケージ内のクラスだけが対象のクラスをインスタンス化できる。
private
非公開のコンストラクタを定義します。
非公開のコンストラクタの用途は、あるアプリケーション内でインスタンスが1つしかないことを保証したり、コンストラクタをオーバーロードして複数定義し、公開するコンストラクタと非公開にするコンストラクタに分ける(公開コンストラクタの中で非公開コンストラクタを呼ぶ)ためにも使います。

クラスに戻る

クラス及びインスタンスの初期化

似ているけれども、同一ではない個々のインスタンスを作りだすのが初期化処理です。
初期化はコンストラクタで行うのが一般的ですが、初期化はそれだけではありません

段階的に行われる初期化

1.
クラスロード時にスタティックイニシャライザが実行され、クラス変数(static変数)の初期化が行われる。
2.
クラスのフィールドで宣言した変数を既定値で初期化(初期値を代入)する。
3.
オブジェクトの生成時にインスタンスイニシャライザが実行される。
4.
次にコンストラクタが実行され、引数などによる初期化処理が行われる。

スタティックイニシャライザ
クラスのロード時、クラス変数を初期化するために使用します。
クラス変数はインスタンス化しなくても存在し、使える変数ですから初期化が必要です。通常は初期値の代入で行われますが大量の要素を初期化しなければいけないような場合には有効です。
スタティックイニシャライザはクラスのどこにでも、いくつでも記述できますが、ロード時に実行されるのでクラスの先頭から順方向で参照が行われることに注意して、適切な位置に記述する必要があります。

書式

static{
  初期化処理
}

参考例

public class StaticInitializeSample {
	public static void main(String[] args) {

		System.out.print("static arrayの値は[");
		for(int num: StaticInitialize.array) {
			System.out.print( num );
		}
		System.out.println( "]です。");
	}
}

class StaticInitialize {
	static int[] array = new int[10];
	
	//スタティックイニシャライザ
	static{
		for (int i=0; i<array.length; i++) {
			array[i] = i;
		}
	}
}

実行結果

>java StaticInitializeSample
static arrayの値は[0123456789]です。

>

なお、JVMがスタティックイニシャライザを処理中に何らかのトラブルが発生(例えばNullPointerException)した場合、そのことを通知するクラスが存在しないため、JVMはExceptionlnlnitializerErrorを発生させ、プログラムを強制終了させます。

インスタンスイニシャライザ(初期化ブロック)
インスタンス生成時にインスタンス変数(インスタンスごとに作成される変数)を初期化します。インスタンス変数は、通常コンストラクタで初期化しますが、オバーロードした複数のコンストラクタで共通する処理がある時にはインスタンスイニシャライザで一括して初期化すればプログラムの可読性が向上します。
なお、インスタンスイニシャライザはコンストラクタより先に実行されます。

書式

{ }に囲まれたブロックで、クラスブロック直下にフィールドやメソッド、コンストラクタと
同列の扱いで記述します。

参考例

public class InstanceInitializeSample {
	public static void main(String[] args) {

		StaticInitialize si1 = new StaticInitialize();
		StaticInitialize si2 = new StaticInitialize("引数一つの");

		System.out.println("インスタンスsi1は" + si1.constracterType + si1.postFix);
		System.out.println("インスタンスsi2は" + si2.constracterType + si2.postFix);
	}
}

class StaticInitialize {
	String postFix;
	String constracterType;
	
	//インスタンスイニシャライザ
	{
		postFix = "コンストラクタを使いました。";
	}
		
	StaticInitialize() {
		this.constracterType = "デフォルト";
	}

	StaticInitialize(String type) {
		this.constracterType = type;
	}
}

実行結果

>java InstanceInitializeSample
インスタンスsi1はデフォルトコンストラクタを使いました。
インスタンスsi2は引数一つのコンストラクタを使いました。

>

クラスに戻る