第6章 クラスメンバとインスタンスメンバ

6.1 インスタンスメンバとクラスメンバについて

これまでクラス内にはフィールド変数、メソッド、コンストラクタといったメンバを記述できることを学習してきました。その中のフィールドとメソッドのメンバについて2種類に分類することができます。それはこれまでの章で目にしてきたインスタンスメンバ、そしてもう1つ本節の後半で学習するクラスメンバとなります。
Java基礎テキストの第2章では、「static」キーワードがついたメソッドのことを「クラスメソッド」、つかないメソッドを「インスタンスメソッド」と呼ぶことを説明しましたが、それはフィールド変数にも当てはまり、「static」キーワードがついた変数のことを「クラス変数」、つかない変数を「インスタンス変数」と呼びます。また、「static」キーワードがついたメソッドや変数を総称して「クラスメンバ」、つかないメソッドや変数を「インスタンスメンバ」と呼びます。
2つのメンバの違いを簡単に説明すると以下のようになります。

  • インスタンスメンバはオブジェクトに対して関連付けられるメンバ
  • クラスメンバはクラスに対して関連付けられるメンバ

この関連付けされる場所によって大きく仕組みが変わってきますので、これからその仕組みについて学習します。
まずはこれまでに目にしてきた、インスタンスメンバで構成されたクラスの再確認をしていきましょう。

6.1.1 インスタンスメンバとは

これまでの学習でクラスに定義してきたメンバ(フィールド変数、メソッド)をインスタンスメンバと呼びます。

インスタンスメンバの特徴
クラス内のインスタンスメンバにはオブジェクトが作成されてはじめて、フィールド変数に値を格納できたり、メソッドを呼び出したりすることができます。逆に言えば、オブジェクトを生成しない限りメンバにアクセスができないという性質をもつものがインスタンスメンバになります。
オブジェクト生成時にインスタンスメンバは、オブジェクトごとにメンバが生成されます。(2.3.3サンプル参照)

書式:インスタンスメンバの定義

これまでの各章で定義してきたクラスと同じになります。

凡例:インスタンスメンバのクラス定義


次の項にてインスタンスメンバで構成されたサンプルを再度簡単に説明していきます。

6.1.2 インスタンスメンバを使用したプログラム

インスタンスメンバで構成されたクラスを複数オブジェクト化し、インスタンスメンバにアクセスして内容を確認します。

① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch06
③ 名前: InstanceMember
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

  package jp.co.f1.basic.ch06;

  class Computer1 {
    private String os; // インスタンス変数
    private int memory; // インスタンス変数

    public Computer1() { // コンストラクタ
      this.os = null;
      this.memory = 0;
      System.out.println("パソコンを作成しました。");
    }

    public void setOsMemory(String os, int memory) { // インスタンスメソッド
      this.os = os;
      this.memory = memory;
      System.out.println("OSを「" + os + "」にメモリを「" + memory + "MByte」に変更しました。");
    }
    public void show() { // インスタンスメソッド
      System.out.println("パソコンのOSは「" + os + "」です。");
      System.out.println("メモリサイズは「" + memory + "MByte」です。");
    }
  }

  public class InstanceMember {
    public static void main(String[] args) {
      Computer1 com1 = new Computer1(); // com1オブジェクト作成(インスタンス作成)
      com1.setOsMemory("WindowsXP", 2048); // com1オブジェクトのsetOsMemoryメソッド呼び出し
      com1.show(); // com1オブジェクトのshowメソッド呼び出し
      System.out.println("---------------------------------------------------------");
      Computer1 com2 = new Computer1(); // com2オブジェクト作成(インスタンス作成)
      com2.setOsMemory("Windows2000", 512); // com2オブジェクトのsetOsMemoryメソッド呼び出し
      com2.show(); // com2オブジェクトのshowメソッド呼び出し
    }
  }
  
実行結果

解説
3~22行目で定義されているComputer1クラスはインスタンスメンバのみで構成されています。
4、5行目の2つのフィールド変数「os、memory」がインスタンス変数に該当し、13行~21行目の2つのメソッド「setOsMemory()、show()」がインスタンスメソッドに該当します。

