A-1 Java言語の基本文法

 Java言語の文法は非常にシンプルで首尾一貫したものです。 しかも C や C++ の記述法を可能な限り導入しているため、 覚えるための労力は最小限ですみます。 以下の項目といくつかのサンプルのソースに目を通せば、 文法的な部分で困難を感じることはほとんどないでしょう。

A-1-1 予約語と演算子

キーワード

 Java言語では以下の50語がキーワードとして予約されています。 これらをグループに分けて示します。 これらの意味および目的は後の節で詳しく説明します。

 これらの他に const, goto の3語は、使用されませんが予約はされています。 したがってプログラマーが独自の用途に使用することはできません。

演算子

 Java言語には、四則演算(剰余算も含む)、論理演算、ビット演算などを 実行する演算子が定義されています。 演算子は1文字もしくは2文字の記号からなります。

二項演算子 《+》 は、Stringクラスのデータに対して文字列の連結を 行う機能もあります。 Stringクラスのデータと数値や論理値を + で結合すると 数値や論理値は自動的に Stringクラスに変換されます。

区切り記号

数値表現

 整数は10進、8進、16進の形式で記述が可能です。 0で始まる整数は 8進数、0x または 0Xで始まる整数は 16進数と見なされます。 16進数表現の abcdef の各文字は大文字であっても小文字であってもかまいません。 0 で始まらない数値は 10進数と見なされます。
 32ビットの範囲を越えない数値は自動的に int型として扱われます。 long 型の数値であることを明示するには、3L または 3l のように 末尾に L または l を付けます。

 小数の表現は通常の形式の他に e または E で 10の累乗部分を示すことができます。 たとえば 4.0e10 のように書きます。
 小数の 末尾に f または F を付けると float型、 末尾に d または D を付けると double型、 と見なされます。末尾に何も付いていない場合は double型と見なされます。 末尾に何も付かない小数(double型として扱われる)を float型の変数に代入しようとするとエラーになるので注意してください。

括弧

 Java言語では3種類の括弧が用いられます。 意味は C 及び C++ と同じです。 いずれの括弧もネストすることができます。

引用符

 Java言語では2種類の引用符が用いられます。 意味は C 及び C++ と同じです。

コメント

 Java言語では3種類のコメントが用いられます。

A-1-2 型とクラス

原始型

 Java言語には、数値及び論理値の型とvoidの計9種類の原始型が用意されています。 これらはクラスではありません。

 数値の型は以下の7種類です。 意味は C 及び C++ のそれにほぼ対応します。 ただし Java言語の場合、short,int,long のサイズはマシンに依存せず 常に固定です。char は2バイトです。 数値は常に符号付き(2の補数表現)です。unsigned の宣言は存在しません。 数値の型とサイズは以下の表のとおりです。

Java言語の数値型
byte short char int long float double
1バイト 2バイト 2バイト 4バイト 8バイト 4バイト 8バイト

 boolean型は論理値のデータを扱う型です。 値は true もしくは false です。 Java言語では論理値を、その実装である数値のデータとしてアクセスすることは できません。(キャストは許されません。)

 void型は実体が存在しない型です。 メソッドの返値が存在しないことを示すために用います。 Java言語には、いわゆる「プロトタイプ宣言」はありません。 メソッドの引数がないことを void で記述する必要はありません。

class と interface

 class および interface は、新しい型のデータを定義するキーワードです。
 class(または interface )に続く文字列がクラス名、 それに続くブロックがクラス定義と解釈されます。
インターフェイスは、実装を持たないように制限を受ける特殊なクラスです。 インターフェイスの定義のブロック内には、static変数と abstractメソッド しか存在できません。

 クラスの宣言の形式の例を以下に示します。


class NewClass {

}

