パパの勉強部屋

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

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

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

仮想世界の真の姿

  • ここまで「Java仮想世界」と表現してきたものは、実際には「コンピュータのメモリ領域」。
  • この領域はJavaのプログラムを実行する際にJVMが大量にメモリ領域を準備(通常は数十MB~数GB)するもので、ヒープ(heap)と言う。
  • newを用いてインスタンスを生み出すたびにヒープの一部の領域(通常は数十~数百バイト)が確保され、インスタンスの情報を格納するために利用される。そのため多くの属性を持った大きなクラスをインスタンス化すると多くのヒープ領域を消費する。
  • インスタンスとは「ヒープの中に確保されたメモリ領域」。★

クラス型変数とその中身

public class Main{
  public static void main(String[] args) {
    Hero h;
    h = new Hero();
    h.hp = 100;
  }
}

Step1:Hero型変数の確保
  • 最初に動くのは3行目「Hero h;」。JVMは「Hero型の変数h」をメモリ内に準備する。
  • JVMは広いヒープ領域の中から現在利用していないメモリ領域を探し出して、自動的に確保してくれる。
  • 仮に1928番地が確保されたとする。Hero型の箱が準備されただけ。
Step2:インスタンスの生成
  • 4行目「h = new Hero();」は代入文。代入の場合は左辺より先に右辺が評価される。
  • 「new Hero()」が実行されると、JVMはヒープ領域から必要な量のメモリを確保する。
  • 仮に3299番地から24バイト分(3299~3945番地)が確保されたとする。
Step3:参照の代入
  • 右辺の実行が終了した後、4行目は「h = 右辺の実行結果」という状態になる。
  • 右辺の実行結果とは、newを実行することによって生成されたインスタンスのために確保されたメモリの先頭番地。
  • 変数hには3299という数字が代入される。変数hに入っているアドレス情報を参照と言う。(配列型と同じ)
  • クラス型と配列型は総称して「参照型」と呼ばれる。
Step4:フィールドへの値の代入
  • 5行目の「h.hp = 100」では変数hに格納されている勇者のHPを100に設定する。
  • JVMの解釈は以下の通り。
  • ①変数hの中身を調べる。→「3299番地を参照せよ」
  • ②メモリ内の3299番地にあるインスタンスのメモリ領域にアクセスし、その中のhpフィールド部分を100に書き換える。
  • このような①→②のJVMの動作を「参照の解決」「アドレス解決」と言う。

同一インスタンスを指す変数

public class Main {
  public static void main(String[] args) {
    Hero h1;
    h1 = new Hero();
    h1.hp = 100;
    Hero h2;
    h2 = h1;
    h2.hp = 200;
    System.out.println(h1.hp);
  }
}

  • ポイントは7行目の「h2 = h1」。ここでコピーされているのは「勇者インスタンスそのもの」ではなく、「3299」などの番地情報。
  • h1とh2はどちらも「まったく同じ1人の勇者インスタンス」を指している。
  • h1.hp、h2.hpどちらのフィールドに代入しても、結局は同じ勇者インスタンスのHPに代入することになる。

クラス型をフィールドに用いる

// まず、Swordクラスを定義しておく
public class Sword {
  String name;
  int damage;
}

// 次にHeroクラスを定義する
public class Hero {
  String name;
  int hp;
  Sword sword;
  public void attack() {
    System.out.println(this.name + "は攻撃した!");
    System.out.println("敵に5ポイントのダメージをあたえた!");
  }
}

  • Heroクラスの5行目「Sword sword」。このようにフィールドにクラス型を使っても問題ない。
  • あるクラスが別のクラスをフィールドとして利用している関係を「has-aの関係」と言う。
  • Hero has-a Sword(勇者は剣を持っている)

public class Main {
  public static void main(String[] args) {
    Sword s = new Sword();
    s.name = "炎の剣";
    s.damage = 10;
    Hero h = new Hero();
    h.name = "ミナト";
    h.hp = 100;
    h.sword = s;
    System.out.println("現在の武器は" + h.sword.name);
  }
}

  • 3行目「Sword s = new Sword()」でswordフィールドに生成済みの剣インスタンスの番地を、9行目「h.sword = s」で代入。
  • 10行目の「h.sword.name」は、勇者「の」剣「の」名前を出力する。

クラス型をメソッド引数や戻り値に用いる

  • クラス型はフィールドの型として用いることができるだけではなく、メソッドの引数や戻り値の型として利用することもできる。

String型の真実

  • String型は、int型やdouble型の仲間ではなく、Hero型と同じ「クラス型」。
  • String型を、int型と同じように扱える理由は、Java言語が作られた時に次のような特別の配慮がなされたため。
java.lang パッケージに宣言してある:
  • java.lang パッケージに所属するクラスを利用する際は、特例としてimport文を記述する必要がない。
  • 本来「java.lang.String s;」と宣言する必要があるところを、単に「String s;」と書けば済む。
二重引用符で文字列を囲めばインスタンスを生成・利用できる:
  • 通常、インスタンスを生成するにはnew演算子を利用する必要がある。
  • 文字列はプログラムの中で多用されるため、その都度newを書いていてはソースコードがnewだらけになる。
  • そこで「二重引用符で文字列を囲めば、その文字列情報を持ったStringインスタンスを利用できる」という特例が設けられた。