クラス
クラスを作成する
さて、これまで私たちはmain()メソッドをtestという名前のクラスで動かしてきました
これからは、このクラスやメソッド、オブジェクトやコンストラクタなどのオブジェクト指向の真髄を目指しましょう
まず、私たちが作ってきたtestクラスにはmain()メソッドがあります
Javaのプログラムがここから始まるのは、すでにご存知のとおりですね
このように、クラスに登録されているメソッドや変数のことをメンバと呼びます
クラスを作成するには、クラス名を決め、メソッドや変数などのメンバを定義します
クラスの作成は次のような書式になります
class class-name {
members...
}
class-nameにはクラス名を指定します。いままでのサンプルでは全て test で統一していた部分です
membersにはクラスのメンバを定義します
たとえば次の例は neko クラスを定義しています
class neko {
String name;
int love;
}
このクラスのメンバを利用するにはオブジェクトが必要です
オブジェクトとは、このクラスのメンバを利用する「物」のことです
これをJava言語ではインスタンス(実体)と呼び、インスタンスを作ることをインスタンス化と言います
インスタンスの生成にはnew演算子を使用します
class-name objRef;
objRef = new constructor(args);
class-nameはインスタンスを作成するクラス型変数を指定します
objRefはオブジェクトへの参照変数名(オブジェクト名)を指定します
constructorにはコンストラクタを指定します
コンストラクタについては後記しますが、この場ではクラス名と同じメソッドであると覚えてください
ここで大切なのは、最初の一行目がなにをやっているかです
実は、オブジェクト変数は直接オブジェクトを操作するわけではありません
オブジェクト型変数はオブジェクトへの参照なのです(この部分はC++と概念が違う)
class-name objRef; では、まだオブジェクトへの参照はありません
これは、ただ指定されたクラス型の変数をメモリ領域に割り当てたにすぎないのです
class-nameがクラス型の指定であり、この変数にオブジェクトの参照を代入するのがコンストラクタなのです
上の neko クラスのインスタンスを生成するには次のようになります
neko objRef = new neko();
objRefには任意のオブジェクト名を指定すれば良いですね
オブジェクトは必要な数だけ用意することができます
作成したオブジェクト(インスタンス)にアクセスするには、オブジェクト名とメンバを指定します
objRef.member;
objRefはオブジェクト名、memberにはメンバを指定します
オブジェクト名とメンバの間にドット演算子をつけるのを忘れないでください
さっそくプログラムしてみましょう
class neko {
String name;
int love;
}
class test {
public static void main(String args[]) {
neko rena = new neko();
neko yuki = new neko();
neko mimi = new neko();
rena.name = "レナ";
rena.love = 186;
yuki.name = "ユキ";
yuki.love = 549;
mimi.name = "ミミ";
mimi.love = 325;
System.out.println("名前\t\t好感度");
System.out.println(rena.name + "\t\t" + rena.love);
System.out.println(yuki.name + "\t\t" + yuki.love);
System.out.println(mimi.name + "\t\t" + mimi.love);
}
}
このプログラムでは、rena yuki mimi の三つのオブジェクトを生成しています
それぞれのオブジェクトに割り当てられているメンバに値を代入して初期化し、それを出力します
結果は次のようになりました
名前 好感度
レナ 186
ユキ 549
ミミ 549
この結果を見ていただけると分かると思いますが、メンバはオブジェクトごとに割り当てられます
renaオブジェクトとyukiオブジェクトの nameメンバ変数はまったくの別物なのです
この章では、インスタンスの生成とクラスの定義方法
メンバはオブジェクト一つひとつに割り当てられるということを理解してください
さらに、クラスのメンバ変数にはデフォルト値を与えることができます
生成されたインスタンスは、何らかの値が代入されるまでデフォルト値を参照することになります
また、メンバ変数で同じ型のメンバが複数ある場合
メソッド内の変数の宣言同様に一行に複数の変数をカンマで区切って宣言できます
class neko {
String name = "レナ";
int nekoj , hitoj = 84 , love = 200;
}
class test {
public static void main(String args[]) {
neko rena = new neko();
rena.nekoj = 726;
rena.love += 300;
System.out.println("名前\t好感度\t猫常識\t人常識");
System.out.println(rena.name + "\t"
+ rena.love + "\t" + rena.nekoj + "\t" + rena.hitoj);
}
}
メンバ変数の宣言時に値を代入しているのがわかりますね
こうすることで、インスタンスに初期値を定義することができます
main()メソッドを見てわかると思いますが、renaオブジェクトはnameメンバ変数を初期化していません
しかし、デフォルトでnameメンバ変数は「レナ」という文字列になっているので結果は次のようになります
名前 好感度 猫常識 人常識
レナ 500 726 84
これで、初期時の値が定められているメンバ変数は、インスタンス生成時に同時に初期化できます
ところで、C++プログラマなどなオブジェクトの解放を気にするかもしれません
C++ 言語などでは、作成したオブジェクトは不用になると削除します
しかし、Java ではそのような必要はないのです
Java VM はガーベジコレクションと呼ばれる小さなプログラムが走っており
これが、不用になったオブジェクトを判断して自動的に解放してくれます
多重参照
クラスのメンバ変数に、オブジェクト型を指定することも可能です
そのため、この機能を用いてクラスのネスティングが可能です
たとえば、Aクラスのメンバ変数でBクラスのオブジェクト型が定義され
BクラスのオブジェクトをCクラスが参照していれば、CからAクラスの参照が可能なのです
objB.objA.valueA;
objBは、CクラスがもつBクラスのインスタンスで
objAは、Bクラスがメンバとして定義しているAクラスへのオブジェクト型変数で
valueAは、Aクラスのメンバ変数などとたとえることができます
Aクラスのインスタンスは、Bクラスのインスタンスごとに生成されることになります
class A {
String str = "Kitty on your lap";
}
class B {
A objA = new A();
}
class test {
public static void main(String args[]) {
B objB = new B();
System.out.println(objB.objA.str);
}
}
testクラスのmain()メソッド内で、Bクラスのインスタンスを生成し
Bクラスのメンバ変数 objA は、デフォルトでAクラスのインスタンスを持っています
nullポインタ
クラスオブジェクト型の変数を宣言した時は
まだこの変数にオブジェクトへの参照があるわけではありません
Javaでは、この状態をnullという値で表します
参照がないnullの状態で、オブジェクトにアクセスしようとした場合
当然インスタンスがないので、アクセスのしようがありません
ですが、構文は正しいのでコンパイルは可能です
しかし、実行すると例外と呼ばれるものが発生し、実行を中断されます(例外については後記)
class A {
String str = "Kitty on your lap";
}
class B {
A objA;
}
class test {
public static void main(String args[]) {
B objB = new B();
System.out.println(objB.objA.str);
}
}
先程のプログラムで、Bクラスのメンバ変数 objA の初期化を無くしています
objAは初期化されていないため、デフォルトでnullを参照しています
このプログラムはコンパイルされますが、正しく実行されません
参照するオブジェクトがnullかどうかは、if文などで調べることができます
class A {
String str = "Kitty on your lap";
}
class B {
A objA;
}
class test {
public static void main(String args[]) {
B objB = new B();
if (objB.objA == null) {
System.out.println("nullを参照しました。インスタンス化します");
objB.objA = new A();
}
System.out.println(objB.objA.str);
}
}
このプログラムを実行すると、以下の結果が得られます
nullを参照しました。インスタンス化します
Kitty on your lap
nullが検出された場合、インスタンスを生成して代入することで、例外を防ぎます
複雑なプログラムになれば、このような対策が頻繁に要求されます
オブジェクト型の配列
データ型の変数同様に、オブジェクトも配列にすることができます
方法は、通常の変数のときと変わりません
new演算子のあとに、クラス型と添え字を指定します
class Kitty {
String str;
}
class test {
public static void main(String args[]) {
Kitty obj[] = new Kitty[10];
for (int i = 0 ; i < 10 ; i++) {
obj[i] = new Kitty();
obj[i].str = "Kitty " + i;
}
for (int i = 0 ; i < 10 ; i++)
System.out.println(obj[i].str);
}
}
上のプログラムは、Kitty型の配列変数を10まで用意し
全ての要素にインスタンスを生成し、それを代入しています
同じに、文字列をKitty.objメンバ変数に代入し、次のfor文でそれを出力します
new
オブジェクト、もしくは配列を生成します
オブジェクトの場合、最初に指定されたクラスのインスタンスが生成されます
class
クラスを宣言します
オブジェクトを新しく定義し、コンストラクタ、メソッド、およびフィールドを使用してインプリメントを記述します