クラスとインスタンス

<概要>クラスに関係した用語を学びます。クラスフィールドとクラスメソッドについても学びます。

専門書やJava言語のヘルプには様々な用語が登場します。
ここではクラスに関係した用語を中心に、その意味を解説していきます。

*=*=*=*=*=*=*=*=*=*= インスタンス *=*=*=*=*=*=*=*=*=*=

インスタンスは、クラスの実体を意味します。
「instance」の意味は「実例」。
プログラムでは表現されない、メモリにあるデータです。
クラスの変数との違いに注意して下さい。

ClassName cn=new ClassName();
cn.x= ……


cn ……クラスの変数であり、インスタンスを指すポインタ。
new ClassName() ……インスタンスを作っています。このプログラムがインスタンスではありません。
cn.x ……変数 cn が指し示す先にあるインスタンスの x フィールド

この違いをよく理解して下さい。

また、インスタンスのことをオブジェクトと呼ぶこともあります。
オブジェクトという用語は一般的に使われますが、
インスタンスという用語はクラスと対比して話すときに便利です。

自分のインスタンスを指す予約語として this があります。
this はインスタンスメソッドの中でしか使うことが出来ません。
何故ならば、インスタンスメソッドが呼べるということは、
インスタンスが必ず存在するということになるからです。
もちろん存在しない自分のインスタンスを使えるはずありませんから、
インスタンスが存在しないかも知れないクラスメソッドの中で使えないのは当然ですね。

this が「自分の」インスタンスを指すのに対して、
super は、「スーパークラスの」インスタンスを指します。
スーパークラスは「スーパークラスとサブクラス」で詳しく解説します。

this() は「自分の」インスタンスのコンストラクタ、
super() は「スーパークラスの」インスタンスのコンストラクタ呼び出しを表します。

「クラス」ではなく「インスタンス」である点に注意して下さい。

ちなみに、デフォルトコンストラクタの定義は以下のようになっています。

ClassName(){
  super();
}


*=*=*=*=*=*=*=*=*=*= クラス *=*=*=*=*=*=*=*=*=*=

新たなクラスを作り出すことを「クラスを宣言する」と言います。
定義ではありません。
宣言と定義という用語の使い分けのなんと難しいことか……。
とりわけ、クラスが最小構成単位のJava言語においては混乱の極みです(汗)。
この使い分けに五月蠅かった「決定版 はじめてのC++/塚越一雄」を参照するなら
「クラスを定義する」という言葉はありません。
したがって、クラスに関しては「宣言する」という用語に統一しましょう。

次にメソッドですが、Java言語ではメソッドを宣言する(関数原型宣言)ことが出来ません。
つまり、定義しか書けないことになります。
したがって、メソッドに関しては「定義する」という用語に統一しましょう。

最後に変数ですが、変数は宣言でもあり定義でもあります。
名前が導入され(宣言)、それと同時にメモリが確保される(定義)からです。
このように宣言と定義が同時に行われるのを定義宣言といいます。
しかし、変数は「宣言する」という用語を使うのが一般的でしょう。

クラス、メソッド、定数にはそれぞれ名付けの慣習があります。

クラス……大文字で始める。
メソッド……小文字で始める。
定数……全て大文字。

基本的にはこの慣習に倣った方が良いです。

これまで最も利用してきたクラス String は、java.lang というパッケージに含まれています。
パッケージは「パッケージ」で詳しく解説します。
パッケージは利用することをプログラムで明記しなければなりませんが、
java.lang パッケージに含まれているクラスはいつでも使えます。
java.lang パッケージには基本的な機能が含まれているからです。
String を java.lang.String と表記する場合もあります。
これは、java.lang というパッケージに含まれている String クラスという意味です。

*=*=*=*=*=*=*=*=*=*= フィールド *=*=*=*=*=*=*=*=*=*=

フィールドには初期値を設定することが出来ます。

class Rectangle{
  int width=10;
  int height=20;
}


C++言語ではこのような書き方は許されませんでした。
何故なら、メンバー変数の宣言は定義ではなかったからです。
でも、こんな風に書けるも便利でイイですね(笑)。

上記のようにフィールドに初期値を与えない場合は以下の値が自動的に与えられます。

