パパの勉強部屋

勉強の記録をつけています。Java、ネットワーク、Excel、etc.

スッキリわかるJava入門 第9章 さまざまなクラス機構③

第9章 さまざまなクラス機構③

静的メンバ クラス上に準備されるフィールド

  • インスタンスの独立性…newによって生成される個々のインスタンスは基本的に独立した存在。
  • たとえば勇者3名がパーティを組んで冒険するRPGを作る場合、次のようなクラスを書くと、各フィールド(name,hp,money)を各インスタンスが別々に持つことになる。

public class Hero {
  String name;
  int hp;
  int money;
}

  • しかし、プログラムを開発していると「各インスタンスで共有したい情報」が出てくることがある。
  • たとえばRPGはパーティで行動しているので「パーティで1つの財布」を設定したいなど。
  • 同じクラスから生成されたインスタンスでフィールドを共有したい場合、フィールド宣言の先頭にstaticキーワードを追加する。

public class Hero {
  String name;
  int hp;
  static int money;
}
  • staticキーワードが指定してあるフィールドは特に静的フィールド(static field)といわれ、下記の3つの特殊な効果をもたらす。
①フィールド変数の実体がクラスに準備される
  • 通常、フィールドが格納される箱(領域)は個々のインスタンスごとに用意されるが、静的フィールドの箱はインスタンスではなく、クラスに対して1つだけ用意される。
  • イメージは、「勇者の金型」の上にmoney箱が準備されるイメージ。
  • Heroクラスに準備された箱(静的フィールド「money」)を読み書きするには、「Hero.money」という表記を使う。
静的フィールドへのアクセス方法
  • クラス名.フィールド名

public class Main {
  public static void main(String[] args) {
    Hero h1 = new Hero();
    Hero h2 = new Hero();
    System.out.println(h1.hp);
    System.out.println(Hero.money);
  }
}
②全インスタンスに、箱の分身が準備される
  • 共通財産である金額が格納される変数(Hero.money)は、あくまで金型に作られる。
  • しかし、同時にh1やh2といった各インスタンスにもmoneyという名前で「箱の分身」が準備され、金型の箱の別名として機能するようになる。
  • つまり、「h1.money」や「h2.money」という分身の箱に値を代入すれば、本物の箱Hero.moneyにその値が代入される。(インスタンス(h1,h2)経由で、静的フィールド(Hero.money)へのアクセスが可能となる)
  • 実質的に「h1.money」「h2.money」「Hero.money」はどれも同一の箱を指すことになる。
  • このようすは「h1とh2がmoneyフィールドを共有している」と考えることもできるので「静的フィールドを用いれば、インスタンス間でフィールドを共有できる」と解説されることもある。
静的フィールドへの別名によるアクセス
  • インスタンスが含まれる変数名.静的フィールド名」と書いても良いが、「クラス名.静的フィールド名」と同じ意味。

public class Main {
  public static void main(String[] args) {
    Hero h1 = new Hero();
    Hero h2 = new Hero();
    Hero.money = 100;
    System.out.println(Hero.money); -- 100と表示
    System.out.println(h1.money);   -- 100と表示
    h1.money = 300;
    System.out.println(h2.money);   -- 300と表示
  }
}
インスタンスを1つも生み出さなくても箱が利用可能になる
  • 「Hero.money」は金型の上に作られる箱。よって、1つの実態もまだ生み出されていない状況にあっても利用することができる。

public class Main {
  public static void main(String[] args) {
    Hero.money = 100;
    System.out.println(Hero.money); -- 100と表示
  }
}
  • 静的フィールドはクラス(金型)にフィールド(箱)が所属するという特徴からクラス変数と言われることもある。
public static final コンビネーション
  • 多くの場合、staticはfinalやpublicと一緒に利用され、「変化しない定数を各インスタンスで共有するため」に利用される。

静的メソッド

  • Heroクラスに「勇者たちの所持金をランダムに設定する」setRandomMoney()メソッドを追加する場合。

public class Hero {
  String name;
  int hp;
  static int money;
  
  static void setRandomMoney() {
    Hero.money = (int)(Math.random() * 1000);
  }
}
  • staticキーワードがついているメソッドは、静的メソッド(static method)またはクラスメソッド(class method)と言う。
  • 静的フィールドとあわせて静的メンバ(static member)と総称される。
  • 静的メソッドを定義すると、静的フィールドと同様に以下の3つの効果が現れる。
①メソッド自体がクラスに属するようになる
  • 静的メソッドは、その実体が各インスタンスではなくクラスに属し、「クラス名.メソッド名();」で呼び出せる。
インスタンスにメソッドの分身が準備される
  • 静的メソッドは「インスタンス変数名.メソッド名();」でも呼び出せる。
インスタンスを1つも生み出さなくても箱が利用可能になる
  • 静的メソッドは、1つもインスタンスを生み出していない状況でも呼び出せる。

public class Main {
  public static void main(String[] args) {
    Hero.setRandomMoney();
    System.out.println(Hero.money); -- ランダムな金額が表示
    Hero h1 = new Hero();
    System.out.println(h1.money);   -- 同じ額を表示
  }
}
mainメソッドがなぜstaticでなければならないか?
  • mainメソッドが呼び出されるとき、仮想世界にはまだ1つもインスタンスが存在していない。
  • mainメソッドが属するクラス(Mainクラスなど)さえも、まだインスタンス化されていない状況で、mainは呼び出される必要がある。

静的メソッドの制約

  • 静的メソッドの中に記述するコードでは、staticが付いていないフィールドやメソッドは利用できない。

public class Hero {
  String name;
  int hp;
  static int money;
  
  static void setRandomMoney() {
    Hero.money = (int)(Math.random() * 1000);
    System.out.println(this.name + "たちの所持金を初期化しました"); -- this.nameでコンパイルエラー
  }
}
  • 静的メソッドsetRandomMoney()は、まだ1つも勇者インスタンスが存在しない状況でも呼び出されることがあるメソッド。
  • しかし「this.name」は、thisを用いて「自分自身のインスタンスのメンバ」を利用しようとしている。
  • もし仮想世界に1つも勇者インスタンスがない状況でsetRandomMoney()が動いたら、「this.name」は処理できない。