24行~34行の「InstanceMember」クラスで定義しているmainメソッド内で「Computer1」クラスを2つ生成しています。26、30行目でオブジェクトを2つ生成することにより、個々のオブジェクト(com1、com2)のインスタンスメソッドを呼び出すことができます。

実行結果を見て分かるようにオブジェクト毎(com1、com2)にフィールドの値があることが確認できます。

ポイント
  • インスタンスメンバはオブジェクト自身に関連付けられる為、オブジェクトを生成してアクセスする必要がある。さらにインスタンスメンバはオブジェクト毎に個々として独立する。


図 6.1.1: インスタンスメンバの関連付け

上記の図のようにオブジェクトを生成することで、インスタンスメンバも同時に生成されます。これによりメソッドを利用してそれぞれのオブジェクトの「os」や「memory」に値を代入したり、出力したりすることができました。OSやメモリといった値がそれぞれのパソコンに設定されているように、各Computer1オブジェクトのフィールドへ値を格納することができたのです。

オブジェクトを生成すると実体化しアクセス可能になるメンバはオブジェクトに関連付けられていると言えます。このような、各オブジェクトに関連づけられているフィールドをインスタンス変数、メソッドをインスタンスメソッド、この2つを総称してインスタンスメンバと呼びます。
図を見るとコンストラクタが消されているのが確認できます。なぜならコンストラクタはオブジェクトを生成する際に呼び出される特別なメソッドのため、オブジェクトに関連付けられません。このことも合わせて覚えておいて下さい。

前項でインスタンスメンバについて学習しました。次の項ではもう1つのクラスメンバについて説明を行っていきます。

6.1.3 クラスメンバとは

インスタンスメンバはオブジェクトに関連づけられたように、クラスメンバはクラス自身に関連づけられるメンバのことをいいます。ではどのようにすればクラスメンバとして定義できるか、構文を説明していきます。

クラスメンバの特徴

static修飾子がついたクラスメンバはオブジェクトを生成しないでメンバにアクセスができるという性質をもちます。オブジェクトがいくつ生成されてもクラスメンバは、各オブジェクト共通のメンバになりますので、クラス全体で扱うべきデータや操作をクラスメンバとしておくのが一般的な使い方になります。

書式:クラスメンバの定義

これまでインスタンスメンバの定義方法に加えてstaticというキーワードがついている所がポイントです。
このstaticキーワードがつくことでクラスメンバを「スタティックメンバ」とも呼びます。

凡例:クラスメンバの定義

書式:クラスメンバへのアクセス方法

クラスメンバへのアクセス方法は「クラス名」の後に「.」をつけてアクセスしたいクラスメンバを記述します。
オブジェクトの生成が必要ない点がポイントになります。

凡例:クラスメンバへのアクセス方法

クラスメンバの定義凡例で紹介した「Computer2」クラスを例にしています。

① クラス変数へはオブジェクトを生成せずに「クラス名.フィールド変数名」で直接アクセスします。
② クラスメソッドへはオブジェクトを生成せずに「クラス名.メソッド名」で直接アクセスします。
③ Computer2クラス型のオブジェクトを生成します。(com1)
④ インスタンスメソッドへはオブジェクトから「オブジェクト変数名.メソッド名」でアクセスします。
⑤ Computer2クラス型のオブジェクトを生成します。(com2)
⑥ インスタンスメソッドへはオブジェクトから「オブジェクト変数名.メソッド名」でアクセスします。

上記を見て分かるように、クラスメンバへはオブジェクトを生成しなくてもアクセスすることが可能になっています。なぜならクラスメンバはクラス自身に関連付けされており、既に実体として定義されているからです。インスタンスメンバもクラスに定義しますが実体ではありません。オブジェクトを生成することでオブジェクト自身に関連付けられた実体を持つことになり、そこで初めてアクセスが可能になります。この違いがインスタンスメンバとクラスメンバの大きな違いになります。
では次の項でクラスメンバを利用したサンプルを紹介します。