boolean 型…… false
整数型…… 0
浮動小数点数型…… 0.0
参照型…… null


しかし、初期化されなかった変数の値は未定義になります。
もっとも、変数は初期化しないとコンパイルエラーになり実行できませんが。

*=*=*=*=*=*=*=*=*=*= その他 *=*=*=*=*=*=*=*=*=*=

引数(ひきすう)と一言に言っても実引数と仮引数があります。
実引数はメソッドを呼び出すときに与える引数、
仮引数はメソッドの定義に書かれる引数です。

変数が確保されている領域をスタック( stack )と呼び、
インスタンスが確保されている領域をヒープ( heap )と呼びます。

Java言語では、クラスを単なる部品としてではなく、1つのプログラムとして動かすことが出来ます。
そのとき、そのクラス内の main というメソッドが実行開始位置になります。
これまでに作ってきたクラスに main メソッドを追加すれば、そのクラス単体で動かすことも出来るのです。

例題11-1 クラスフィールドとインスタンスフィールドの違い。

○プログラム

class Rectangle{
  static int counter;
  int number;
  Rectangle(){
    number=counter++;
  }
}

public class ClassTest1{
  public static void main(String[] args){
    System.out.println("counter="+Rectangle.counter);
    Rectangle r1=new Rectangle();
    Rectangle r2=new Rectangle();
    Rectangle r3=new Rectangle();
    System.out.println("r1="+r1.number);
    System.out.println("r2="+r2.number);
    System.out.println("r3="+r3.number);
    System.out.println("counter="+Rectangle.counter);
  }
}


○実行結果

D:\atsushi\Java\List11-9>java ClassTest1
counter=0
r1=0
r2=1
r3=2
counter=3
-- Press any key to exit (Input "c" to continue) --

○解説

static という修飾子を付けてフィールドを宣言すると、クラスフィールドになります。
インスタンスフィールドはインスタンスごとに作られますが、
クラスフィールドはクラスごとに作られます(つまりクラスに1つ)。

インスタンスフィールドはインスタンスの情報を保持しますが、
クラスフィールドはクラスの情報を保持する、全てのインスタンスに共通のフィールドです。
クラスフィールドはインスタンスを作る前から存在しています。
したがって、クラスフィールドはインスタンスを作る前から使うことが出来ます。
このプログラムではクラスの名前から呼び出す方法と
インスタンスを作って変数から呼び出す方法の両方を試しています。
もちろん両方とも正しいプログラムです。

ところで、Java言語のクラスフィールドと同じ機能が
C++言語にも静的メンバー変数という名前でありましたね。

例題11-2 クラスフィールドを返すクラスメソッド。

○プログラム

class Rectangle{
  static int counter;
  int number;
  Rectangle(){
    number=counter++;
  }
  static int getCounter(){
    return counter;
  }
}

public class ClassTest2{
  public static void main(String[] args){
    System.out.println("counter="+Rectangle.getCounter());
    Rectangle r1=new Rectangle();
    Rectangle r2=new Rectangle();
    Rectangle r3=new Rectangle();
    System.out.println("r1="+r1.number);
    System.out.println("r2="+r2.number);
    System.out.println("r3="+r3.number);
    System.out.println("counter="+Rectangle.getCounter());
  }
}


○実行結果

D:\atsushi\Java\List11-10>java ClassTest2
counter=0
r1=0
r2=1
r3=2
counter=3
-- Press any key to exit (Input "c" to continue) --

○解説

static を付けて宣言したメソッドはクラスメソッドです。
クラスメソッドは特定のインスタンスと関連付けられません。
したがって、クラスメソッドの中で「現在のインスタンス」を指す「this」を使うと、コンパイルエラーになります。
同様に、クラスメソッドの中で「スーパークラスのインスタンス」を指す「super」を使っても、コンパイルエラーになります。

クラスメソッドはインスタンスを作る前から存在し、使うことが出来ます。
これもC++言語の静的メンバー関数と一緒ですね。

クラスメソッドだけのクラスを作ることも出来ます。
クラスメソッドだけを集めたクラスの目的は、インスタンスを作ることではなく、
関連したメソッドを1カ所にまとめ、プログラマが目的のメソッドを探しやすくすることです。
例えば Math クラスはクラスメソッドとクラスフィールドだけで構成されています。

