Javaの文法(Javaのぶんぽう)の記事では、プログラミング言語Javaの構文(シンタックス、英: syntax)について解説する。また、それ以外についても解説している。
Javaの基本言語仕様は54種類程度に抑えたキーワードによって比較的コンパクトにまとめられている。Javaの構文はC++言語によく似たものであり、それよりも比較的平易化されている。従って以下のキーワードを眺めるだけでもJavaプログラミングの大まかなスタイルを掴むことができる。
類別 | キーワード | 説明 |
---|---|---|
アクセス修飾子 | import |
パッケージ名前空間の解決用 |
private |
同クラス内でアクセス可 | |
package |
同クラス内と同パッケージ内でアクセス可、パッケージスコープの宣言 | |
protected |
同クラス内と同パッケージ内と派生クラス内でアクセス可 | |
public |
全範囲でアクセス可 | |
クラス定義系 | abstract |
抽象クラス、抽象メソッド |
class |
クラス | |
enum |
列挙型 | |
extends |
スーパークラスの指定 | |
final |
クラスの継承不可、メソッドのオーバーライド不可、フィールドの定数化 | |
implements |
実装するインターフェース | |
interface |
インターフェース | |
native |
そのメソッドは他言語コンパイルコードで実装される | |
static |
静的フィールド、静的メソッド、静的内部クラスはトップレベル扱いされる | |
strictfp |
指定クラス、指定メソッドで厳密な浮動小数点計算が行われる | |
synchronized |
同期用クラス、同期用メソッド、同期用ブロック | |
transient |
直列化から除外されるフィールド | |
void |
返り値無しのメソッド | |
volatile |
そのフィールドは各スレッドからキャッシュ参照されない | |
制御構文系 | break |
ループ脱出 |
case |
パターンマッチ項目 | |
continue |
ループ起点回帰 | |
default |
パターンマッチ外 | |
do |
無条件ループ起点 | |
else |
条件式の偽側フロー | |
for |
初期値定義と周回毎値変化で修飾された条件式ループ | |
if |
条件式の真側フロー | |
instanceof |
インスタンスの実行時型チェック | |
return |
メソッドの評価終了 | |
switch |
パターンマッチ起点 | |
while |
条件式ループ | |
例外処理系 | assert |
条件式が偽ならプログラム中断 |
catch |
例外パターン捕捉 | |
finally |
例外デフォルト捕捉 | |
throw |
例外発生 | |
throws |
そのメソッドで発生する例外候補 | |
try |
例外発生ブロック | |
基本値系 | boolean |
真偽値 |
byte |
8ビット値 | |
char |
文字表現用16ビット値 | |
double |
64ビット浮動小数点 | |
float |
32ビット浮動小数点 | |
false |
偽値 | |
int |
符号付き32ビット値 | |
long |
符号付き64ビット値 | |
short |
符号付き16ビット値 | |
true |
真値 | |
null |
ヌルポインタ | |
参照値系 | super |
スーパークラスのインスタンス |
this |
カレントクラスのインスタンス | |
new |
インスタンスの生成 | |
使用できない | goto |
|
const |
Javaのデータ型には大別して、プリミティブ型と参照型がある。
primitive data type[1]もしくはprimitive type[2]とも。 プリミティブ型はオブジェクトではなく、スーパークラスを持たない。
byte | 8ビット符号付き |
---|---|
short | 16ビット符号付き |
int | 32ビット符号付き |
long | 64ビット符号付き |
Integer.MAX_VALUE
(= 231−1) に1を加えると、結果はInteger.MIN_VALUE
(= −231) になる。float | 32ビット符号付き |
---|---|
double | 64ビット符号付き |
float f = 2147483647f;
double d = 9223372036854775807;
float型は数字を符号付き32Bitの最大値(2,147,483,647)まで代入できるが[3]、double型は符号付き64Bitの最大値(9,223,372,036,854,775,807)まで代入できる。[4]
また、float型は代入する数字の末尾に「F」または「f」をつけなければdouble型とみなされ、コンパイルエラーになってしまう。[5]
char | 16ビット符号無しUnicode |
---|
String
クラスやプリミティブラッパークラスであるCharacter
クラスなどの各種メソッドが利用できる。boolean | true または false |
---|
すべての参照型はオブジェクト型を表すクラスObject
から派生する。オブジェクトは参照型のインスタンスである。クラスclass
は既定でObject
から派生する参照型である。列挙型enum
は抽象クラスEnum
から暗黙的に派生する参照型である。
リフレクション機能のための「クラスを表現するクラス(メタクラス)」としてClass
が存在するが、クラス自体はオブジェクトではない。
String
オブジェクトは不変(変更不能)である+
と +=
は文字列を連結するためにオーバーロードされるString str1 = "alpha";
String str2 = new String("alpha");
StringBuffer
とStringBuilder
オブジェクトは可変(変更可能)なので、オブジェクト生成オーバヘッド無しで柔軟に文字列を生成・変更できる。StringBufferとStringBuilderの違いは、StringBufferがマルチスレッドに対応している(=スレッド・セーフである)のに対し、StringBuilderは対応していないことである。StringBuffer str1 = new StringBuffer("alpha");
str1.append("-meta");
str1.setCharAt(str1.indexOf("m"), 'b');
System.out.println(str1); // str1.toString() を呼び出す。
// 印字結果は"alpha-beta"となる
Object
のサブクラスつまり参照型である。length
」を持つ。// 配列を宣言 - 配列名は「myArray」、要素の型は "SomeClass" への参照
SomeClass[] myArray = null;
// 配列を生成
myArray = new SomeClass[10];
// または宣言と生成を同時に行う
SomeClass[] myArray = new SomeClass[10];
// 配列の要素を割り当てる (ただし、基本型の配列なら必要ない)
for (int i = 0; i < myArray.length; i++)
myArray[i] = new SomeClass();
リテラルの例を示す。
データ型 | 例 |
---|---|
byte 、short 、int 、long
|
|
float
|
|
double
|
|
char
|
|
String
|
|
Javaは1バイト整数の型(byte)と文字の型(char)とを区別する。なお、charは、名前に反して必ずしも文字を表現していない場合があり得る。すなわち、実際にはUCS-2(UTF-16)による、いわゆるダブルバイトである。J2SE 5.0以降ではサロゲートAPIによって、サロゲートペアに対応した。
JavaプログラムのソースコードおよびソースファイルはUTF-8でエンコードするのが基本である。Unicodeに対応したエンコードであれば、Javaプログラムのソースコードに直接Unicode文字を記述することもできる。なお、-encoding
コンパイルオプションを指定することで、対応する任意のエンコードを使用することもできる。ロケール依存のコードページ(日本語版Microsoft Windowsで使われているCP932等)を利用することもできる。
文字列リテラルやコメントの他、クラス名や変数名も国際化に対応している。例えば、次のソースコードはJavaのコードとして正しく解釈されコンパイルされ実行できる。ここではクラス名、変数名、および文字列リテラルとして日本語の文字を使っている。
public class こんにちは世界 {
private String 文字列 = "こんにちは世界";
}
表記 | 意味 |
---|---|
+
|
加算 |
-
|
減算 |
*
|
乗算 |
/
|
除算 |
%
|
剰余 (整数の余りを返す) |
表記 | 意味 |
---|---|
-
|
単項マイナス (符号反転) |
++
|
インクリメント (変数の前か後につけることができる) |
--
|
デクリメント (変数の前か後につけることができる) |
!
|
ブール補数演算 |
~
|
ビット単位反転 |
(型名)
|
キャスト |
表記 | 意味 |
---|---|
=
|
代入 |
+=
|
加算と代入 |
-=
|
減算と代入 |
*=
|
乗算と代入 |
/=
|
除算と代入 |
%=
|
剰余と代入 |
&=
|
ビット演算 ANDと代入 |
|=
|
ビット演算 ORと代入 |
^=
|
ビット演算 XORと代入 |
<<=
|
左シフト(ゼロ埋め)と代入 |
>>=
|
右シフト (符号拡張)と代入 |
>>>=
|
右シフト (ゼロ埋め) と代入 |
表記 | 意味 |
---|---|
==
|
左辺の数値は右辺の数値と等しい。左辺のインスタンスは右辺のインスタンスと同一である。 |
!=
|
左辺の数値は右辺の数値と等しくない。左辺のインスタンスは右辺のインスタンスと同一ではない。 |
>
|
左辺の数値は右辺の数値より大きい。 |
>=
|
左辺の数値は右辺の数値と等しい、または、より大きい。 |
<
|
左辺の数値は右辺の数値より小さい。 |
<=
|
左辺の数値は右辺の数値と等しい、または、より小さい。 |
instanceof
|
左辺のインスタンスのクラスは右辺のクラスまたは右辺のクラスのサブクラスである。 |
関係演算子(==と!=)を参照型に対して用いた場合、そこで比較されるのは参照先のオブジェクトが同じかどうかであり、オブジェクトの中身の値が一致するか否かではない。オブジェクトの中身を比較したい場合はObject.equals(Object)
メソッドを使用する。instanceof演算子は、オブジェクトが指定クラスのインスタンスであるか否かを判定するために用いる。
三項演算子は二つの記号?
と:
を組み合わせて記述する。条件演算子とも呼ぶ。構文は以下である。
条件 ? 式1 : 式2
条件がtrue
であるとき、式1の値をとる。そうでない場合は式2の値をとる。
例:
String answer = (p < 0.05) ? "reject": "keep";
// これは以下のコードと等価である:
String answer;
if (p < 0.05) {
answer = "reject";
} else {
answer = "keep";
}
表記 | 意味 |
---|---|
&&
|
論理積(左のオペランドが false のとき、式は false を返し、右のオペランドは評価されない)
|
||
|
論理和(左のオペランドが true のとき、式は true を返し、右のオペランドは評価されない)
|
!
|
論理否定 |
文法 | 意味 |
---|---|
&
|
ビット積 |
|
|
ビット和 |
^
|
ビット排他的和 |
<<
|
左シフト(ゼロ埋め) |
>>
|
右シフト(符号拡張) |
>>>
|
右シフト(ゼロ埋め) |
表記 | 意味 |
---|---|
~
|
ビット反転 |
表記 | 意味 |
---|---|
+
|
連結 |
+=
|
連結と代入 |
if (expr) { statements; } else if (expr) { statements; } else { statements; }
switch (expr) { case VALUE1: statement11 ; … ; statement1L ; break ; case VALUE2: statement21 ; … ; statement2M ; break ; default: statementd1 ; … ; statementdN ; }
expr
値の型はbyte
/short
/int
/char
あるいはそれらのプリミティブラッパークラスでなければならない。
for (initial-expr; cond-expr; incr-expr) { statements; }
J2SE 5.0では、for-each文[8]と呼ばれる新機能が追加された。これは集合の中の全要素を順番に参照するような処理を大いに簡素化する。このような場合、従来は次の例に示すような反復子 (iterator) を書かねばならなかった:
// Java 5.0以前にはジェネリクスもないため、実際にはさらに煩雑となる。
public int sumLength(Set<String> stringSet) {
int sum = 0;
Iterator<String> itr = stringSet.iterator();
while (itr.hasNext()) {
sum += itr.next().length();
}
return sum;
}
for-each文はこのメソッドを大いに簡素化する:
public int sumLength(Set<String> stringSet) {
int sum = 0;
for (String s : stringSet) {
sum += s.length();
}
return sum;
}
この例の動作としては、stringSet
に含まれる全てのString
について、長さを取得してsum
に加算する。
while (expr) { statements; }
do { statements; } while (expr);
文法 | 意味 |
---|---|
break; | 最も深いループから直ちに脱出する。 |
continue; | ループの現在の回を中断し、次の回の冒頭に移る。 |
break LABEL | ラベル付き文の実行を中断し、ラベル付き文の直後の文に移る。 |
continue LABEL | ラベル付きの文にジャンプする(ラベル付きの文またはラベル付きループを冒頭から再開する) |
int sum = 0;
for (int i = 1; i < 10; i++) {
if (i == 3) {
continue; // このループの残りをスキップしforに戻る。
}
sum += i;
if (sum > 15) {
break; // ループを脱出する。
}
}
例:
LABEL1: statement; LABEL2: { statements; }
Javaのキーワードには「goto」という綴りも含まれているが、goto文は無い(他にもうひとつ、constも同様に、キーワードとして予約されているが、予約されているだけで使われていない)。
Javaではクラスあるいはインタフェースの内部で別のクラスを宣言することができる。これは「ネストされたクラス」(nested class) と呼ばれる。ネストされていないクラスは「トップレベルクラス」(top-level class) と呼ばれる。ネストされたクラスがstatic
修飾されていない場合、「内部クラス」(inner class) となる。内部クラスは外側のクラスのインスタンスを暗黙的にキャプチャすることで、静的メンバ、非静的メンバいずれにもアクセスすることができる。ネストされたクラスがstatic
修飾されている場合、「静的クラス」(static class) となり、静的メンバのみにアクセスできる。メソッドの内部にクラスを定義することもでき、これは「ローカルクラス」(local class) と呼ばれる。ローカルクラスでは、外側のローカル変数には読み取りアクセスのみできる。また、型の名前を持たないローカルクラスとして「匿名クラス」(anonymous class) を定義し、同時にインスタンス生成をすることもできる。
クラスあるいはインタフェースの内部で別のインタフェースを宣言することもできる。これは「ネストされたインタフェース」と呼ばれる。
クラスを宣言する際は以下の修飾子を付けることができる:
abstract
(抽象) クラスだけがabstract
(抽象) メソッドを持つことができる。抽象クラスを継承する具象 (非abstract
) サブクラスは、引き継がれた全ての抽象メソッドを、abstract
でないメソッドでオーバーライドしなければならない。修飾子finalと併用することはできない。Javaのクラス定義ブロックはセミコロン;
で終わらせる必要はない点に注意。この点はC++の文法と異なる。
// 子クラスは親クラスを継承する
class ChildClass extends ParentClass { ... }
Object
クラスである。this.someMethod()
)。super.someMethod()
)。サブクラスがオーバライドした親クラスのメソッドや、サブクラスが継承しつつも隠蔽した親クラスのフィールドにアクセスするために使うことができる。インタフェースとは、実装の詳細がいっさいない抽象型である。その目的は複数のクラスを多態性によって統一的に扱うことである。共通のインタフェースを実装する複数のクラスは、そのインタフェース型のコンテキストにしたがって互いに交換可能とすることができる(リスコフの置換原則)。インタフェースはまた、抽象化 – クラスの実装方法を隠蔽すること – という考え方を強制するのにも役立つ。
インタフェースは抽象メソッドと定数フィールド (static finalフィールド) だけを含むことができる。インタフェースメソッドはデフォルトでpublicかつabstractであり(実装を持たない)、インタフェースフィールドはデフォルトで public static final である。インスタンスフィールドやクラスフィールドすなわち状態を持つことができない点が抽象クラスと異なる。
Javaは完全な直交の多重継承はサポートしておらず、インタフェースによる「型の多重継承」のみをサポートする。C++における多重継承(実装の多重継承)には、複数の親クラスや型から複数回継承したフィールドやメソッドを識別するための複雑なルールが伴う。インタフェースを実装から分離することにより、インタフェースはより単純かつ明快に多重継承が持つ利点の多くを提供する。もっとも、実装の多重継承を避ける代価としてコードは若干冗長になる。というのは、インタフェースはクラスのシグネチャを定義するのみで実装を持てないため、インタフェースを継承する全てのクラスは定義されたメソッドをいちいち実装しなければならないからである。純粋な多重継承であれば実装自体も継承されるのでこのようなことはない。
なお、Java 8ではインタフェースのデフォルトメソッドにより実装の多重継承を限定的にサポートするようになった。また、インタフェースが静的メソッドを持つこともできるようになった。
Javaのインタフェースは Objective-C規約のコンセプトによく似た振る舞いをする。
クラスは、一つのクラスを継承できるのに加えて、implementsキーワードを用いて一つ以上のインタフェースを実装することができる。
interface MyInterface {
void foo();
}
interface Interface2 {
void bar();
}
class MyClass implements MyInterface {
void foo() {...}
...
}
class ChildClass extends ParentClass implements MyInterface, Interface2 {
void foo() {...}
void bar();
...
}
以下の例では、Deleteable
インタフェースを実装する非abstract
クラスは、引数無しで戻り型がvoid
であるdelete
という名前の非抽象メソッドを定義しなければならない。そのメソッドの実装と機能は各々のクラスによって決定される。
public interface Deleteable {
void delete();
}
このコンセプトにはさまざまな使い道がある。例えば:
public class Fred implements Deleteable {
// このメソッドはDeleteableインタフェースを満足する
public void delete() {
// ここにコードを実装
}
public void someOtherMethod() {
}
}
public void deleteAll(Deleteable[] list) {
for (int i = 0; i < list.length; i++) {
list[i].delete();
}
}
上の配列に含まれる全てのオブジェクトはdelete()
メソッドを持つことが保証されるので、deleteAll()
メソッドはFred
オブジェクトと他の如何なるDeleteable
オブジェクトをも区別する必要がない。
インタフェースはextends
キーワードを用いて一つ以上のインタフェースを継承することができる。
interface ChildInterface extends ParentInterface, AnotherInterface {
...
}
結果として生じるインタフェースを実装するクラスは、元のインタフェースに含まれたメソッドをも併せて定義しなければならない。
public interface MyInterface {
foo();
}
public interface Interface2 extends MyInterface {
bar();
}
public class MyClass implements Interface2 {
void foo() {...}
void bar() {...}
...
}
アクセス修飾子は、そのクラスやクラスメンバにアクセス可能なクラスを決定する。
デフォルトでは、Javaのクラスは、それら自身のJavaパッケージからのみアクセスできる。これは、クラスのパッケージが、裏に隠れて機能を実行するようなAPIを提供することを可能とする。外にアクセスを公開されたクラスの動作を、隠されたクラスが支える形になる。
クラスメンバとはフィールド、メソッド、コンストラクタ、クラス内で定義されたネストされたクラスのことである。アクセス制限が厳しいものから並べると、クラスメンバのアクセス修飾子は次の通り。
private
宣言されたメンバはサブクラスによって引き継ぐことができない。メソッドをオーバライドする際、そのメソッドのアクセス権を「より厳しく」することはできない。さもなくば親クラスのインタフェース契約を壊してしまうからである。したがってオーバライドされる場合、publicメソッドはpublicとして宣言されねばならず、protectedメソッドをデフォルトアクセス権(修飾子省略)とすることはできない。しかしながら、メソッドをオーバライドしてアクセス権を「より緩める」ことは許される。したがってオーバライドする際、デフォルト (パッケージ) アクセス権のメソッドはprotectedまたはpublicとして宣言することができ、protectedメソッドはpublicとして宣言することができる。
アクセス修飾子に加えて、データフィールドは以下の修飾子によって宣言される:
static
なブランクfinalフィールドは最終的にスタティックイニシャライザによって初期化されなければならない。static
でないブランクfinalフィールドはコンストラクタの実行中に必ず初期化されなければならない。volatileにはなれない。staticとfinal両方を宣言されたフィールドは事実上、定数である。static
はそのフィールドがそのクラスにおいてただ一つのみ存在することを示し、final
はそのフィールドがただ一度のみ値を設定(初期化)可能であることを意味する。
「イニシャライザ」(初期化子)はフィールドのイニシャライザと同時に実行されるコードのブロックである。
「スタティックイニシャライザ」(静的初期化子)はstaticフィールドのイニシャライザと同時に実行されるコードのブロックである。静的フィールド初期化子と静的初期化子は宣言された順番に実行される。静的初期化はクラスがロードされた後で実行される。
static int count = 20;
static int[] squares;
static { // スタティックイニシャライザ
squares = new int[count];
for (int i = 0; i < count; i++)
squares[i] = i * i;
}
static int x = squares[5]; // x には値25が代入される。
「インスタンスイニシャライザ」(インスタンス初期化子)はインスタンスの (非static
な) フィールドの初期化子と同時に実行されるコードのブロックである。インスタンスフィールド初期化子とインスタンス初期化子は宣言された順番に実行される。
インスタンス初期化子とインスタンスフィールド初期化子はコンストラクタが呼び出された際に実行される。正確な実行順序としては、親クラスのコンストラクタが実行された後、かつ、自身のコンストラクタが実行される前、となる。
アクセス修飾子に加えて、メソッドには以下の修飾子を付けて宣言できる:
abstract
) サブクラスによって定義されなければならない。static、final、nativeのいずれとも併用できない。static
) メソッドでありかつ十分小さいならば、コンパイラはそのメソッドをインライン関数のように各所に展開する場合がある(訳注:性能改善目的と思われる)。abstractと併用はできない。Class
オブジェクトを指し、非static
ならばオブジェクトインスタンスを指す。abstractメソッドをsynchronizedとして宣言することは可能だが意味はない。何故なら排他とは宣言ではなく実装に伴う機能であり、抽象メソッドは実装を持たないからである。privateメソッドは自ずからfinalであり、abstractにはできない点に注意。
Java SE 5.0において、引数の個数が可変であるようなメソッドについての文法上の便宜 (varargs) [1]が追加された。これによって引数の個数が可変であるメソッドをタイプセーフに使用することが容易になる。最後のパラメタの後に「...」と書くと、Javaは全ての引数を配列に格納する:
public void drawPolygon(Point... points) { /* ... */ }
このメソッドを呼ぶ際、プログラマは個々のpointsを単にカンマで区切って書けばよく、Point
オブジェクトの配列をわざわざ用意する必要はない。このメソッドの内部でpointsを参照する際はpoints[0]、points[1]、などのように書ける。pointsが渡されていない場合、配列のlengthは0となる。可変個の引数と別に固定的に必要なパラメタがある場合は、それらのパラメタは可変引数に先立って指定すればよい。
// ポリゴンは少なくとも3つの点を必要とする。
public void drawPolygon(Point p1, Point p2, Point p3, Point... otherPoints) { /* ... */ }
コンストラクタはオブジェクトが割り当てられた後すぐに呼び出され、オブジェクトの初期処理を行う。コンストラクタは典型的にはnewキーワードを使用して呼び出されるが、リフレクションを使用して呼ぶこともできる。リフレクション機能はjava.lang.reflectパッケージより提供される。
コンストラクタを宣言する際に使える修飾子はアクセス修飾子のみである。
super(...);
または同じクラス内の別のコンストラクタ:this(...);
を呼び出せる。super(...)
または this(...)
に対する明示的な呼び出しがないならば、コンストラクタ本体が実行される前に、親クラスのデフォルトコンストラクタsuper();
が呼ばれる。Object
クラスのメソッドObject
クラスのメソッドは継承されるので、全てのクラスにて使用できる。
clone
メソッドObject.clone()
メソッドは現在のオブジェクトのコピーである新しいオブジェクトを返す。クラスは、それがクローンできることを明示するためにマーカーインタフェースCloneable
を実装しなければならない。
equals
メソッドObject.equals(Object)
メソッドはそのオブジェクトともう一つのオブジェクトを比較し、二つのオブジェクトが同一かどうかをboolean
型の値で返す。意味的には、このメソッドはオブジェクトの内容を比較するのに対し、関係演算子"==
"はオブジェクトの参照を比較する。equals
メソッドはjava.util
パッケージにあるデータ構造クラスの多くで使われる。これらのデータ構造クラスのいくつかはObject.hashCode
メソッドにも依存している - equals
とhashCode
との間の契約の詳細について、hashCodeメソッドを参照のこと。
finalize
メソッドObject.finalize()
メソッドはガベージコレクタがオブジェクトのメモリを解放する前に必ず一度だけ呼び出される。オブジェクトが消滅する前に実行しなければならない何らかの後処理がある場合、各クラスはfinalize
をオーバーライドすることができる。とはいえほとんどのオブジェクトはfinalize
をわざわざオーバーライドする必要はない。
finalize
メソッドがいつ呼ばれるかは保証されない。複数のオブジェクトのfinalize
がどのような順番で呼ばれるかも不定である。もしJVMがガベージコレクションを実行せずに終了するならば、OSがオブジェクトを解放する可能性があり、その場合finalize
メソッドは呼ばれない。
finalize
メソッドは、他のクラスから呼ばれるのを防ぐために、常にprotectedとして宣言されるべきである。
protected void finalize() throws Throwable { ... }
getClass
メソッドObject.getClass()
メソッドはオブジェクトをインスタンス化するために使われたクラスのClass
オブジェクトを返す。このクラスオブジェクトはJavaにおけるリフレクションの基本となる。その他のリフレクション機能はjava.lang.reflect
パッケージにて提供される。
hashCode
メソッドObject.hashCode()
メソッドは連想配列にオブジェクトを保存するための「ハッシュ値」として (int
型の) 整数を返す。java.util.Map
インタフェースを実装するクラスは連想配列を提供しhashCode
メソッドに依存する。hashCode
の良い実装は安定 (不変) かつ均等に分布するハッシュ値を返す (異なるオブジェクトのハッシュ値は互いに異なる値となる傾向を持ち、かつハッシュ値は整数値の範囲内で均等に分布する)。
連想配列はequals
とhashCode
の両メソッドに依存するため、これら二つのメソッドの間では、オブジェクトがMap
に挿入される場合に関する或る重要な契約[注 1]が維持されねばならない:
a.equals(b) == b.equals(a)
でなければならない。a.equals(b)
がtrue
ならば、a.hashCode() == b.hashCode()
でなければならない。この契約を維持するために、equals
メソッドをオーバーライドしたクラスは同時にhashCode
メソッドもオーバーライドし、逆もまた同様として、hashCode
とequals
が常に同じ性質(または同じ性質の一部)に基づくようにしなければならない。
マップがオブジェクトとの間に有する更なる契約は、ひとたびオブジェクトがマップに挿入されたなら、hashCode
と equals
両メソッドの結果は以後変わらないということである。したがって、一般にハッシュ関数はオブジェクトの不変(変更不能)な属性に基くように設計するのが良い。
toString
メソッドObject.toString()
メソッドはオブジェクトの文字列表現をString
で返すものである。toString
メソッドは、オブジェクトが文字列連結演算子(+
と+=
)のオペランドとして使われたとき、コンパイラによって暗黙のうちに呼び出される。
全てのオブジェクトは、そのオブジェクトに関連するスレッドについての二つの待ちリストを持つ。一つの待ちリストはsynchronizedキーワードに伴いオブジェクトをミューテックス排他するために使われる。もしミューテックスが他スレッドによって排他されているならば、自スレッドは排他を待っているスレッドのリストに追加される。もう一つの待ちリストはスレッド間でシグナルを送るためのもので、これはwait、notify、notifyAllの各メソッドを通して使用される。
wait/notifyを用いるとスレッド間での能率的な連携が可能となる。あるスレッドが別スレッドでの処理が終わるのを待ち合わせる必要があるとき、または何らかのイベントが発生するまで待たねばならないとき、スレッドはその実行を一時停止してイベントが発生した際に通知を受け取ることができる。これはポーリングとは対照的である。ポーリングにおいては、スレッドは一定時間スリープしてはフラグや他の状態表示をチェックする処理を繰り返す。ポーリングはスレッドがチェックを繰り返さねばならないという点でより計算コストが掛かる上に、実際にチェックしてみるまでイベント発生を検知できないという意味で鈍感でもある。
wait
メソッドwait
メソッドには三つのオーバーロード版があり、タイムアウト値の指定方法がそれぞれ異なる:wait()
、wait(long timeout)
、wait(long timeout, int nanos)
の三つである。一つ目のメソッドはタイムアウト値が0であり、これはタイムアウトが発生しないことを意味する。二つ目のメソッドはミリ秒単位のタイムアウト値を取る。三つ目のメソッドはナノ秒単位のタイムアウト値を取り、これは1000000 * timeout + nanos
として計算される。
wait
を呼んだスレッドは待機状態となり、そのオブジェクトの待ちリストに追加される。そのスレッドは以下の三つのイベントのいずれか一つが起きるまで、オブジェクトの待ちリスト上に留まる:
notify
またはnotifyAll
メソッドを呼ぶ (詳細はnotifyメソッド参照)interrupt()
メソッドを呼ぶwait
にて指定した0でないタイムアウト値が満了するwait
メソッドは、そのオブジェクトについての同期(synchronized)ブロックまたは同期(synchronized)メソッドの内部からのみ呼ばねばならない。これはwait
とnotify
との間で競合を起こさないためである。スレッドが待ちリストに入るとき、そのスレッドはそのオブジェクトのミューテックス排他を解除する[注 2]。そのスレッドが待ちリストから削除され実行可能スレッドとなった際に、そのスレッドは走行を再開するのに先立ってそのオブジェクトのミューテックスを改めて排他しなければならない。
notify
とnotifyAll
メソッドObject.notify()
と Object.notifyAll()
メソッドはオブジェクトの待ちリストから一つ以上のスレッドを削除し、それらを実行可能スレッドとする。notify
は待ちリストから1スレッドのみ削除し、notifyAll
は待ちリストから全てのスレッドを削除する。notify
がどのスレッドをリストから削除するかは規定されておらず、JVMの実装に依存する。
notify
とnotifyAll
メソッドは、そのオブジェクトについての同期(synchronized)ブロックまたは同期(synchronized)メソッドの内部からのみ呼ばねばならない。これはwait
とnotify
との間で競合を起こさないためである。
Javaのアノテーションはクラスやインタフェース、メソッドやフィールド、パッケージなどに対してメタデータとして付加情報を記入する機能で、Java SE 5 で追加された。
アノテーションはjava.lang.annotation.Annotation
インタフェースを実装することで自作することもできる。
Javaのアノテーションは三つに分けることができる。
@Deprecated
– 対象となるクラスやメソッドが非推奨であることを情報として付加する。@Override
– そのメソッドがスーパークラスのメソッドをオーバーライドしていることを示す。@SuppressWarnings
– 引数で指定した特定の警告メッセージをJavaコンパイラに与えず無視する。@Target
– 定義したいアノテーションがどこに適用されるべきかを指し示すメタアノテーション。@Retention
– アノテーションの配置方法を設定するメタアノテーション。@Test
test
で始まるメソッドと同じ。@Before
@Test
アノテーションが付いたメソッドを実行するたびに事前に実行されることを意味する。@After
@Test
アノテーションが付いたメソッドを実行するたびに、必ず後から実行されることを意味する。@BeforeClass
@AfterClass
クラスやメソッドにマーカーアノテーションを付加するには以下のようクラスやメソッドの接頭辞に最低一つ以上のスペースまたは改行コードを入れて修飾子のように記述する。 この例は、クラスに非推奨、メソッドに、スーパークラスからのメソッドをオーバーライドしていることを意味するマーカーアノテーションを付加している例である。
@Deprecated class DeprecatedClass {
@Override public boolean equals(Object obj) {
return this == obj;
}
}
単一値アノテーションを付加するには以下のようにする。
この例は、Serializable
インタフェースを実装したクラスのフィールドにstatic finalなserialVersionUIDが宣言されていないという警告を無視するアノテーションを付加していることを意味する。
@SuppressWarnings(value = {"serial"})
class NoSerialVersionIDClass implements java.io.Serializable { }
これは、以下のような書き方もできる。これは単一値アノテーション@SuppressWarningsがvalue()メソッドを一つしか持たないことがわかっているためvalue = を省略できることを意味する。
@SuppressWarnings("serial")
class NoSerialVersionIDClass implements java.io.Serializable { }
このアノテーションは、戻り値の型がString[]になっているため同じvalue値であっても以下のように複数指定することができる。以下のように指定することで、シリアルバージョンIDが設定されていない警告と、コレクションで総称型による型チェックを行われていないことによって生ずる警告を無視することができる。 "unchecked"はメソッドに対してのみ設定することもできる。
@SuppressWarnings("serial", "unchecked")
class NoSerialVersionIDClass implements java.io.Serializable {
public void setupList() {
List list = new ArrayList();
list.add("abcdef");
}
}
このアノテーションは正確に記述すると以下のようにString[]配列の初期化宣言のようになる。
@SuppressWarnings(value = {"serial", "unchecked"})
フルアノテーションは、複数のデータ型を持つアノテーションである。ここでは自作したアノテーション @MyAnnotation
があるとき、以下のように、変数名 = 値の形をカンマで区切って記述する。各値の記法は、各アノテーションで定義されているメソッドの戻り値の型で決まる。たとえばこの場合valueという変数名はStringを戻り型にとる value()
というメソッドと、int
を戻り型にとる version()
というメソッドを持つ。フルアノテーションの場合は、default
によりデフォルト値が設定されているアノテーション以外は、value =
や version =
を省略することはできない。
@MyAnnotation(value = "abc", version = 2)
class AnnotatedClass { }
アノテーションを定義するには、interfaceキーワードの接頭辞に@をつけて定義する。
マーカーアノテーションは以下のように定義する。メソッドやフィールドが一切ないマーカーインタフェースのアノテーション版ともいえる。@Override
や@Deprecated
がこれらのアノテーションに相当する。
public @interface MarkerAnnotation { }
単一値アノテーションは以下のように定義する。このアノテーションには少なくともメソッドがひとつだけ定義されている。単一値アノテーションのメソッド名にはvalueという名前をつけるのが儀礼である。
@interface Single {
String value();
}
フルアノテーションは以下のように定義する。以下のように二つ以上のメソッドを定義する。
@interface FullAnnotation {
String value();
int id();
}
メタアノテーションとは、定義しているアノテーションのみにつけられるアノテーションのことである。メタアノテーションの例としては@Target
や@Retention
、@Documented
、@Inherited
があり、これらはクラスやメソッドなどには使うことができず、アノテーションのみに使うことができる。アノテーションを定義するために使われるアノテーションということから、メタアノテーションと呼ばれる。
メタアノテーションを使ってアノテーションを定義するには、以下のように記述する。
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
public @interface NewAnnotation {
}
このとき、@Retention
は、新たにアノテーション NewAnnotation
を作るとき、このアノテーション情報はソースコードのみにしか保存されないことを意味する。@Target
はこのアノテーションをどの型に使うことができるかを指定している。この場合、ANNOTATION_TYPE
と METHOD
を指定しているのでこのアノテーションはアノテーション型とメソッドにしか使うことができない。つまり、この NewAnnotation
もまた、メソッドだけでなくアノテーションにも保存できるため、メタアノテーションとしても使えることを示している。
メタアノテーション@Retention
には以下のRetentionPolicy
列挙型を設定することができる。
RetentionPolicy 名
|
説明 |
---|---|
RetentionPolicy.CLASS
|
アノテーション情報はコンパイル時に保存されるが実行時にはJava仮想マシンによって保持されない。 |
RetentionPolicy.RUNTIME
|
アノテーション情報はコンパイル時に保存され、実行時にもJava仮想マシンによって保持される。 |
RetentionPolicy.SOURCE
|
アノテーション情報はコンパイル時に破棄される。ソースコード内のみで有効。 |
メタアノテーション@Target
には以下のElementType
列挙型を設定することができる。これは配列を使って
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
と複数指定することができる。ただし、同じ値を {
… }
内で複数使用するとエラーとなる。これによって型を指定することで、そのアノテーションが、どの型に対して使うことができるのかを指定できる。
ElementType 名
|
説明 |
---|---|
ElementType.ANNOTATION_TYPE
|
アノテーション型に指定できることを示す。 |
ElementType.CONSTRUCTOR
|
コンストラクタに指定できることを示す。 |
ElementType.LOCAL_VARIABLE
|
ローカル変数に指定できることを示す。 |
ElementType.FIELD
|
フィールドに指定できることを示す。 |
ElementType.METHOD
|
メソッドに指定できることを示す。 |
ElementType.PACKAGE
|
パッケージに指定できることを示す。 |
ElementType.PARAMETER
|
メソッド引数に指定できることを示す。 |
ElementType.TYPE
|
クラス、またはインタフェース(アノテーション型を含む)、列挙型に指定できることを示す。 |
J2SE1.4よりも前のバージョンのJavaはストリーム・ベースのブロッキングI/Oのみをサポートしていた。これは1ストリームにつき1スレッドを必要とした。何故ならストリームの入力または出力を行おうとすると、それが完了するまでそのスレッドは完全に待ちに入ってしまい、他の処理がいっさい行えなくなったからである。これは、Javaを用いたネットワークサービスを構築する上で、スケーラビリティと性能双方の面で大きな問題となっていた。J2SE1.4以降では非ブロッキングI/OフレームワークとしてNIO (New I/O) が導入され、このスケーラビリティ問題は修正された (ただし、サンによるNIO APIの実装にはまだ多くの問題点がある)。
非ブロッキングIOフレームワークは、以前のブロッキングIOフレームワークより遥かに複雑ではあるが、一つのスレッドで任意の数の"チャネル"を扱うことができる。このフレームワークはReactorパターンをベースとしている。
public class MyClass {
public static void main(String[] args) {...}
...
}
// MyApplet.java
import java.applet.*;
public class MyApplet extends Applet {
init() {...} // ブラウザが最初にアプレットを読み込むときに呼ばれる。
destroy() {...} // ユーザがブラウザを終了するときに呼ばれる。
start(){...} // アプレットを実行し始めるときに呼ばれる。
stop() {...} // ユーザがウェブページを去るとき、再読込するとき、
// ブラウザを終了するときに呼ばれる。
}
<applet code="MyApplet" width="200" height="200">
</applet>
/* */
コメントによって囲まれていなければならない。// MyApplet.java
...
/*
<applet code="MyApplet" width="200" height="200">
</applet>
*/
...
文法 | 意味 |
---|---|
<% Java構文 %> | スクリプトレット |
<%= 単一Java構文の出力 %> | 構文 |
<%! Java宣言文 %> | 宣言 |
<%@ [page, include, taglib] JSPディレクティブ %> | ディレクティブ |
Javaはケースセンシティブ (大文字小文字を区別する) である。
コメントの文法はC++と同じである。
// 一行コメント
/* 複数行
コメント */
なお、ドキュメンテーションコメントとしてJavadoc方式をサポートする。
/**
* この行はクラス、インタフェース、メソッド、フィールド宣言の直前に記述する。
* このコメントはクラスのドキュメントを自動生成する
* ユーティリティで使用することができる。
*/