extends と implements

 extends と implements はクラスの継承を実現するためのキーワードです。
 extends は新しいクラス定義のクラス名の後に現れます。 extends の直後の文字列は、ベースとなるクラス名と解釈されます。 新しいクラスは、この指定されたクラス(インターフェイスであってもよい)の 拡張クラスとして定義されます。
 class で新しいクラスを定義する場合、 extends の後に指定できるクラス名は1つだけです。 interface で新しいインターフェイスを定義する場合、 extends の後に指定できるインターフェイス名は複数あってかまいません。 インターフェイス名の間は , で区切ります。
 すべてのクラスは Object という共通の先祖のクラスを持っています。 extends が存在しない場合、先祖のクラスが存在しないという意味ではなく、 extends Objetct を省略したものとして扱われます。

 implements も新しいクラス定義のクラス名の後に現れます。 implements の後の文字列は、組み込まれるインターフェイス名と解釈されます。 implements の後に指定できるのは、 インターフェイスとして定義されたものに限ります。 (通常のクラスを指定するとエラーになる)
 implements の後に来るインターフェイス名は、複数あってもかまいません。 インターフェイス名の間は , で区切ります。
 Java言語では、implements の機能を通じて多重継承を実現します。

 継承を行うクラスの宣言の形式の例を以下に示します。


class NewClass extends OldClass implements Interface1,Interface2 {

}

this と super

 this はクラス定義内で、そのクラスのオブジェクト自身を表すための キーワードです。
メソッドの引数として自分自身のオブジェクトを渡したり、 ローカル変数との名前の衝突を解消する場合などに用いられます。

 super はクラス定義内で、そのクラスのスーパー・クラスの機能を 呼び出すために用いるキーワードです。 super.init() のようにして、スーパー・クラスで定義されたメソッドを 呼び出すことを可能にします。 super() はスーパー・クラスのコンストラクタを呼び出します。

new と null

 new は新しいオブジェクトのデータを生成するキーワードです。 new の後には生成したいオブジェクトのコンストラクタが来ます。
 Java言語では配列のサイズを指定する時にも new が必要です。 なぜなら配列もクラスだからです。


int a[];  と  int[] a;

は同じ意味です。int[] がクラス名と考えればわかりやすいでしょう。 new の後のサイズの指定は次のような形式です。


 a = new int[10];

これは、「配列のクラスのコンストラクタが int[] であり、 それに引数 10 を与えてオブジェクトを生成している。 引数はサイズを記憶する変数の値である。」と解釈してください。
 実際、配列のサイズはどの配列のクラスでも length という public 変数に 格納されています。したがって、上で定義した配列 a のサイズは次のようにして いつでも知ることができます。


 int count = a.length;  // 値 10 が返る

 null は「オブジェクトが存在しない」ことを表す特別な値です。 どのクラスのオブジェクトにも代入することができます。 (ただし原始型のデータには代入できない。) 宣言だけで生成されていないオブジェクトの値は null に初期化されています。 Java言語ではメモリの解放は明示的に行われません。 不用になったオブジェクトは、 デストラクタに相当するメソッドを呼び出して処理する必要はありません。 単に null を代入するだけでオブジェクトと変数の間の繋がりはなくなります。 どの変数とも繋がりを持たなくなったオブジェクトは、 自動的にガーベッジ・コレクションの対象となります。

 オブジェクトの変数はクラスの型を指定して宣言し、 クラスのコンストラクタを用いて生成します。 したがって通常は、オブジェクトが何という名前のクラスなのかは自明です。 しかし外から引数で渡されてきたオブジェクトのような場合には、 それが何というクラスのオブジェクト(インスタンス)であるか チェックしたい場合も生じます。
 instanceof はそのような時に使用するキーワードです。 instanceof は boolean 型の値を返す2項演算子です。


 if( objetcname instanceof ClassName )
先頭の項のオブジェクトが 後の項のクラスに所属するオブジェクト(インスタンス)であれば true, そうでなければ false が返ります。

