最終更新:2012.12.07
個人的な備忘録です。自分なりにわかりやすいように解釈しているため、用字用語などは一般的なそれとはかけ離れている場合があることをお断りしておきます。
なお、情報はできる限り正しく解釈しようと努力していますが、ここで書かれていることに関しての正確性は、一切保証できません。必ず、公式な文献をあたるようにしてください。
Javaプログラミングとは、クラスを記述していくことにほかなりません。クラスには少なくとも1つ以上の機能が含まれており、その機能を呼び出すことで様々な操作を実現していきます。記述したクラスは、Javaコンパイラでコンパイルすることで、コンピュータ上で実際に実行できる部品になります。クラスは、単体で利用することはもちろん、必要に応じて特定のクラスから他のクラスを呼び出して利用することもできます。このため、いちいちプログラムを最初から記述する必要はなく、すでに存在しているクラスを組み合わせて効率的にプログラミングを行うことができるというメリットを持っています。
クラスとは、前述のようにコンピュータ上で実行できる部品を作るための設計図です。Javaの開発環境には、汎用的なクラスが多数、最初から用意されているので、これらを使ってすぐにソフトウェア開発をはじめることができます。クラスを記述したファイルは拡張子「.java」が付けられ、そのファイル名はクラス名と同じになります。
自分でクラスを作成する場合は、以下のように記述します。
class クラス名 {}
先頭の「class」は、これから記述する内容がクラスであるという宣言です。
次の「クラス名」は、作成者が自由に決めることができますが、仕様では以下のように記載されているので、それに従ったほうが良いでしょう。
クラス名は、その内容を把握できるような名詞または名詞句であるべきです。極端に長くせず、また複数の単語から構成される場合は、おのおのの単語の先頭を大文字にするのが望ましいでしょう。以下に例を示します。
ClassLoader SecurityManager Thread Dictionary BufferedInputStream
なお、クラス名には以下の制約があります。
最後の中括弧「{}」は、クラスの開始と終わりを示します。実際のプログラミングでは、この中括弧内に必要な処理を記述していきます。
以下に、クラスの例を示します。
class Rat {}
ごく単純なプログラムを別として、たいていの場合、いま現在記述しているクラスから他のクラスを呼び出して利用することになります。その際、呼び出したクラスには個別に名前を付ける必要があります。これにより、同じクラスを複数呼び出して利用することが可能となっています。
特定のクラス内から別のクラスを呼び出すには、以下のように記述します。
呼び出したいクラス名 名称;
たとえば、Ratというクラスを複数呼び出すには、以下のように記述します。
Rat rat1;
Rat rat2;
しかしながら、この段階ではクラスを使用するための名前を宣言したに過ぎません。実際にクラスを利用するには、さらに以下のように記述する必要があります。
名称 = new クラス名();
前述の例でいえば、以下のようになります。
rat1 = new Rat();
rat2 = new Rat();
なお一般的には、上記をまとめて以下のように記述することが多いようです。
Rat rat1 = new Rat();
Rat rat2 = new Rat();
上記のように宣言することでクラスは初めてメモリ上に実体を持ち、有効なプログラムとして動作するようになります。これをクラスのインスタンス化と呼びます。
クラス名の後の括弧「()」内には、後述するconstructorに受け渡したい情報があるときに記述します。特に何もない場合は、そのまま「()」と書いておきます。
既存のクラスを流用し、新しいクラスを作りたい場合があるとします。たとえば、クラスRatとクラスMouseがあったとします。このとき、RatとMouseはともにRodent(齧歯類)に属しますから、ある程度の機能は共通となるはずです。このような場合、個々にRatとMouseのクラスを作成するのは無駄が生じます。あらかじめ共通の機能を実装したクラスRodentsを用意しておき、それに対してRatやMouse固有の機能を付け加えていく方がはるかに合理的だといえるでしょう。
Javaではこのような要望に応えるため、クラスを拡張する機能が用意されています。クラスを拡張するには以下のように記述します。
class 新規に作成するクラス名 extends 元となるクラス名 {}
上記の例だと、以下の通りとなります。
class Rat extends Rodent {}
通常のクラスと同様、中括弧「{}」内に処理したい内容を記述します。このとき、特に指定しない限り、元のクラスが持っていた機能は新しいクラスへと引き継がれます。これを、継承と呼びます。また、元となるクラスをスーパークラス、新たに作成するクラスをサブクラスと呼びます。
なお、Javaではあらゆるクラスのスーパークラスとしてjava.lang.Objectというクラスが定義されています。ただしこれは暗黙的にスーパークラスとして扱われるので、わざわざ明示的にextendsする必要はありません。もちろん、明示しても構いませんが。
以下の記述は、同じものとして扱われます。
class NewObject {}
class NewObject extends java.lang.Object {}
クラスの拡張は、同時に一つまでしか行えないことに注意してください。たとえば次のようにしてキメラを作ることはできません。
class Chimera extends Rat extends Cat {}
クラスの中には、そのままでは使えない、一部が未完成な状態のものがあります。
たとえば前述のクラスRodentは「齧歯類」を定義していましたが、それが具体的に「何を」表すのかは決められておりません。ハムスターかラットなのか、あるいはカピパラなのかもわかりません。しかし、「齧歯類」という共通の目(もく)に属しているので、クラスとしてまとめてあります。このようなクラスを抽象クラス(abstract class)と呼びます。
abstract classの定義方法はabstractという修飾子が付く以外は、通常のクラスと同様です。ただし、メソッドについては中身が空の状態でこのままでは実装できません。あくまでも継承後のクラスに共通となるメソッドのひな形を示しているだけです。このため、abstract classを拡張したクラスでは、必要なメソッドについて中身を実装しなければなりません。
以下に、abstract classの例を示します。
abstract class Rodent() {
public String tailLength() {} // none, short, long
public boolean useHandWhenEat {} // true, false
// 以下必要に応じて齧歯類に共通するメソッドのひな形を定義
}
クラスを呼び出すときに、情報を渡して特定の処理を行わせることができます。この処理を記述した部分を、constructorと呼びます。constructor名は、constructorを含むクラス名と同じでなければいけません。さもなければ、コンパイル時にエラーが発生します。
以下に、constructorの記述方法を示します。
constructor名(渡される情報の形式 情報を格納する変数名){}
括弧「()」内に、渡される情報の形式とその情報を格納する変数名を記述します。
中括弧「{}」内には、実際に実行される処理を記述します。
たとえば、以下のようなクラスがあるとします。
class Rat {
Rat(Food food){
if (food == cheese){
feels happy;
} else if (food == garbage) {
feels sad;
}
}
}
このとき、形式Foodは食べ物を表し、変数foodへと格納されます。
ここで実際に、別のクラスから以下のようにクラスRatを呼び出してみます。
Rat rat;
rat = new Rat(cheese);
好物のcheeseを与えられたRatは、「feels happy;」の一文が呼び出されて喜びます。反対に、
Rat rat;
rat = new Rat(garbage);
garbage((生)ごみ)を与えられた場合、「feels sad;」が呼び出され、Ratはがっかりします。さらにここで、
Rat rat;
rat = new Rat(wheel);
とした場合、wheel(回し車)はFood(食べ物)ではありませんから、型が不一致となりコンパイル時にエラーとなります。
constractorはクラスが呼び出された時点で実行されることに注意してください。これはつまり、わざわざインスタンス化する必要がないということを意味しています。もちろん、インスタンス化しても構わないのですが、その分余計にメモリを消費するので避けた方が無難です。
methodは、必要に応じて呼び出すことのできる機能で、クラスの中で宣言されます。methodが呼び出されると、method内に記述された処理を行い、その結果を値として返します。methodの宣言方法は、以下のとおりです。
method名 返す値の形式 () {}
methodはまた、特定の処理のみを行って値を返さない場合があります。このとき、返す値の形式の代わりに、voidを指定します。
method名 void () {}
よくわかっていないのでこの項修正予定。
mainは特殊なmethodでクラスの実行時に呼び出されます(逆に、実行されるクラスファイルには、必ずmethodとしてmainが存在しなければなりません)。methodのmainは必ず、publicかつstatic、そしてvoidとして宣言されなければなりません。また、文字の配列を引数に持たなければなりません。
methodの宣言方法は、以下のとおりです。
public static void main (String[] 変数名)
変数名は、一般的に「args」が用いられることが多いようです。
クラス内部で定義される変数。
前述の通り、Javaでは一度に一つのクラスしか拡張することしかできません。前述の例でいえば、クラスRatを作成するのに、抽象クラスRodentを拡張しました。抽象とはいえRodentも立派なクラスですから、これに加えて別のクラスを拡張することはできません。
ここで、尻尾というメソッドについて考えてみましょう。尻尾は齧歯類だけに限らず、その他の動物も持っています。では、Rodentsの親クラスとしてAnimalを作り、そこに尻尾のメソッドを持たせてみるのはどうでしょうか? Animalはその名の通り、あらゆる動物に対する親クラスとなります。動物の中には当然、尻尾を持たないものも存在します。これではクラスAnimalに尻尾のメソッドを持たせるわけにはいきません。
このような経緯から用意されたのが、インターフェイスです。インターフェイスには様々なクラスで汎用的に使うことが想定されるメソッドが定義されており、クラスに組み込んで使用できます。また、クラスのような制限はなく、必要に応じていくつでもインターフェイスを組み込むことが可能です。
インターフェイスの定義はクラスとほとんど変わりません。単に、classの部分がinterfaceになっただけです。具体的には以下のように記述します。
public interface Tail {
public boolean exists();
public String length();
// 以下必要に応じてメソッドを定義
}
メソッドをクラスに組み込むにはimplementsを使用します
注意してもらいたいのは、インターフェイスのメソッドはすべて抽象メソッドであるということです。このため、インターフェイスを組み込んだクラスでは、メソッドを実装しなければなりません。
インターフェイスの実装例を以下に示します。
public class Rodents imprements Tail {
public boolean exists(){
return true;
}
public String length(){
return("long"); // ハムスターとかはほとんど尻尾ないですが。。
}
// 以下抽象メソッドを実装
}
Javaでは関連性の高いものはクラスとそのクラスから派生するサブクラスという具合にまとめて、複数のクラスにまたがって共有できるものをインターフェイスとして定義することが多いようです。
ネズミの例でいうとクラスはAnimal(動物)、Mammalia(ほ乳類)、Rodent(げっ歯類)、Rat(ラット)といった感じで継承していきます。上記の「尻尾」などはネズミ以外の動物にもあるので、インターフェイスとするのが良いでしょう。
なお、クラスとは違い、インターフェイスは一つのクラスに複数属することができます。
public class Rodents imprements Tail, Hands {
// 何か処理
}
明らかなプログラミングのミス(誤記等)はコンパイル時にエラーとなりますが、プログラムとして問題はなくても実際に実行するとエラーが発生する場合があります。たとえば、プログラム中で指定したファイルを読み込むことができなかった場合などです。
このように発生する障害があらかじめ予想できる場合、前もってプログラム内でどのように処理するかを記述しておくことができます。これを、障害処理と呼びます。障害処理では、障害の発生が予測できる箇所をtry{}でくくり、実際に障害が発生した際に実行される処理をcatch{}でくくります。
実際の書式は、以下のとおりです。
try{
障害の発生することが予想される処理
} catch (発生が予想される障害名 発生した障害名を格納する変数名) {
障害発生時に実行する処理
}
実際にcatch{}で記述した障害が発生すると、その障害名が発生した障害名を格納する変数名に代入されます。ですから仮に、変数名をerrorとしておくと、
System.out.Println(error);
と記述すれば、発生した障害名を知ることができます。
なお、障害はひとつだけでなく、複数発生することが予想される場合もあります。このため、catch{}節は、複数記述しておくことが可能です(逆に、ひとつのcatch{}節にはひとつの障害処理しか記述しておくことができません)。
なお、障害の発生いかんにかかわらず、常に実行したい処理がある場合は、catch{}節の後にfinally{}と続けて、その中に処理を記述しておきます。
メール(英小文字):nec@pcfx.jp