Previous | Next | Trail Map | Writing Java Programs | オブジェクト、クラス、インタフェース


インスタンスとクラスメンバ

MyClass の aFloat のようにメンバ変数を宣言する時、

class MyClass {
    float aFloat;
}

インスタンス変数を宣言する。 クラスのインスタンスを作成する度に、実行時システムはインスタンスのためにクラスのインスタンス変数の各コピーを作成する。オブジェクトの使用で説明するように、オブジェクトからオブジェクトのインスタンス変数にアクセスすることができる。

「PENDING: クラスのインスタンス化とインスタンス変数のコピーの図」

インスタンス変数は、( static 修飾子を使用して宣言する)クラス変数と対照的である。 実行時システムは、そのクラスの作成するインスタンス数にかかわらず、クラス毎にクラス変数を割り当てる。 システムは、クラスを検出した最初の時にクラス変数のためにメモリを割り当てる。 すべてのインスタンスがクラスのクラス変数の同じコピーを共有する。 インスタンスあるいはクラスそれ自身を通してクラス変数にアクセスすることができる。

「保留:クラス vars の picture か?」

メソッドも同様。クラスはインスタンスメソッドとクラスメソッドを持つことができる。 インスタンスメソッドが現在のオブジェクトのインスタンス変数を操作するが、クラス変数にもアクセスできる。 また一方では、クラスメソッドはクラス内で宣言したインスタンス変数にアクセスすることができない(新規のオブジェクトを作成したり、オブジェクトを通してアクセスしないならば)。 また、クラスメソッドはクラス上で呼び出すことができる。クラスメソッドを呼び出すためのインスタンスは不要である。

デフォルトでは、特に指定されなければ、クラス内で宣言するメンバはインスタンスメンバである。 以下に定義するクラスには、インスタンス変数 (x という名前の整数)と他のオブジェクト設定をして、 x の値に問い合わせる2つのインスタンスメソッド(setX()x())がある。。

class AnIntegerNamedX {
    int x;
    public int x() {
        return x;
    }
    public void setX(int newX) {
        x = newX;
    }
}

クラスから新規のオブジェクトをインスタンス化する度に、クラスの各インスタンス変数の新規コピーを得る。 これらのコピーは新規のオブジェクトと関連がある。 クラスから新規の AnIntegerNamedX オブジェクトをインスタンス化する度に、新規の AnIntegerNamedX オブジェクトと関連がある x の新規コピーを得る。

「 PENDING:x インスタンス変数を持っている AnIntegerNamedX の 図」

すべてのクラスのインスタンスがインスタンスメソッドの同じ実装を共有する。すべての AnIntegerNamedX のインスタンスは同じx() setX() の実装を共有する。 x()setX() の両方のメソッドは、オブジェクトのインスタンス変数 x を名前で参照するので注意すること。 「もしすべての AnIntegerNamedX のインスタンスが x() および setX()と同じ実装を共有すれば、 これは曖昧ではないか」という疑問があるだろう。 答えはノーである。インスタンスメソッド内では、インスタンス変数名は現在のオブジェクトのインスタンス変数を参照する(インスタンス変数がメソッドパラメータにより隠されないと仮定した場合)。 それで、 x() と setX()内では、 x this.x と等しい。

x にアクセスするAnIntegerNamedX 外部のオブジェクトは、AnIntegerNamedX の特定のインスタンスを通してアクセスしなければならない。 このコードの一部が別のオブジェクトのメソッドにあったと仮定する。 AnIntegerNamedX 型の2つの異なるオブジェクトを作成し、異なる値でそれらの x を設定し、以下に示す。

. . .

AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2;
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
. . .

このコードは setX() myX x 値をセットしたが、anotherX.x の値が直接に割り当てたので注意すること。 いずれかの方法で、コードは x の2つの異なるコピーを操作している。1つは myX オブジェクトに含まれ、1つは anotherX オブジェクトに含まれる。 このコードの一部が作り出した出力は、

myX.x = 1
anotherX.x = 2

クラス AnIntegerNamedX の各インスタンスはインスタンス変数 x 自身のコピーをもち、各 x が異なる値を持っていることを示している。