例題11-3 インスタンスフィールドを返すクラスメソッド。

○プログラム

class Rectangle{
  static int counter;
  int number;
  Rectangle(){
    number=counter++;
  }
  static int getNumber(Rectangle obj){
    return obj.number;
  }
}

public class ClassTest3{
  public static void main(String[] args){
    Rectangle r1=new Rectangle();
    Rectangle r2=new Rectangle();
    Rectangle r3=new Rectangle();
    System.out.println("r1="+Rectangle.getNumber(r1));
    System.out.println("r2="+Rectangle.getNumber(r2));
    System.out.println("r3="+Rectangle.getNumber(r3));
  }
}


○実行結果

D:\atsushi\Java\List11-13>java ClassTest3
r1=0
r2=1
r3=2
-- Press any key to exit (Input "c" to continue) --

○解説

引数にインスタンスを渡してもらうことで、
そのインスタンスのインスタンスフィールドを返すことが出来ます。
しかし、インスタンス無しでは使えないクラスメソッドなんて意味無いですけどね(汗)。

例題11-4 自分と他者のインスタンスの内容が等しいかどうかを調べるメソッド。

○プログラム

class Rectangle{
  int width;
  int height;
  Rectangle(int width,int height){
    this.width=width;
    this.height=height;
  }
  boolean equals(Rectangle r){
    if(r==null){
      return false;
    }else if(width==r.width && height==r.height){
      return true;
    }else{
      return false;
    }
  }
}

public class ClassTest4{
  public static void main(String[] args){
    Rectangle r1=new Rectangle(10,20);
    Rectangle r2=new Rectangle(10,30);
    Rectangle r3=new Rectangle(10,30);
    if(r1.equals(r2)){
      System.out.println("r1==r2");
    }else{
      System.out.println("r1!=r2");
    }
    if(r1.equals(r3)){
      System.out.println("r1==r3");
    }else{
      System.out.println("r1!=r3");
    }
    if(r2.equals(r3)){
      System.out.println("r2==r3");
    }else{
      System.out.println("r2!=r3");
    }
  }
}


○実行結果

D:\atsushi\Java\ListA11-4>java ClassTest4
r1!=r2
r1!=r3
r2==r3
-- Press any key to exit (Input "c" to continue) --

○解説

自分のインスタンスは必ず存在しているはずなので、
2つのオブジェクトを引数として渡してもらうより、
相手のオブジェクトを引数として渡してもらい、自分と比較した方がスマートですよね。

○補足

Object クラスは自分と他のオブジェクトとの同一性を調べる equals メソッドを持っています。
Object クラスはクラス階層の最上位に位置するため、
全てのクラスは equals メソッドを継承していることになります。
つまり、全てのオブジェクトは他のオブジェクトとの同一性を調べることが出来るのです。
また、自分で equals メソッドを実装するということは、
既に持っている equals メソッドを上書き定義(オーバーライド)することになります。

*=*=*=*=*=*=*=*=*=*= 修飾子 *=*=*=*=*=*=*=*=*=*=

ここで、クラスやメソッドやフィールドや変数に付ける修飾子について解説する。

final
 最終的なものであり、もう変更できないことを表す。
 クラスやインタフェースなら拡張できないことを表す。
 インスタンスフィールドやクラスフィールなら定数であることを表す。
 メソッドなら上書き定義(オーバーライド)することを禁止する。
 インタフェースは「インタフェース」で詳しく解説します。

abstract
 抽象クラスや抽象メソッドであることを表す。
 メソッドには付けられますが、フィールドには付けることは出来ません。
 詳しくは「スーパークラスとサブクラス」で解説します。

static
 クラスフィールドやクラスメソッドであることを表す。

synchronized
 synchronized メソッドであることを表す。
 詳しくは「スレッド」で解説します。

native
 Java言語以外の言語(C言語やC++言語)で書かれたメソッド(ネイティブメソッド)であることを表す。

public / protected / private
 アクセス制御を行います。
 アクセス制御は「パッケージ」で詳しく解説します。


戻る / ホーム