Javaの文(ステートメント・statement)。
|
|
|
「文」が書ける位置には、波括弧で複数の文を囲った「複文」を置くことが出来る。
文 { 文; … }
複文のブロックの中で変数を宣言すると、その変数のスコープはそのブロック内となる。
if (条件式) 文; if (条件式) 文; else 文; if (条件式) { 文;… } if (条件式) { 文;… } else { 文;… }
条件式にはboolean型しか使えない(C言語のように数値を使うことはできない)。[2003-07-06]
switch (式) { case 値1: case 値2: … 文; … break; case 値n: 文; … //breakなし default: 文; … break; }
式は、整数型(byte, char, short, intのいずれか)の値になるもの。[2003-07-06]
(値にはstatic finalな変数(定数)も使用できるが、条件がある。→caseで使える定数)
JDK1.5以降では列挙型も可。[2007-03-26]
JDK1.7以降では文字列も可(ただし式がnullだとNullPointerExceptionになる)。[2012-01-14]
「case null:
」と書くとコンパイルエラーになる。[2012-12-15]
(→nodamushiさんのJava7のswitch(String)構文のJavaコンパイラーとEclipseコンパイラーの違い)
「switch」の前にラベルを付けて、switchブロック内のbreakでそのラベルを指定できる。
Java14以降では、switchから値を返すことが出来る。(switch式)[2020-03-21]
→switch文・switch式の詳細 [2020-03-21]
while (繰り返し条件) 文; while (繰り返し条件) { 文; … }
条件式にはboolean型しか使えない(C言語のように数値を使うことはできない)。[2003-07-06]
条件式にtrueを入れると無限ループになる(break等で抜ける)。
「while」の前にラベルを付けて、whileブロック内のbreak・continueでそのラベルを指定できる。
do 文; while (繰り返し条件); do { 文; … } while (繰り返し条件);
条件式にはboolean型しか使えない(C言語のように数値を使うことはできない)。
continueが実行されると、繰り返し条件が再びチェックされる。
「do」の前にラベルを付けて、doブロック内のbreak・continueでそのラベルを指定できる。
for (初期化部; 繰り返し条件の式; 更新部) 文; for (初期化部; 繰り返し条件の式; 更新部) { 文; … }
条件式にはboolean型しか使えない(C言語のように数値を使うことはできない)。
forの括弧内の各項(初期化・条件・更新)は省略することが出来る。「for (;;)
」だと無限ループになる(break等で抜ける)。
「for」の前にラベルを付けて、forブロック内のbreak・continueでそのラベルを指定できる。
第1項(初期化部)で変数を宣言することが出来る。この変数のスコープは、ループのブロック内のみ。[2004-06-12]
(forループが終わった後は、その変数は参照できない)
ただし名前空間は別にならないので、forより前で宣言した変数と同じ名前の変数は宣言できない。
// int i = 0; ←forより先に宣言していたらダメ
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// System.out.println(i); ←forが終わった後では使えない
for (int i = 0, j = 1; i < 10; i++) { ←同じ型の変数であれば、カンマ区切りでいくつでも宣言できる System.out.println(j); j *= 2; }
第3項(更新部)には、カンマ区切りで式を並べることが出来る。[2007-12-22]
(C言語ではカンマ演算子を使って実現していたのだが、Javaではカンマ演算子は無いけれど、ここはC言語と同様に書けるようにしたのだろう)
for (int i = 0; i < 10; System.out.println(i), i++);
第1項(初期化部)でも、変数宣言でない場合は、第3項と同様にカンマ区切りで式を並べることが出来る。[2008-09-06]
int i; for (i = 0, System.out.println(i); i < 10; i++);
本来は、「i = 0, j = 1」といった(変数宣言でない)代入式を並べるための構文だと思うが…^^;
Java10で、for文の変数定義にvarを使えるようになった。[2018-04-21]
for (var i = 0; i < 10; i++)
ただし、varの場合は複数の変数を定義することは出来ない。
× for (var i = 0, j = 0; i < 10; i++, j++)
for (型 変数: コレクション) 〜
JDK1.5からの新文法。→SunのFor-Eachループ [2007-03-26]
配列や、Iterableインターフェースを実装したクラス(コレクション)で使用可能。
for (型 変数 : コレクション) { 〜 }
for (型 変数 : 配列) { 〜 }
↓コンパイルした実体は、Iterator(配列の場合は添字)を使った形式に変換されるらしい。
for (Iterator i = コレクション.iterator(); i.hasNext();) { 型 変数 = (型)i.next(); 〜 }
for (int i = 0; i < 配列.length; i++) { 型 変数 = 配列[i]; 〜 }
Java10で、for文の変数定義にvarを使えるようになった。[2018-04-21]
for (var 変数 : コレクション) { 〜 }
ラベル: 文
Javaの文の前には、どこにでもラベルを付けることが出来る。(C言語/C++のラベルと同じ)
しかし、基本的には、switch・for・while・doといったブロック内からbreakで抜けたりcontinueで繰り返したりするときにラベルを指定するので、
それ以外の場所に付けたラベルには意味が無い。
「switch」「for」「while」「do」の前に「ラベル: 」と書くことにより、そのブロックにラベルを付けたことになる。
label: for (;;) { while (true) { break label; //for文から抜ける } }
※ブロック(「switch」「for」「while」「do」等)の直前以外の場所に付けたラベルにはジャンプすることは出来ない。
breakで明示的にラベル名を指定する場合は、処理を中断する対象(ブレイクターゲット:break
target)は通常のブロックでもよい。[2008-09-06]
(ブロックを抜ける=そのブロックの処理を中断(途中終了)する)
System.out.println("start"); label: { System.out.println("block start"); if (true) break label; System.out.println("block end"); //この文は実行されない } System.out.println("end");
C言語/C++では、goto文によって自由にラベルにジャンプすることが出来た。[2008-04-08]
(したがってラベルはどんな場所にでも自由に付けることが出来た)
Javaではこの機能は無いが、gotoは予約語になっている。(使用するとコンパイルエラーになる)
return; return 値;
メソッド(やコンストラクター)を終了し、値を返す。(メソッドの戻り型がvoidの場合やコンストラクターでは値を返さないので、値は指定しない)
throw 例外インスタンス;
例外を発生させる。(例外インスタンスを投げる(スロー(throw)する))
普通は「throw new Exception();
」という形をとるが、別の箇所でインスタンス化してそれを投げる事も可能。
public void test1() throws Exception { throw create(); } public void test2() throws Exception { Exception ex = create(); System.out.println(ex.toString()); throw ex; } private Exception create() { return new Exception("テスト"); }
ただしこの形だと、スタックトレースの例外発生場所(newで作った場所)とスローした場所が異なる状態になるので注意。こういう事はあまりやらない方がいいだろう。
try { 〜 //処理本体。例外が発生する可能性がある } catch (例外 e) { 〜 //例外を処理する }
try { 〜 } finally { 〜 //終了処理。必ず実行される }
tryブロック内で発生した例外を捕捉(キャッチ(catch))する。
finallyブロックは、tryブロック・catchブロックを終了する際に必ず実行される。
終了とは、それらのブロックの末尾まで実行されたか、あるいはreturn・throw・break・continueといった文によってブロックを抜けること。
finallyブロック内でreturnやthrowを書くことは出来るが、すべきでない。
なぜなら、正常にreturnした値や最初に発生してスローされていた例外が握りつぶされてしまう為。
なお、System.exit()(Runtime#exit())によってプログラムを終了する場合はfinallyは呼ばれないので要注意。
tryブロックにラベルを付けてtryの中からその名前を使ってbreakすることが出来る。[2008-09-06]
label: try { System.out.println("try start"); if (true) break label; System.out.println("try end"); //この文は実行されない } finally { System.out.println("finally"); //finallyはちゃんと実行される }
tryブロックにラベルを付けてfinallyの中からその名前を使ってbreakすることが出来る。
すると、finallyの実行が中断される。
int method() { System.out.println("method start"); label: try { return 999; } finally { System.out.println("finally start"); if (true) break label; System.out.println("finally end"); //この文は実行されない } System.out.println("method end"); //returnしたはずなのに、この文が実行される return 0; }
念のため。finallyの中で新たにreturnしたりthrowしたりするのが推奨されないのと同様、breakするようなプログラミングもきっと推奨されないだろう。
catchの変数にもfinalを付けることが出来る。[2011-06-25]
} catch(Exception e) { e = new Exception("change"); throw e; } |
finalを付けない場合、変数に別の例外を代入する事が出来てしまう。 |
} catch(final Exception e) {
e = new Exception("change");
throw e;
}
|
finalを付けると、代入する箇所でコンパイルエラー。 |
catch句は複数書くことが出来る。
try { 〜 } catch (例外A e) { 〜 } catch (例外B e) { //catchは複数書くことが出来る 〜 } finally { 〜 }
しかしキャッチした例外に対して全く同じ処理をしたい場合でも処理内容は別々にコーディングする必要があって、不便。[2012-01-14]
JDK1.7で複数の例外をまとめて記述することが可能になった。
try { 〜 } catch (例外A | 例外B e) { //JDK1.7 〜 } finally { 〜 }
この場合、変数eの型(クラス)は例外Aと例外Bの共通の親クラスとなる。
JDK1.6までは、catch句でExceptionクラスを使って全例外を受けると、その例外を再スローすることは出来なかった。[2011-07-30]
public void test() {
try {
〜
} catch (Exception e) {
throw e; //JDK1.6以前はコンパイルエラー。メソッドに「throws Exception」を付けなければならない
}
}
JDK1.7では、例外の変数を書き換えない限り、そのまま再スローできるようになった。(安全な再スロー(safe rethrow))
public void test() {
try {
〜
} catch (Exception e) {
throw e; //JDK1.7ではOK
}
}
public void test() {
try {
〜
} catch (Exception e) {
e = new Exception("上書き");
throw e; //例外の変数を書き換えると再スローできない
}
}
finalの付いたcatch(catch(final Exception e)
)という形(代入不可状態)なら再スローできるということらしい。
throws宣言されている例外があっても、Exceptionで受けてExceptionで再スローできる。[2012-01-14]
public void test() throws IOException { try { 〜 } catch (Exception e) { throw e; } }
try (型 変数 = 初期化) { } try (型1 変数1 = 初期化1; 型2 変数2 = 初期化2; …) { }
try (変数1; 変数2; …) { // Java9 }
try (var 変数1 = 初期化1; var 変数2 = 初期化2; …) { // Java10 }
JDK1.7で、リソース付きtry文(try-with-resources statement)という構文が導入された。[2011-07-30]
これは、リソース(変数)の型がAutoCloseableインターフェースを継承(実装)しているとき、自動的にclose()が呼ばれるというもの。
このtryの丸括弧内には、AutoClosealbeを継承したインターフェース(実装したクラス)しか指定することは出来ない。(コンパイルエラーになる)
リソース付きtry文 | 展開されたイメージ | 備考 |
---|---|---|
try (InputStream is = new FileInputStream("")) { //isを使った処理 } |
リソースを1つ指定する例。 | |
try (InputStream is = new FileInputStream(""); OutputStream os = new FileOutputStream("")) { //is,osを使った処理 } |
リソースを2つ指定する例。 セミコロンで区切る。 close()は、後に指定した方から先に実行される。 |
|
InputStream is1 = 〜; try (InputStream is = is1) { 〜 } |
必ずしもtry()の中でnewする必要は無い。 | |
void method(final InputStream is) throws IOException
try (is) {
〜
}
} |
Java9では、try()の中で新たな変数を宣言をしなくても、 既存の変数(実質的finalである必要はある)を書ける。[2017-09-23] |
|
try()の中に書くのは変数であり、式が自由に書けるわけではない。[2018-06-03] 例えば「 try(lo.lock()) 」の様に書くことは出来ない。 |
||
try (var is = new FileInputStream("")) { 〜 } try (var is = new FileInputStream(""); var os = new FileOutputStream("")) { 〜 } |
Java10で、リソースの変数定義にvarが使えるようになった。[2018-04-21] | |
try (InputStream is = is1)
try (InputStream is = is1;)
try (InputStream is = is1;;) //これはエラー
|
丸括弧閉じの直前はセミコロンを付けてもいいし省略してもいい。 | |
try (InputStream is = null) { } |
close()呼び出し時にnullチェックされるので、そこでNullPointerExceptionが発生することは無い。 | |
try (InputStream is = null) {
is = new FileInputStream("〜"); //コンパイルエラー
}
|
try句の中でリソース変数に代入することは出来ない。 | |
try (InputStream is = new〜) { System.out.println("main"); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("finally"); } |
try { Throwable save = null; InputStream is = null; try { is = new〜; System.out.println("main"); } catch (Throwable t) { save = t; throw t; } finally { if (is != null) { try { is.close(); } catch (Throwable c) { if (save == null) { save = c; } else { save.addSuppressed(c); } throw save; } } } } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("finally"); } |
リソース付try文でもcatchやfinallyを付けることが出来る。 tryブロック→close→catchブロック→finallyブロックの順で実行される。[2017-07-11] (コンストラクターやclose()の中で起きた例外もcatchされる。[2013-06-29]) |
public class A implements AutoCloseable { public static void main(String[] args) { try (A a = new A()) { throw new RuntimeException("try-ex"); } } public void close() { throw new RuntimeException("close-ex"); } } 実行結果: Exception in thread "main" java.lang.RuntimeException: try-ex at A.main(A.java:6) Suppressed: java.lang.RuntimeException: close-ex at A.close(A.java:15) at A.main(A.java:7) |
public class A implements AutoCloseable { public static void main(String[] args) { Throwable save = null; A a = null; try { a = new A(); throw new RuntimeException("try-ex"); } catch (Throwable t) { save = t; throw t; } finally { if (a != null) { if (save != null) { try { a.close(); } catch (Throwable c) { save.addSuppressed(c); } } else { a.close(); } } } } public void close() { throw new RuntimeException("close-ex"); } } |
リソース付try文においては、try本体で例外が発生し、さらにclose()時に例外が発生すると、 本体で発生した例外にclose()時の例外が付加される。 このThrowable#addSuppressed()はJDK1.7で追加されたメソッド。 英和辞書によれば、suppressは抑える・鎮圧する・隠すという意味らしい。スローされなかったので隠されたという感じかな。 原因(Cause)の例外はThrowable#initCause()で設定するので、本筋でない例外をSuppressedで設定するのだろう。 |
public class A implements AutoCloseable { private int no; public A(int n) { this.no = n; } public static void main(String[] args) { try (A a1 = new A(1); A a2 = new A(2)) { throw new RuntimeException("try-ex"); } catch (Exception e) { e.addSuppressed(new Throwable("zzz")); throw e; //安全な再スロー } } public void close() { throw new RuntimeException("close-ex" + no); } } 実行結果: Exception in thread "main" java.lang.RuntimeException: try-ex at A.main(A.java:10) Suppressed: java.lang.RuntimeException: close-ex2 at A.close(A.java:22) at A.main(A.java:11) Suppressed: java.lang.RuntimeException: close-ex1 at A.close(A.java:22) at A.main(A.java:11) Suppressed: java.lang.Throwable: zzz at A.main(A.java:13) |
addSuppressed(例外の追加)は何度も行うことが出来る。 (自分で呼び出すことも出来ちゃう) 追加された例外は、Throwable#getSuppressed()で配列として取得できる。 |
try-with-resources構文を使った場合、try→close→catch→finallyの順に実行される。[2017-07-11]
パターン | tryブロック | → | closeメソッド | → | catchブロック | → | finallyブロック |
---|---|---|---|---|---|---|---|
正常系 | 実行 | 実行 | 実行 | ||||
コンストラクターで例外 | コンストラクターの例外をキャッチ | 実行 | |||||
tryブロックで例外 | 実行(例外発生) | 実行 | tryの例外をキャッチ | 実行 | |||
closeで例外 | 実行 | 実行(例外発生) | closeの例外をキャッチ | 実行 | |||
tryとcloseで例外 | 実行(例外発生) | 実行(例外発生→suppressedに追加) | tryの例外をキャッチ | 実行 |
synchronized (ロックオブジェクト) { 文; … }
マルチスレッドにおいて排他をかけることが出来る。[2003-07-06]
assert 真偽値; assert 真偽値 : メッセージ;
アサーション。JDK1.4からの新機能。[2003-07-06/2007-11-08]
真偽値がfalseの時にAssertionErrorが投げられる。
assert n == 0; assert n == 0 : "nは0でなければならない"; assert n == 0 : n; //nが0以外のとき nの値をスローするので、発生した例外を見るだけで値が分かる
つまり、「真偽値」の欄には「あるべき正当な値・条件」を記述する。
assert文を使う為には、コンパイル時にソースバージョン1.4以降を指定する必要がある。(JDK1.4のコンパイラでは デフォルトは1.3でコンパイルする為)
> javac -source 1.4 Hoge.java
実行時にアサーションを有効にする必要がある。
> java -ea Hoge > java -enableassertions Hoge
アサーションが無効な場合、assertに書かれた真偽値の判断ロジックは一切実行されない。
すなわち、真偽値の欄には処理本体に関わるロジックを書いてはいけない。