import文 と package文

 import と package はクラス定義の外で用いられるキーワードです。

 importは、バイトコードをクラスライブラリ内で探索するための パッケージの情報を与えます。 パッケージの階層構造はファイルシステムのディレクトリ構造に対応します。 java.awt.image.ImageObserverのように各ディレクトリの区切りを ≪ .≫で表します。 探索のルート・ディレクトリはクラスパスで指定されたディレクトリです。 (デフォルトは、カレント・ディレクトリと JDKの標準クラスライブラリが 置かれたディレクトリ) 注意:

  1. パッケージの階層構造(ディレクトリ構造)は クラスの親子関係の階層とは全く無~関係です。
  2. JDK1.02 ではクラス・ライブラリは アーカイブ形式に圧縮して使用するのが普通です。 したがってディレクトリ構造を実際のファイル・システムの中で見ることはできません。
  3. importはロードを実行させる命令ではありません。 探索の情報を提供するだけです。 importを使用する代わりにソース・コードの中で パス名を直接指定してもかまいません。次の2つ例は同じ意味です。

/* import を使った場合 */
import java.applet.Applet;
public class NewClass extends Applet {
                :

/* import を使わない場合 */
pulic class NewClass extends java.applet.Applet {
                :

 packageは、クラスあるいはコンパイル単位が所属するパッケージ名を 指定するのに用います。 クラスの集まりをライブラリとして名前を付けて管理し、 再利用しやすくするための機能です。 小規模のプログラムでは必須の命令ではありません。 しかし開発対象が大きくなってきたら、 クラス名の衝突などを防ぐ意味でも package文が必要になってきます。 package 命令は他のすべての命令より先に置かれなくてはいけません。

A-1-3 変数とメソッド

変数 と メソッド

 クラス定義の中に存在できるデータは、変数(valiable)とメソッド(method)です。
 変数は、原始型のデータもしくはクラスのオブジェクト、 もしくはそれらの配列です。 オブジェクトが存在している間は、変数もずっと存在します。
 メソッドは一連の手続きのブロックです。 メソッドの型と返値、引数の記述の方法は C の関数から引き継いだ形式に従います。 ただし返値の型、引数の型と個数は厳密にチェックされます。 C++ と同じように、 それらが一致しない場合は異なるメソッドとして扱われます。
 メソッドの内部でローカルな変数を宣言することも可能です。 ただし、そうしたローカル変数はメソッドの処理のたびごとに 生成と消滅を繰り返すことになります。 もちろんメソッドの外部から参照することはできません。 以下で説明する「変数」とはローカルな変数のことではなく、 クラス内のメソッドから共通して参照でき、 オブジェクトと同じ「寿命」を持つ変数のことです。

アクセス・モード

 クラス自身、 クラス内の変数およびメソッドは、 必ずいずれかのアクセス・モードを持たなければなりません。 アクセス・モードのキーワードは、C++ と同じです。

 この他に「デフォルトのアクセスモード」という概念があります。 アクセスモードを明示的に記述しなかった場合は、 同じパッケージ内のクラスからのみアクセスが可能になります。

 アクセス・モードを指定した変数とメソッドの例


 private int x, y;
 public int getX(){ return x; }

 private や protected で制限を受けるのは、object.x という形式のアクセスです。 上の例の場合 object.x は許されませんが、getX() メソッドが public なので、 このメソッドを通して値を取り出すことは許されます。 同様に値を設定するメソッドを public で提供することもできます。 あるいは敢えてそれを行わないことで、データ x を「読み出し専用」のデータとして 扱うこともできます。

修飾子 static と final

 Java言語では型とアクセス・モードの他に、 変数及びメソッドの性質を指定する修飾子が何種類か存在します。 static と final は、それらの中でも最も頻繁に用いられるキーワードです。

 static として宣言された変数はオブジェクトごとに生成されません。 そのクラスのオブジェクトすべてに共通して利用されます。 static な変数は、static でない変数よりも先に初期化されています。 (このため変数の初期化の順番が、プログラムの見かけとは 異なって見える場合があるので注意してください。) そのクラスの中の static変数しか対象として取り扱わないメソッドは、 staticメソッドとして宣言することができます。 (他のクラスのオブジェクトを生成したり、取り扱ったりすることはかまわない。) staticメソッドの中で、そのクラスの staticでない変数や staticでない他のメソッドを利用しようとすると、 コンパイル時にチェックされエラーとなります。
 static な変数およびメソッドは、オブジェクトを生成しなくても利用できます。 アクセスするには次のようにクラス名を直接使います。 (オブジェクトを生成した場合は、そのオブジェクト名で指定してもよい。)


  Color.red;  // 赤色を表す static なデータ

 final は変数ならば値の変更を許さないこと、 メソッドであれば再定義を許さないこと、 クラスであればサブ・クラスの定義を許さないことを示します。
final で宣言された数値は定数となります。これは C 及び C++ の const 宣言 と同じ機能を提供します。 final の変数をサブ・クラスの中で再定義することは許されます。

 static と final の概念は互いに独立です。 しかし、クラス固有の定数を定義する目的で static かつ final な 変数がしばしば用いられます。


 public static final int BUFSIZE=1024;

修飾子 abstract と native

 abstract と native は、いずれもクラス内に実装を持たない メソッドを宣言するためのキーワードです。 これらの修飾子が付いたメソッドは、名前のみで定義のブロックが存在しません。


  abstract public void addSomething();
  native public void callSomething();

 abstract は、 サブ・クラスでメソッドが再定義され実装される予定であることを示します。
 native は、Java言語以外のモジュール内にメソッドの実装が存在することを 示します。

 abstract メソッドを1つでも含む(もしくは再定義せずにそのまま継承した) クラスは、やはり abstract として宣言しなくてはいけません。 abstract なクラスはオブジェクトを生成することはできません。

修飾子 synchronized と volatile

 sysnchronized と volatile修飾子は、 複数のスレッドによって同一のデータが非同期にアクセスされ 処理に混乱や矛盾が生じるのを防ぎます。

 sysnchronized は特定のオブジェクトとともに指定したブロック、 またはメソッドに対して指定します。 ブロックの場合は synchronized の指定を持つ処理が終わるまで 指定したオブジェクトにロックがかかります。 synchronized の指定を持つメソッドは、そのメソッドを定義したオブジェクト自身に ロックをかけ、メソッドの処理が終わるまで他のスレッドから 内部のデータに同時にアクセスされることを防ぎます。
 volatileは変数に対して指定します。 volatileの指定を受けた変数は、メモリーからのロードと格納の タイミングについて制限がかけられ、 複数のスレッドの処理による混乱から保護されます。

A-1-4 制御構造と例外処理

ブロックとローカル変数の寿命

 Java言語の制御構造とそのキーワードは、C 及び C++ とほとんど同じです。 プログラムは完全に構造化されています。 処理の単位は 《{》 と 《}》 で囲まれた、クラス定義、メソッド、ブロックです。 ブロックは何重にも階層構造を作ることができます。 ブロックは制御構造のキーワード for, while, do, if, else, switch 、 例外処理のキーワード try, catch と結びつきます。 これらのキーワードの後のブロックが単独の文もしくは 単独の他のブロックからなる場合は、 《{》 《}》 を省略することができます。 またキーワードなしで単にブロックを作ることもできます。
 メソッドおよびブロック内で宣言されたローカルな変数は、 その内部のみでアクセスが可能です。 処理がメソッドおよびブロックから抜けると、 ローカル変数は消滅します。 したがってローカルな変数にアクセスモードや修飾子を指定しても 無意味です。 ブロックの外の変数とローカル変数の名前が衝突した場合は、 ローカル変数だと解釈されます。

繰り返しの処理

 繰り返しの処理の対象となるブロックを作るキーワードは、 for, while, do の3つです。

 for はそれに続く《(》 と 《)》 の中に 《;》 で区切られた3つの文を持ちます。


  for( int i=0; i<100; i++ ) {
  }

それぞれの意味は、初期化の処理、繰り返しを続けるかの判定条件、 繰り返しが行われたたびに行う処理です。 上の例のように、初期化の処理の中でブロック内で有効なローカル変数を 宣言することができます。

 while はそれに続く《(》 と 《)》 の中に、 繰り返しを行うかどうかの判定条件を持ちます。


  while( i<100  ) {
  }

 do の判定条件の文はその直後には来ません。 ブロックの後に置かれた while の文で条件を判定します。


  do {
  }
  while( i<100  )

 do で始まるブロックは、最低1回は必ず呼び出されます。

 continue 文はブロック内の処理を強制的に中断し、 次の繰り返しの処理に移ることを意味します。
 繰り返しのブロック自身から強制的に抜け出すには break 文を使います。 break によって処理は1つ外側のブロックに抜け出します。 多重のブロックを一度に抜け出すためには、 ブロックにラベル付けをし、そのラベル名を指定する break文を使います。 (Java言語に goto 文はありません。)


  maketable: for( int i=0; i<100; i++ ) {
                for( int j=0; j<100; j++ ) {
                          :
                     break  maketable;
                          :
                }
             }

条件分岐

条件分岐を作るキーワードは if と switch です。

if は対応する else を持つことができます。else if をつなげることで、 複数の処理の分岐を行うことも可能です。


if( i<100 ){
}

if( i<100 ){
}
else {
}

if( i<100 ){
}
else if( i<50 ){
}
else{
}

 switch は、その後の《(》 《)》 内の値によって複数の処理に分岐を行います。 switch のブロック内には case の後に指定された値ごとに処理を置く ことができます。 該当する値の処理が存在しない場合の処理は、default: の後に置きます。


switch( value ){
        case 1:   break
        case 2:   break
        default:  break
} 

 switch のブロックは単独のブロックです。 それぞれの分岐した処理を終えるには break 文でブロックから抜け出す必要が あります。
 break 文は if のブロックは対象としません。 その外側のブロックに対しての break と解釈されます。 (そうしないと break するかどうかの条件判定ができなくなってしまうからです。)

 if と switch の他に、記号≪ ? ≫による条件判定も使用できます。 使い方は C と同じです。≪ ? ≫の前にある boolean型の値を判定条件とし、 true ならば≪ ? ≫直後の命令を、false ならば≪ : ≫で区切られた 後の命令を実行します。


  ( i > 0 ) ? j = i : j = (-i); 

例外の検出と処理

 例外を検出するためのキーワードが try です。 try はブロック内の処理で発生した例外をすべて検出し、 後に続く catch のブロックのうちの該当する処理を呼び出します。
 例外の処理を記述するためのキーワードが catch です。 catch の後の《(》 《)》 内には検出された例外が渡されます。 例外はクラス名によって処理を選択するのに持ちいられるのと同時に、 そのオブジェクトは例外処理内でデータとして利用される引数でもあります。 例外のクラスとは Exception またはそのサブ・クラスです。
 try と catch はブロック単位である点は if と else の関係と似ています。 場合分けのスタイルはむしろ switch と case の関係に似ています。 try に対して最低1つは catch が必要です。 キーワード finally のブロックは、 try ブロックの処理に伴って必ず呼び出されます。 該当する例外のクラスが見つからなかった 場合の処理を書けるのは、 switch case の default に似ています。ただし、 finally の内容は catch の処理が実行された場合でも必ず呼び出される点が 違います。 また、try のブロックの処理が return, break, continue によって 途中で終わった場合にも呼び出されます。


try {
}
catch( Exception1 e ){
}
catch( Exception2 e ){
}
finally{
}
例外の発生

 例外が発生したことを知らせるためのキーワードが throw です。 throw は try の中から呼び出されるメソッドの側に記述されます。 throw を行うメソッドは、その宣言部分で どんな例外を渡すのかキーワード throws によって宣言します。


public method() throws SpecialExeption {

       throw new SpecialException("Error message");
}

throw で渡されるのは例外クラスのオブジェクトです。 (クラスではありません。) 上の例のように throw の前にコンストラクタを呼び出して 例外のオブジェクトを生成する必要があります。