メンバ変数を宣言する時、変数がインスタンス変数ではなくクラスであることを明示することができる。 同様に、メソッドがインスタンスメソッドではなくクラスメソッドであることを明示することができる。 システムは、変数が定義されるクラスを初めて検出した時に、クラス変数のコピーを作成する。 そのクラスのすべてのインスタンスがクラス変数の同じコピーを共有する。 クラスメソッドはクラス変数でのみ操作することができる。それらはクラスで定義したインスタンス変数にアクセスすることができない。

メンバ変数がクラス変数であることを明示するためには、 static キーワードを使用する。 例えば、 x 変数がクラス変数であるように、 AnIntegerNamedX クラスを変更する。

class AnIntegerNamedX {
    static int x;
    public int x() {
        return x;
    }
    public void setX(int newX) {
        x = newX;
    }
}

AnIntegerNamedX の2つのインスタンスを作成する
前述した 全く同じコードだが、 x の値を設定したら、次のような異なる結果になる。

myX.x = 2
anotherX.x = 2

x がクラス変数となったため、出力は異なる。それは変数のコピーは1つだけあり、そしてそれが myX anotherX を含むすべての AnIntegerNamedX のインスタンスにより共有される。 いずれかのインスタンス上に setX() を呼び出すと、 x の値がすべての AnIntegerNamedX のインスタンスへセットされる。

1つだけのコピーを必要とする項目にはクラス変数を使用する。そのクラス変数は、変数が宣言されるクラスから継承しているすべてのオブジェクトからはアクセス可能でなければならない。 例えば、定数を定義するためによく final でクラス変数を使う(これは定数が変更できないほどのメモリ効果があるので、コピーは実際に1つだけでよい)。

同様に、メソッドを宣言すると、インスタンスメソッドではなくクラスメソッドになるメソッドを指定することができる。 クラスメソッドはクラス変数でのみ動作することができ、クラスで定義するインスタンス変数にアクセスすることができない。

メソッドがクラスメソッドであることを明示するためには、メソッド宣言で static キーワードを使用する。 メンバ変数 x がもう一度インスタンス変数になり、その2つのメソッドがクラスメソッドになるように、 AnIntegerNamedX クラスを変更する。

class AnIntegerNamedX {
    private int x;
    static public int x() {
        return x;
    }
    static public void setX(int newX) {
        x = newX;
    }
}

AnIntegerNamedX のこのバージョンをコンパイルしようとすると、コンパイラエラーになる。

AnIntegerNamedX.java:4: Can't make a static reference to nonstatic variable x in class AnIntegerNamedX.
        return x;
               ^ 
AnIntegerNamedX.java:7: Can't make a static reference to nonstatic variable x in class AnIntegerNamedX.
        x = newX;
        ^
2 errors

これは、メソッドが最初に AnIntegerNamedX のインスタンスを作成し、それを通して変数にアクセスしないと、クラスメソッドはインスタンス変数をアクセスすることができないからである。

その x 変数をクラス変数にして、 AnIntegerNamedX を修正する。

class AnIntegerNamedX {
    static private int x;
    static public int x() {
        return x;
    }
    static public void setX(int newX) {
        x = newX;
    }
}

これで、クラスはコンパイルし、 前述したコードはAnIntegerNamedX の2つのインスタンスを作成し、 x 値を設定してから、次の出力を生成する。

myX.x = 2
anotherX.x = 2

myX を通して x を変更すると、 AnIntegerNamedX の他のインスタンスの xも変更される。

インスタンスメンバとクラスメンバのもう1つの相違は、クラスメンバがクラス自身からアクセス可能ということである。 そのクラスメンバをアクセスするためにクラスをインスタンス化する必要がない。 AnIntegerNamedX クラスから直接 x()setX() をアクセスするために、前述の コードの一部を書き換える。

. . .
AnIntegerNamedX.setX(1);
System.out.println("AnIntegerNamedX.x = " + AnIntegerNamedX.x());
. . .

myX anotherX を作成しなくてもよいことに注意する。 x を設定して、そして AnIntegerNamedX クラスから直接 x を検索することができる。 インスタンスメンバに対してはこれを行うことができない。オブジェクトからインスタンスメソッドを起動するだけや、オブジェクトからインスタンス変数にアクセスするだけは可能である。 クラスのインスタンスあるいはクラス自身からクラス変数とクラスメソッドをアクセスすることができる。


Previous | Next | Trail Map | Writing Java Programs | オブジェクト、クラス、インタフェース