WisdomSoft - for your serial experiences.

4.7 静的なメンバ

インスタンスとは関係なく、実行単位に対して 1 つしか存在しないフィールドやメソッドを作成できます。

4.7.1 メンバを共有する

クラスのメンバはインスタンスごとにコピーされます。そのため、異なるインスタンス間のフィールドに関連はなく、それぞれが独自のメモリ領域を確保しています。しかし、フィールドにアクセスしないメソッドや、すべてのインスタンスで特定の値を共有したい時は、逆に障害となってしまうでしょう。メソッド内でフィールドにアクセスする必要がなければ、メソッドはオブジェクトがなくても動作します。にもかかわらずオブジェクトが必要だというのは横暴なプログラムです。

そこで、メンバをインスタンスごとに持たせるのではなく、クラスに 1 つだけ保有するように指示することができます。そうすることによって、そのクラスのすべてのインスタンスは 1 つのメンバを共有するようになります。このように、実体が 1 つしか存在しないメンバを静的なメンバと表現します。このようなメンバは、インスタンスが存在しなくても機能します。

静的なメンバは static キーワードを用いて宣言します。フィールド宣言やメソッド宣言で、型の前に static を指定することで、そのメンバがクラスに対して 1 つしか存在しないことをアピールします。

static フィールド
static  フィールド名;
static メソッド
static  メソッド名 (仮パラメータリスト) { 本文 }

static のような、フィールドやメソッドの性質を決定するためのキーワード群を修飾子と呼びます。フィールド宣言やメソッド宣言では、型の前に修飾子を指定すると定義されています。修飾子は省略可能であり、複数の修飾子を指定することも可能です。static 以外の修飾子は後述します。

修飾子を持つフィールド宣言
フィールド修飾子  フィールド名 = 初期化子 , ...;
修飾子を持つメソッド宣言
メソッド修飾子  メソッド名 (仮パラメータリスト) { 本文 }

これが、フィールド宣言とメソッド宣言のより具体的な構文です。static として宣言されたフィールドやメソッドはすべてのインスタンスで共有され、インスタンスが存在しなくてもアクセスすることができます。static フィールドはクラス変数とも呼ばれ、static メソッドはクラス・メソッドと呼ばれます。これに対して static が指定されていない通常のフィールドをインスタンス変数と呼び、static が指定されていないメソッドをインスタンス・メソッドと呼びます。

static が指定されているクラス変数やクラス・メソッドはインスタンスを持ちません。インスタンス変数やインスタンス・メソッドは this キーワードを指定して自分自身のオブジェクトを指すことができましたが、static メンバでは this を指定することができません。

このような static が指定されることによって生じるインスタンスを持たない範囲を静的コンテキストと呼びます。静的コンテキスト内に記述されている文は this を持たないことを意識してください。例えば、私たちが何度も宣言してきた main() メソッドには static が指定されており、main() メソッド内のコードは静的コンテキスト内に記述されています。

クラス変数へのアクセスやクラス・メソッドの起動は、オブジェクトからはもちろんですが、クラス名でアクセスすることもできます。static メンバはインスタンスがいくつあっても(インスタンスがなくても)、常に 1 つしか実体が存在しないため、通常はクラス名からアクセスします。

static メンバへのアクセス
クラス名 . メンバ

オブジェクトを指定して static メンバにアクセスする方法は、好ましい記述ではありません。概念的に、static メンバを保有しているのはクラスでありインスタンスが保有しているわけではないからです。開発者はクラス名からアクセスするように、記述を統一しましょう。

コード1
class Test {
	static String title;
	String name;

	Test(String name) {
		this.name = name;
	}

	public static void main(String args[]) {
		title = "Kitty on your lap";
		
		print(new Test("obj1"));
		Test.print(new Test("obj2"));
	}
	static void print(Test obj) {
		System.out.println("name = " + obj.name + " : title = " + obj.title);
	}
}
実行結果
>java Test
name = obj1 : title = Kitty on your lap
name = obj2 : title = Kitty on your lap

コード1は、クラス変数とクラス・メソッドの実体が常に一つしか存在しないことを証明します。Test クラスはクラス変数 title とクラス・メソッド print() を宣言しています。main() メソッドでは、まず title フィールドに新しい文字列を代入し、Test オブジェクトの情報を画面に表示する print() メソッドを呼び出しています。print() メソッドは、渡されたオブジェクトの name インスタンス変数と、title クラス変数をそれぞれ表示します。

同一クラス内のメンバへのアクセスは自分自身のオブジェクトを省略することができましたが、これは static メンバの場合も同じです。オブジェクト、またはクラス名を省略して直接アクセスすることができるので、最初の title フィールドへのアクセスや print() メソッドの起動は識別子だけを指定します。ただし、静的コンテキストからインスタンス変数やインスタンス・メソッドへのアクセスは、オブジェクトを省略することができません。これは、静的コンテキストが this を持たないためです。

次に、Test クラス名を明示的に指定して、再び異なるオブジェクトで print() メソッドを呼び出していています。通常の外部からの呼び出しでは、クラス名とメンバ名を指定して static メンバにアクセスすることができます。

print() メソッドは、与えられたオブジェクトから name フィールドと title フィールドにアクセスしています。print() メソッドのブロックは静的コンテキスなので、インスタンス変数である name へはオブジェクトからでなければアクセスすることができません。title はクラス変数なので、オブジェクトからアクセスすることに意味はありませんが、このように記述しても問題はありません。

実行結果を見れば、すべての Test オブジェクトが title フィールドを共有していることを証明できます。プログラムでは 2 つの異なるインスタンスを生成して print() メソッドに与えていますが、得られた文字列は同一です。もちろん、インスタンスがなくても title フィールドを使うことは可能です。

static メンバは実体が 1 つしか存在しないものを効率よく提供するために使います。例えば、オペレーティング・システムの情報を提供するクラスのメンバはほとんどが static となるでしょう。数学的な演算を行ってくれるメソッドなど、とくにオブジェクトが必要ない場合などにも使用されます。