SJ blog
beginner
S

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

演算子の評価順序と落とし穴 — i++とi++の違い・短絡評価・整数演算の罠

前置/後置インクリメントの評価タイミングの違い、&&と&の短絡評価の仕組み、整数除算の切り捨て、オーバーフローの挙動を試験頻出パターンとともに解説。

一言結論

i++は「今の値を使ってからインクリメント」、++iは「インクリメントしてから使う」。&&と||は短絡評価するが&と|はしない。整数除算はゼロ方向への切り捨てでオーバーフローは黙って折り返す。

前置と後置インクリメントの違い

インクリメント演算子++には2種類ある:

int i = 5;
System.out.println(i++);  // 5(今の値を返してからインクリメント)
System.out.println(i);    // 6

int j = 5;
System.out.println(++j);  // 6(インクリメントしてから値を返す)
System.out.println(j);    // 6

後置(i++):式の評価では「今の値」を返し、その後インクリメントする
前置(++i):先にインクリメントし、インクリメント後の値を式の評価で使う

単独で使うだけなら差はないが、式の中で使うときに差が出る:

int a = 3;
int b = a++ * 2;
// a++ は「3を使ってからaを4にする」
// b = 3 * 2 = 6
// a = 4
System.out.println(a);  // 4
System.out.println(b);  // 6

int c = 3;
int d = ++c * 2;
// ++c は「cを4にしてから4を使う」
// d = 4 * 2 = 8
// c = 4
System.out.println(c);  // 4
System.out.println(d);  // 8

式の中で複数回使うケース

int i = 0;
int result = i++ + i++;

これはどうなるか。左から右に評価される

i=0の状態で i++ を評価 → 0を返してiを1にする
i=1の状態で i++ を評価 → 1を返してiを2にする
result = 0 + 1 = 1
i = 2
System.out.println(result);  // 1
System.out.println(i);       // 2

試験ではこういった「i++を複数回使うコード」が頻出だ。ポイントは「後置は今の値を返して後でインクリメント」を一つずつ丁寧に追うこと。

デクリメント--も同じルールだ:

int x = 5;
int y = x-- - --x;
// x-- : 5を返してxを4にする
// --x : xを3にして3を返す
// y = 5 - 3 = 2
// x = 3

代入式は値を返す

Javaでは代入(=)も式として値を返す:

int a;
int b = (a = 5) + 3;
// a = 5 → aに5を代入し、値として5を返す
// b = 5 + 3 = 8

System.out.println(a);  // 5
System.out.println(b);  // 8

これを使って複数変数を一度に初期化できる:

int a, b, c;
a = b = c = 0;
// c = 0(cに0を代入し0を返す)
// b = 0(bに0を代入し0を返す)
// a = 0
// 右から左への代入

代入演算子は右結合(右から左に評価される)だ。

短絡評価(Short-Circuit Evaluation)

&&||は「短絡評価」を行う:

&&(AND):左辺がfalseなら、右辺を評価しない

int x = 5;
boolean result = (x > 10) && (++x > 0);
// (x > 10) → false
// falseなら結果はfalse確定。右辺は評価しない
System.out.println(result);  // false
System.out.println(x);       // 5(右辺の ++x が実行されていない!)

||(OR):左辺がtrueなら、右辺を評価しない

int y = 5;
boolean r = (y < 10) || (++y > 0);
// (y < 10) → true
// trueなら結果はtrue確定。右辺は評価しない
System.out.println(r);  // true
System.out.println(y);  // 5(右辺の ++y が実行されていない!)

これが試験の罠になる。「++が実行されているように見えて実は実行されていない」というパターンだ。

&|は短絡しない

int z = 5;
boolean b = (z > 10) & (++z > 0);
// (z > 10) → false でも右辺は必ず評価する
// (++z > 0) → zが6になる
System.out.println(b);  // false
System.out.println(z);  // 6(右辺が必ず評価される)

&|は「両辺を必ず評価する」。boolean同士で使う場面は少ないが(ビット演算と混同しやすいため)、試験では区別を問われる。

整数除算は「ゼロ方向への切り捨て」

System.out.println(7 / 2);     // 3(切り捨て)
System.out.println(-7 / 2);    // -3(ゼロ方向)
System.out.println(7 / -2);    // -3(ゼロ方向)
System.out.println(-7 / -2);   // 3

「切り捨て」というとマイナスのときに間違えやすい。-7 / 2 = -3.5の絶対値を切り捨てると-3だ(-4ではない)。Math.floor(-3.5) = -4とは違うので注意。

剰余(%)もゼロ方向

System.out.println(7 % 2);    // 1
System.out.println(-7 % 2);   // -1(符号は左辺に依存)
System.out.println(7 % -2);   // 1(符号は左辺に依存)
System.out.println(-7 % -2);  // -1

剰余の符号は「左辺(被除数)の符号と同じ」と覚えるといい。

整数のゼロ除算は例外

int x = 5 / 0;  // ❌ ArithmeticException: / by zero

浮動小数点のゼロ除算は例外ではなくInfinityNaNになる:

System.out.println(5.0 / 0.0);   // Infinity
System.out.println(-5.0 / 0.0);  // -Infinity
System.out.println(0.0 / 0.0);   // NaN

オーバーフローは黙って折り返す

int max = Integer.MAX_VALUE;  // 2147483647
System.out.println(max + 1);  // -2147483648(!)

Integer.MAX_VALUE + 1はエラーにならない。ビット列が「折り返し」てInteger.MIN_VALUEになる:

int の最大値: 01111111 11111111 11111111 11111111 = 2147483647
+1すると:    10000000 00000000 00000000 00000000 = -2147483648(2の補数表現)

Javaは整数のオーバーフローを検出せず例外を投げない。計算は黙って間違った値になる。これは言語仕様の設計判断(常に検出するとパフォーマンスが落ちる)だ。

大きな数を扱う場合はlongを使うか、java.math.BigIntegerを使う。

演算子の優先順位

試験でよく問われるもの:

// 乗除は加減より先
int a = 2 + 3 * 4;  // 2 + 12 = 14(3*4が先)

// 単項マイナスとインクリメント
int b = -i++;        // -(i++) であって (-i)++ ではない
                     // 後置++の優先度は単項演算子より高い

// 比較より算術が先
boolean c = 1 + 2 == 3;  // (1+2) == 3 → true

// instanceof の優先度
boolean d = "hello" instanceof String && true;  // (("hello" instanceof String) && true)

全体の優先順位表を丸暗記するより「分からないときは()で明示する」のが実用的だ。試験では典型的なパターンだけ押さえれば十分。

まとめ

後置 i++  = 今の値を式で使い、その後インクリメント
前置 ++i  = 先にインクリメントし、インクリメント後の値を式で使う
代入式    = 値を返す(a = 5 は「5」を返す)。右結合

短絡評価:
  && : 左がfalse → 右は実行しない
  || : 左がtrue  → 右は実行しない
  &  : 両辺を必ず実行
  |  : 両辺を必ず実行

整数除算    = ゼロ方向への切り捨て(-7/2 = -3。-4ではない)
剰余の符号  = 左辺(被除数)と同じ
整数/0     = ArithmeticException
浮動小数/0 = Infinity / NaN(例外なし)
オーバーフロー = 黙って折り返す(例外なし)