6.1.4 クラスメンバを使用したプログラム

インスタンスメンバとクラスメンバ両方で構成されたクラスを複数オブジェクト化して、クラスメンバにアクセスして内容を確認します。

① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch06
③ 名前: ClassMember
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

  package jp.co.f1.basic.ch06;

  class Computer2 {
    private String os; // インスタンス変数
    private int memory; // インスタンス変数
    public static int sum; // クラス変数

    // コンストラクタ
    public Computer2() {
      this.os = null;
      this.memory = 0;
      sum++;
      System.out.println("パソコンを作成しました。");
    }
    public void setOsMemory(String os, int memory) { // インスタンスメソッド
      this.os = os;
      this.memory = memory;
      System.out.println("OSを「" + os + "」にメモリを「" + memory + "MByte」に変更しました。");
    }
    public void show() { // インスタンスメソッド
      System.out.println("パソコンのOSは「" + os + "」です。");
      System.out.println("メモリサイズは「" + memory + "MByte」です。");
    }
    public static void showSum() { // クラスメソッド
      System.out.println("■パソコンは合計" + sum + "台作成されています。");
    }
  }

  public class ClassMember {
    public static void main(String[] args) {
      Computer2.sum = 0; // クラス変数sumにアクセスし0で初期化
      Computer2.showSum(); // クラスメソッドshowSumにアクセス1回目(オブジェクト作成前)

      Computer2 com1 = new Computer2();
      com1.setOsMemory("WindowsXP", 2048);
      com1.show();
      Computer2.showSum(); // クラスメソッドshowSumにアクセス2回目(com1オブジェクト作成後)

      Computer2 com2 = new Computer2();
      com2.setOsMemory("Windows2000", 512);
      com2.show();
      Computer2.showSum(); // クラスメソッドshowSumにアクセス3回目(com1,com2オブジェクト作成後)
    }
  }
  
実行結果

解説
3~27行目で定義されているComputer2クラスはインスタンスメンバとクラスメンバで構成されています。6行目のフィールド変数「sum」がクラス変数に該当し、24行~26行目のメソッド「showSum()」がクラスメソッドに該当します。その他のフィールド変数やメソッドはインスタンスメンバになります。
Computer2クラスのコンストラクタ処理内12行目で、クラス変数「sum」をインクリメントしています。
これによりオブジェクト生成するごとに数値が1増え、クラスを何回生成しているか分かるようになります。

ClassMember.javaのmainメソッド内で、まず初めに31行目の処理でクラス変数である「sum」にアクセスして「0」を設定しています。※ここではまだオブジェクトは生成していない。

  Computer2.sum = 0;    //クラス変数sumにアクセスし0で初期化
  

続いて32行目の処理でクラスメソッドである「showSum()メソッド」にアクセス(1回目)し、クラス変数の「sum」の値を画面に出力しています。実行結果からも分かるように31行目で設定した値0で正しくメッセージが出力されているのが確認できます。

34行目で1つ目のComputer2型のオブジェクトを生成し、「setOsMemory()メソッド」を利用して値を設定しています。値が正しく設定されているかを「show()メソッド」を利用して確認しています。

34行でComputer2型のオブジェクト生成が行われクラス変数「sum」の値が1増えているはずなので、37行目でクラスメソッドである「showSum()メソッド」にアクセス(2回目)し、クラス変数の「sum」の値を画面に出力しています。オブジェクト生成後クラス変数「sum」の値は正しく1増えていることが確認できます。

39行目で2つ目のComputer2型のオブジェクトを生成し、「setOsMemory()メソッド」を利用して値を設定しています。値が正しく設定されているかを「show()メソッド」を利用して確認しています。

39行でComputer2型のオブジェクト生成が行われクラス変数「sum」の値がさらに1増えているはずなので、42行目でクラスメソッドである「showSum()メソッド」にアクセス(3回目)し、クラス変数の「sum」の値を画面に出力しています。オブジェクト生成後クラス変数「sum」の値が、1増えていることが確認できます。

