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
char
double
float
false
int
long
short
true
null
super
this
new
goto
const
Javaのデータ型には大別して、プリミティブ型と参照型がある。
primitive data type[1]もしくはprimitive type[2]とも。 プリミティブ型はオブジェクトではなく、スーパークラスを持たない。
Integer.MAX_VALUE
Integer.MIN_VALUE
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]
String
Character
すべての参照型はオブジェクト型を表すクラスObjectから派生する。オブジェクトは参照型のインスタンスである。クラスclassは既定でObjectから派生する参照型である。列挙型enumは抽象クラスEnumから暗黙的に派生する参照型である。
Object
Enum
リフレクション機能のための「クラスを表現するクラス(メタクラス)」としてClassが存在するが、クラス自体はオブジェクトではない。
Class
+
+=
String str1 = "alpha"; String str2 = new String("alpha");
StringBuffer
StringBuilder
StringBuffer str1 = new StringBuffer("alpha"); str1.append("-meta"); str1.setCharAt(str1.indexOf("m"), 'b'); System.out.println(str1); // str1.toString() を呼び出す。 // 印字結果は"alpha-beta"となる
プリミティブラッパークラス
Byte
Short
Integer
Long
Float
Double
Boolean
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();
リテラルの例を示す。
0365
0
0xF5
0x
245
23.5F
23.5f
1.72E3F
1.72E3f
1.72e3F
1.72e3f
23.5
23.5D
23.5d
1.72E3
1.72E3D
'a'
'Z'
'\u000A'
'\0231'
'\t'
'\b'
'\r'
'\f'
'\\'
'\
'\"'
'\n'
"Hello, world"
Javaは1バイト整数の型(byte)と文字の型(char)とを区別する。なお、charは、名前に反して必ずしも文字を表現していない場合があり得る。すなわち、実際にはUCS-2(UTF-16)による、いわゆるダブルバイトである。J2SE 5.0以降ではサロゲートAPIによって、サロゲートペアに対応した。
JavaプログラムのソースコードおよびソースファイルはUTF-8でエンコードするのが基本である。Unicodeに対応したエンコードであれば、Javaプログラムのソースコードに直接Unicode文字を記述することもできる。なお、-encodingコンパイルオプションを指定することで、対応する任意のエンコードを使用することもできる。ロケール依存のコードページ(日本語版Microsoft Windowsで使われているCP932等)を利用することもできる。
-encoding
文字列リテラルやコメントの他、クラス名や変数名も国際化に対応している。例えば、次のソースコードはJavaのコードとして正しく解釈されコンパイルされ実行できる。ここではクラス名、変数名、および文字列リテラルとして日本語の文字を使っている。
public class こんにちは世界 { private String 文字列 = "こんにちは世界"; }
算術演算子
-
*
/
%
++
--
!
~
(型名)
代入演算子
=
-=
*=
/=
%=
&=
|=
^=
<<=
>>=
>>>=
関係演算子
==
!=
>
>=
<
<=
関係演算子(==と!=)を参照型に対して用いた場合、そこで比較されるのは参照先のオブジェクトが同じかどうかであり、オブジェクトの中身の値が一致するか否かではない。オブジェクトの中身を比較したい場合はObject.equals(Object)メソッドを使用する。instanceof演算子は、オブジェクトが指定クラスのインスタンスであるか否かを判定するために用いる。
Object.equals(Object)
三項演算子は二つの記号?と:を組み合わせて記述する。条件演算子とも呼ぶ。構文は以下である。
?
:
条件 ? 式1 : 式2
条件がtrueであるとき、式1の値をとる。そうでない場合は式2の値をとる。
例:
String answer = (p < 0.05) ? "reject": "keep";
// これは以下のコードと等価である: String answer; if (p < 0.05) { answer = "reject"; } else { answer = "keep"; }
論理演算子
&&
||
ビット演算子
&
|
^
<<
>>
>>>
if文
if (expr) { statements; } else if (expr) { statements; } else { statements; }
switch文
switch (expr) { case VALUE1: statement11 ; … ; statement1L ; break ; case VALUE2: statement21 ; … ; statement2M ; break ; default: statementd1 ; … ; statementdN ; }
expr
for文
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に加算する。
stringSet
sum
while文
while (expr) { statements; }
do-while文
do { statements; } while (expr);
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) を定義し、同時にインスタンス生成をすることもできる。
クラスあるいはインタフェースの内部で別のインタフェースを宣言することもできる。これは「ネストされたインタフェース」と呼ばれる。
クラスを宣言する際は以下の修飾子を付けることができる:
Javaのクラス定義ブロックはセミコロン;で終わらせる必要はない点に注意。この点はC++の文法と異なる。
;
// 子クラスは親クラスを継承する class ChildClass extends ParentClass { ... }
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という名前の非抽象メソッドを定義しなければならない。そのメソッドの実装と機能は各々のクラスによって決定される。
Deleteable
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オブジェクトをも区別する必要がない。
delete()
deleteAll()
Fred
インタフェースは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を提供することを可能とする。外にアクセスを公開されたクラスの動作を、隠されたクラスが支える形になる。
クラスメンバとはフィールド、メソッド、コンストラクタ、クラス内で定義されたネストされたクラスのことである。アクセス制限が厳しいものから並べると、クラスメンバのアクセス修飾子は次の通り。
メソッドをオーバライドする際、そのメソッドのアクセス権を「より厳しく」することはできない。さもなくば親クラスのインタフェース契約を壊してしまうからである。したがってオーバライドされる場合、publicメソッドはpublicとして宣言されねばならず、protectedメソッドをデフォルトアクセス権(修飾子省略)とすることはできない。しかしながら、メソッドをオーバライドしてアクセス権を「より緩める」ことは許される。したがってオーバライドする際、デフォルト (パッケージ) アクセス権のメソッドはprotectedまたはpublicとして宣言することができ、protectedメソッドはpublicとして宣言することができる。
アクセス修飾子に加えて、データフィールドは以下の修飾子によって宣言される:
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な) フィールドの初期化子と同時に実行されるコードのブロックである。インスタンスフィールド初期化子とインスタンス初期化子は宣言された順番に実行される。
インスタンス初期化子とインスタンスフィールド初期化子はコンストラクタが呼び出された際に実行される。正確な実行順序としては、親クラスのコンストラクタが実行された後、かつ、自身のコンストラクタが実行される前、となる。
アクセス修飾子に加えて、メソッドには以下の修飾子を付けて宣言できる:
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となる。可変個の引数と別に固定的に必要なパラメタがある場合は、それらのパラメタは可変引数に先立って指定すればよい。
Point
// ポリゴンは少なくとも3つの点を必要とする。 public void drawPolygon(Point p1, Point p2, Point p3, Point... otherPoints) { /* ... */ }
コンストラクタはオブジェクトが割り当てられた後すぐに呼び出され、オブジェクトの初期処理を行う。コンストラクタは典型的にはnewキーワードを使用して呼び出されるが、リフレクションを使用して呼ぶこともできる。リフレクション機能はjava.lang.reflectパッケージより提供される。
コンストラクタを宣言する際に使える修飾子はアクセス修飾子のみである。
super(...);
this(...);
super(...)
this(...)
super();
Objectクラスのメソッドは継承されるので、全てのクラスにて使用できる。
clone
Object.clone()メソッドは現在のオブジェクトのコピーである新しいオブジェクトを返す。クラスは、それがクローンできることを明示するためにマーカーインタフェースCloneableを実装しなければならない。
Object.clone()
Cloneable
equals
Object.equals(Object)メソッドはそのオブジェクトともう一つのオブジェクトを比較し、二つのオブジェクトが同一かどうかをboolean型の値で返す。意味的には、このメソッドはオブジェクトの内容を比較するのに対し、関係演算子"=="はオブジェクトの参照を比較する。equalsメソッドはjava.utilパッケージにあるデータ構造クラスの多くで使われる。これらのデータ構造クラスのいくつかはObject.hashCodeメソッドにも依存している - equalsとhashCodeとの間の契約の詳細について、hashCodeメソッドを参照のこと。
java.util
Object.hashCode
hashCode
finalize
Object.finalize()メソッドはガベージコレクタがオブジェクトのメモリを解放する前に必ず一度だけ呼び出される。オブジェクトが消滅する前に実行しなければならない何らかの後処理がある場合、各クラスはfinalizeをオーバーライドすることができる。とはいえほとんどのオブジェクトはfinalizeをわざわざオーバーライドする必要はない。
Object.finalize()
finalizeメソッドがいつ呼ばれるかは保証されない。複数のオブジェクトのfinalizeがどのような順番で呼ばれるかも不定である。もしJVMがガベージコレクションを実行せずに終了するならば、OSがオブジェクトを解放する可能性があり、その場合finalizeメソッドは呼ばれない。
finalizeメソッドは、他のクラスから呼ばれるのを防ぐために、常にprotectedとして宣言されるべきである。
protected void finalize() throws Throwable { ... }
getClass
Object.getClass()メソッドはオブジェクトをインスタンス化するために使われたクラスのClassオブジェクトを返す。このクラスオブジェクトはJavaにおけるリフレクションの基本となる。その他のリフレクション機能はjava.lang.reflectパッケージにて提供される。
Object.getClass()
java.lang.reflect
Object.hashCode()メソッドは連想配列にオブジェクトを保存するための「ハッシュ値」として (int型の) 整数を返す。java.util.Mapインタフェースを実装するクラスは連想配列を提供しhashCodeメソッドに依存する。hashCodeの良い実装は安定 (不変) かつ均等に分布するハッシュ値を返す (異なるオブジェクトのハッシュ値は互いに異なる値となる傾向を持ち、かつハッシュ値は整数値の範囲内で均等に分布する)。
Object.hashCode()
java.util.Map
連想配列はequalsとhashCodeの両メソッドに依存するため、これら二つのメソッドの間では、オブジェクトがMapに挿入される場合に関する或る重要な契約[注 1]が維持されねばならない:
Map
a.equals(b) == b.equals(a)
a.equals(b)
a.hashCode() == b.hashCode()
この契約を維持するために、equalsメソッドをオーバーライドしたクラスは同時にhashCodeメソッドもオーバーライドし、逆もまた同様として、hashCodeとequalsが常に同じ性質(または同じ性質の一部)に基づくようにしなければならない。
マップがオブジェクトとの間に有する更なる契約は、ひとたびオブジェクトがマップに挿入されたなら、hashCode と equals両メソッドの結果は以後変わらないということである。したがって、一般にハッシュ関数はオブジェクトの不変(変更不能)な属性に基くように設計するのが良い。
toString
Object.toString()メソッドはオブジェクトの文字列表現をStringで返すものである。toStringメソッドは、オブジェクトが文字列連結演算子(+と+=)のオペランドとして使われたとき、コンパイラによって暗黙のうちに呼び出される。
Object.toString()
全てのオブジェクトは、そのオブジェクトに関連するスレッドについての二つの待ちリストを持つ。一つの待ちリストはsynchronizedキーワードに伴いオブジェクトをミューテックス排他するために使われる。もしミューテックスが他スレッドによって排他されているならば、自スレッドは排他を待っているスレッドのリストに追加される。もう一つの待ちリストはスレッド間でシグナルを送るためのもので、これはwait、notify、notifyAllの各メソッドを通して使用される。
wait/notifyを用いるとスレッド間での能率的な連携が可能となる。あるスレッドが別スレッドでの処理が終わるのを待ち合わせる必要があるとき、または何らかのイベントが発生するまで待たねばならないとき、スレッドはその実行を一時停止してイベントが発生した際に通知を受け取ることができる。これはポーリングとは対照的である。ポーリングにおいては、スレッドは一定時間スリープしてはフラグや他の状態表示をチェックする処理を繰り返す。ポーリングはスレッドがチェックを繰り返さねばならないという点でより計算コストが掛かる上に、実際にチェックしてみるまでイベント発生を検知できないという意味で鈍感でもある。
wait
waitメソッドには三つのオーバーロード版があり、タイムアウト値の指定方法がそれぞれ異なる:wait()、wait(long timeout)、wait(long timeout, int nanos)の三つである。一つ目のメソッドはタイムアウト値が0であり、これはタイムアウトが発生しないことを意味する。二つ目のメソッドはミリ秒単位のタイムアウト値を取る。三つ目のメソッドはナノ秒単位のタイムアウト値を取り、これは1000000 * timeout + nanosとして計算される。
wait()
wait(long timeout)
wait(long timeout, int nanos)
1000000 * timeout + nanos
waitを呼んだスレッドは待機状態となり、そのオブジェクトの待ちリストに追加される。そのスレッドは以下の三つのイベントのいずれか一つが起きるまで、オブジェクトの待ちリスト上に留まる:
notify
notifyAll
interrupt()
waitメソッドは、そのオブジェクトについての同期(synchronized)ブロックまたは同期(synchronized)メソッドの内部からのみ呼ばねばならない。これはwaitとnotifyとの間で競合を起こさないためである。スレッドが待ちリストに入るとき、そのスレッドはそのオブジェクトのミューテックス排他を解除する[注 2]。そのスレッドが待ちリストから削除され実行可能スレッドとなった際に、そのスレッドは走行を再開するのに先立ってそのオブジェクトのミューテックスを改めて排他しなければならない。
Object.notify() と Object.notifyAll() メソッドはオブジェクトの待ちリストから一つ以上のスレッドを削除し、それらを実行可能スレッドとする。notifyは待ちリストから1スレッドのみ削除し、notifyAllは待ちリストから全てのスレッドを削除する。notifyがどのスレッドをリストから削除するかは規定されておらず、JVMの実装に依存する。
Object.notify()
Object.notifyAll()
notifyとnotifyAllメソッドは、そのオブジェクトについての同期(synchronized)ブロックまたは同期(synchronized)メソッドの内部からのみ呼ばねばならない。これはwaitとnotifyとの間で競合を起こさないためである。
Javaのアノテーションはクラスやインタフェース、メソッドやフィールド、パッケージなどに対してメタデータとして付加情報を記入する機能で、Java SE 5 で追加された。
アノテーションはjava.lang.annotation.Annotationインタフェースを実装することで自作することもできる。
java.lang.annotation.Annotation
Javaのアノテーションは三つに分けることができる。
@Deprecated
@Override
@SuppressWarnings
@Target
@Retention
@Test
test
@Before
@After
@BeforeClass
@AfterClass
クラスやメソッドにマーカーアノテーションを付加するには以下のようクラスやメソッドの接頭辞に最低一つ以上のスペースまたは改行コードを入れて修飾子のように記述する。 この例は、クラスに非推奨、メソッドに、スーパークラスからのメソッドをオーバーライドしていることを意味するマーカーアノテーションを付加している例である。
@Deprecated class DeprecatedClass { @Override public boolean equals(Object obj) { return this == obj; } }
単一値アノテーションを付加するには以下のようにする。 この例は、Serializableインタフェースを実装したクラスのフィールドにstatic finalなserialVersionUIDが宣言されていないという警告を無視するアノテーションを付加していることを意味する。
Serializable
@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()
version()
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があり、これらはクラスやメソッドなどには使うことができず、アノテーションのみに使うことができる。アノテーションを定義するために使われるアノテーションということから、メタアノテーションと呼ばれる。
@Documented
@Inherited
メタアノテーションを使ってアノテーションを定義するには、以下のように記述する。
@Retention(RetentionPolicy.SOURCE) @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) public @interface NewAnnotation { }
このとき、@Retentionは、新たにアノテーション NewAnnotation を作るとき、このアノテーション情報はソースコードのみにしか保存されないことを意味する。@Targetはこのアノテーションをどの型に使うことができるかを指定している。この場合、ANNOTATION_TYPE と METHOD を指定しているのでこのアノテーションはアノテーション型とメソッドにしか使うことができない。つまり、この NewAnnotation もまた、メソッドだけでなくアノテーションにも保存できるため、メタアノテーションとしても使えることを示している。
NewAnnotation
ANNOTATION_TYPE
METHOD
メタアノテーション@Retentionには以下のRetentionPolicy列挙型を設定することができる。
RetentionPolicy
RetentionPolicy.CLASS
RetentionPolicy.RUNTIME
RetentionPolicy.SOURCE
メタアノテーション@Targetには以下のElementType列挙型を設定することができる。これは配列を使って
ElementType
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
と複数指定することができる。ただし、同じ値を { … } 内で複数使用するとエラーとなる。これによって型を指定することで、そのアノテーションが、どの型に対して使うことができるのかを指定できる。
{
}
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パターンをベースとしている。
main
public class MyClass { public static void main(String[] args) {...} ... }
init
destroy
start
stop
// 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はケースセンシティブ (大文字小文字を区別する) である。
コメントの文法はC++と同じである。
// 一行コメント /* 複数行 コメント */
なお、ドキュメンテーションコメントとしてJavadoc方式をサポートする。
/** * この行はクラス、インタフェース、メソッド、フィールド宣言の直前に記述する。 * このコメントはクラスのドキュメントを自動生成する * ユーティリティで使用することができる。 */