staticなフィールドは、1回宣言すると、プログラムで 1個だけ作成されます。一方で、インスタンスフィールドは、予約語 newでインスタンスを作成した数だけ増えていきます。
同じ型のインスタンスで共有する情報は staticなフィールドでなければなりません。一方で、それぞれのインスタンスに固有の情報はインスタンスフィールドでなければなりません。
staticなフィールドをインスタンスと組み合わせて使用する例はとても少ないと思います。staticなフィールドは、オブジェクト指向との関連性がとても薄いためです。ただし、両者の違いを明らかにすることで、staticなフィールドとインスタンスフィールドの違いが際立つ…ひいては、オブジェクト指向の理解に繋がる、ということで以下で見ていきます。
Dogクラスについて、インスタンスフィールドと staticなフィールドの両方を使用した例を以下に挙げたいと思います。ここで、インスタンスフィールドは nameと cryCounterで、インスタンスの数だけ生成されます(今回はそれぞれ 3個ずつ)。一方で、staticなフィールドは counterで、これはインスタンスの生成とは関係なく、1つだけ生成されていることに注意してください。
ソースコードは以下の通り。
L101/Dog.java
/** * 犬を表すクラスです。 */ public class Dog { /** * カウンタ。 */ private static int counter; /** * 名前。 */ private String name; /** * 鳴く回数。 */ private int cryCount; /** * コンストラクタ * @param name 名前 */ public Dog(String name) { this(name, 1); } /** * コンストラクタ。 * @param name 名前 * @param cryCount 鳴く回数 */ public Dog(String name, int cryCount) { this.setName(name); this.setCryCount(cryCount); Dog.counter ++; System.out.println(Dog.counter + "個目の Dogインスタンスが生成されました。"); } /** * 名前を設定します。 * @param name 名前 */ public void setName(String name) { this.name = name; } /** * 鳴く回数を設定します。 * @param cryCount 鳴く回数 */ public void setCryCount(int cryCount) { this.cryCount = cryCount; } /** * 鳴きます。 */ public void cry() { System.out.print(this.name + "「"); for(int i = 0; i < this.cryCount; i ++) { System.out.print("ワン"); } System.out.println("」"); } /** * お座りします。 */ public void sitDown() { System.out.println(this.name + "は座りました。"); } }
L101/L101.java
/** * staticなフィールドを確認します。 */ public class L101 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { // インスタンスを作成 Dog dog1 = new Dog("ポチ"); Dog dog2 = new Dog("クロ", 5); Dog dog3 = new Dog("ラッキー", 3); // 犬のインスタンスメソッドを実行 dog1.cry(); dog1.sitDown(); dog2.cry(); dog3.cry(); } }
実行結果は以下の通り。
1個目の Dogインスタンスが生成されました。 2個目の Dogインスタンスが生成されました。 3個目の Dogインスタンスが生成されました。 ポチ「ワン」 ポチは座りました。 クロ「ワンワンワンワンワン」 ラッキー「ワンワンワン」
まず、数を数えられることが大切です。Dogクラスについて、以下の数を正確に数えられるでしょうか。
Dogインスタンスを生成する際にコンストラクタが呼び出され、その中で、staticなフィールドである counterがインクリメントされています。counterはインスタンスフィールドではないので、各インスタンスに固有ではなく、すべての Dogインスタンスで共有しているということになります。
イメージとしては以下のような感じです。
なお、counterについては、カプセル化のためアクセス修飾子を privateにしています。カプセル化については先の章にて説明済みです。また、アクセス修飾子の詳細については後の章で解説します。もし、カプセル化をしない場合には、メインメソッド内に以下のような記述をすることで、値を書き換えることができてしまいます。それは良くないだろうということで、カプセル化しています。
Dog.counter = 12345; // カプセル化している場合にはビルドエラーになる