ポイント
  • クラスメンバはクラス自身に関連付けられる為、オブジェクトを生成せずに直接アクセスできる。但しクラスメンバは各オブジェクト共通メンバになる。


図 6.1.2: クラスメンバの関連付け

クラス自身に関連づけられているフィールドをクラス変数、メソッドをクラスメソッドといいこの2つを総称してクラスメンバと呼びます。

6.1.5 インスタンスメソッドからクラスメンバを利用したプログラム

インスタンスメソッド内からクラスメンバにアクセスして動作するプログラムです。

① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch06
③ 名前: ClassMemberFromInstanceMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

  package jp.co.f1.basic.ch06;
  
  class Computer3 {
    private String os;
    private int memory;
    public static int sum; // クラス変数
    // コンストラクタ
    public Computer3() {
      this.os = null;
      this.memory = 0;
      sum++; // コンストラクタ内でクラス変数sumにアクセス(参照)する
      System.out.println("パソコンを作成しました。");
    }
    public void setOsMemory(String os, int memory) {
      this.os = os;
      this.memory = memory;
      System.out.println("OSを「" + os + "」にメモリを「" + memory + "MByte」に変更しました。");
    }
    public static void showSum() {
      System.out.println("■パソコンは合計" + sum + "台作成されています。");
    }
    public void checkSum() {
      if (sum > 2) { // インスタンスメソッド内でクラス変数sumにアクセス(参照)する
        System.out.println("■" + sum + "台コンピュータが作成されたので、メンテナンスを実施して下さい。");
      } else {
        System.out.println("■" + sum + "台目なので、まだメンテナンスは必要ありません。");
      }
    }
  }
  public class ClassMemberFromInstanceMethod {
    public static void main(String[] args) {
      Computer3 com1 = new Computer3(); // Computer3オブジェクト作成1回目
      com1.setOsMemory("WindowsXP", 2048);
      com1.checkSum();
  
      Computer3 com2 = new Computer3(); // Computer3オブジェクト作成2回目
      com2.setOsMemory("Windows2000", 512);
      com2.checkSum();
  
      Computer3 com3 = new Computer3(); // Computer3オブジェクト作成3回目
      com3.setOsMemory("WindowsVista", 3072);
      com3.checkSum();
    }
  }
  
実行結果

解説
このComputer3クラスは6.1.4で紹介したComputer2クラスに22行~28行目で定義しているcheckSumメソッドが追加されています。このcheckSumメソッドはクラス変数であるsumの値を調べ、3以上であるならメンテナンスを促すメッセージを出力します。

ClassMemberFromInstanceMethodクラスのmainメソッド内32行目で1回目のオブジェクト生成を行います。
33行目でインスタンスメソッドを利用して各値を設定したのち、34行目の「checkSum()メソッド」を呼び出してクラス変数の値チェックを行っています。画面の出力結果を見て分かるように、インスタンスメソッドからクラス変数にアクセスできていることが確認できます。

続いて36行目で2回目のオブジェクト作成を行います。37行目でインスタンスメソッドを利用して各値を設定したのち、38行目の「checkSum()メソッド」を呼び出してクラス変数の値チェックを行っています。画面の出力結果を見てまだ2回目のオブジェクト生成なので、「■2台目なので、まだメンテナンスは必要ありません。」と表示されていることが確認できます。

最後となる3回目のオブジェクト作成を40行目で行います。41行目でメソッドを利用して各値を設定したのち、42行目の「checkSumメソッド」を呼び出してクラス変数の値チェックを行っています。今回のオブジェクト生成処理でsumの値が3となり、画面に「■3台コンピュータが作成されましたので、メンテナンスを実施してください。」と正しく出力されているのが確認できます。

今回のサンプルではインスタンスメソッドからクラス変数にアクセスしていますが、クラスメソッドにもアクセス可能となっていますので合わせて覚えておいてください。

ポイント
  • インスタンスメソッド内からクラスメンバへアクセスは可能。

NEXT>> 6.2 クラスメンバの注意点