演算子の優先順位
演算子の優先順位 とは
Java の演算子には算数と同じように優先順位がある。式が長くなったときに、どの計算が先に走るかを正しく読める力を身につけよう。
式を正しく読むための地図
小学校で「掛け算は足し算より先」と習ったのを覚えていますか。1 + 2 * 3 の答えは 9 ではなく 7 でしたね。実は Java の世界でもまったく同じルールが採用されています。式の中に + - * / % のような記号がいくつも並んだとき、それぞれの記号には「先に計算される順番」が決まっていて、これを 演算子の優先順位 (operator precedence) と呼びます。
演算子の優先順位は、数式を「左から右に順番に」読んでしまう初心者がいちばん最初にハマる落とし穴のひとつです。
Javaの式は人間の会話とは違い、書いた順番ではなく 優先順位の高い演算子から 評価されます。ここを押さえないと、足し算と掛け算が混じったコードで永遠にバグります。
小さな式ならミスもしませんが、a + b * c - (a + b) * c / 2 のように記号が増えると、頭の中だけで追うのは大変です。プロのエンジニアでも「優先順位の表を覚えている」というよりは「迷ったらカッコ ( ) で囲む」習慣でカバーしています。
優先順位の早見表
まずは Java の代表的な演算子の優先順位を、上から高い順に並べた表を見ておきます。
- 1 位 —
( )(グルーピング)、a[i](配列アクセス)、メソッド呼び出しf(x) - 2 位 — 単項演算子
++--!+a-a(符号反転) - 3 位 — 乗除算
*/% - 4 位 — 加減算
+- - 5 位 — 比較
<<=>>= - 6 位 — 等価
==!= - 7 位 — 論理 AND
&& - 8 位 — 論理 OR
|| - 9 位 — 三項演算子
? : - 10 位 — 代入
=+=-=*=/=%=
大事なポイントは、カッコ ( ) がいちばん強い ことと、代入 = がいちばん弱い ことです。これだけ覚えておけば、int x = a + b * c; のような式で「右側が全部計算されてから x に入る」のが自然に読めるようになります。
表を全部丸暗記する必要はありません。実務では「
*/%が先で+-が後」「比較<>より論理&&||が後」あたりだけ頭に入れて、迷ったらカッコを足す、で十分戦えます。
同じ優先順位の演算子はどう動くか — 結合性
優先順位が同じ演算子が並んだときの読み方が 結合性 (associativity) です。Java の演算子は、ほとんどが 左から右 へ順に評価されます。
Java
int a = 100 / 10 / 2; // (100 / 10) / 2 = 5
int b = 12 - 5 - 3; // (12 - 5) - 3 = 4例外は 代入 = と単項演算子と三項演算子 で、こちらは 右から左 へ結合します。
Java
int x, y, z;
x = y = z = 7; // x = (y = (z = 7));
// すべての変数に 7 が代入されるx = y = z = 7 は左から読むと意味不明ですが、右から組み立てると z = 7 の結果 (7) が y に代入され、さらに y の値 (7) が x に代入される、という流れになります。これは Java だけでなく C 系の多くの言語に共通の作法です。
括弧で意図を明示する
優先順位を「全部覚える」よりも、迷ったら カッコ ( ) で囲む のが現場の常套手段です。カッコは優先順位 1 位なので、中の式が必ず先に評価されます。
Java
int a = 10 + 3 * 2; // 16 (3 * 2 が先)
int b = (10 + 3) * 2; // 26 (10 + 3 が先)
int c = 100 / 10 / 2; // 5 (左から)
int d = 100 / (10 / 2); // 20 (右の () が先)カッコは「冗長だから付けない」ではなく「読み手のために付ける」もの。
a + b * c - d / eよりもa + (b * c) - (d / e)のほうが、3 ヶ月後の自分やレビュアーに優しい書き方です。コンパイル後のバイトコードは同じなので、性能上のペナルティもありません。
ここで、今回の課題で使う式 a + b * c - (a + b) * c / 2 が a=2, b=3, c=4 のときにどう評価されるかを Mermaid で追ってみます。
( ) を最優先で潰してから、* / を左から、最後に + - を左から、という順序が見えてきました。慣れるまでは紙に書き出して確認するのがいちばんの近道です。
コード例で動きを確認する
まずはシンプルな例から。+ と * が混ざった式を、カッコの有無で比べてみます。
Java
public class PriorityDemo {
public static void main(String[] args) {
int a = 2 + 3 * 4; // 14 (3 * 4 が先)
int b = (2 + 3) * 4; // 20 (2 + 3 が先)
int c = 10 - 6 / 2; // 7 (6 / 2 が先)
int d = (10 - 6) / 2; // 2 (10 - 6 が先)
System.out.println(a + " " + b + " " + c + " " + d);
}
}次は今回の課題に近い形の式を、わざと冗長にカッコを足したバージョンと並べて見比べてみます。
Java
public class Equivalent {
public static int withoutParen(int a, int b, int c) {
return a + b * c - (a + b) * c / 2;
}
public static int withParen(int a, int b, int c) {
// 同じ式を、優先順位を全部カッコで明示した版
return (a + (b * c)) - (((a + b) * c) / 2);
}
}2 つのメソッドはまったく同じ結果を返します。カッコを足したほうは冗長に見えますが、* と / がどう絡むか、+ と - がどこで合流するかが一目でわかります。最初のうちはこのくらい丁寧に書いて、慣れたら自然な書き方に戻していきましょう。
よくある間違い
演算子の優先順位でハマるパターンは、ほぼ次の 3 つに分類できます。
a + b * cを(a + b) * cと勘違いする — 数学の2 + 3 × 4と同じく、*が先です。Javaの式を読むときは、まず*/%を探して心の中で先にカッコを補うクセを付けましょうx = y = zの結合方向を逆にする — 代入=は 右から左 に結合するのでx = (y = z)と読みます。(x = y) = zではないので注意。さらに代入式は=の右辺の値を返すため、if ((line = reader.readLine()) != null)のような書き方も存在します- ビット演算
&|と論理演算&&||の優先順位を混同する — ビット演算&^|は等価演算子==!=や関係演算子<>よりも優先順位が低く、論理演算子&&||よりは高い位置にあります。ただし&と&&の本質的な違いは優先順位だけでなく 短絡評価 (short-circuit evaluation) の有無です。&&は左辺がfalseの時点で右辺を評価しませんが、&は必ず両辺を評価します。たとえばif (obj != null & obj.getValue() > 0)のように&を使うと、objがnullでも右辺が実行されてNullPointerExceptionが発生します。論理判定では必ず&&||を使ってください
もうひとつ地味に多いのが、- の単項用法と二項用法の混同です。-a + b は (-a) + b (単項の - が先)、a - b は a - b (二項の引き算) と、文脈によって挙動が変わります。Java のコンパイラは賢く読み分けてくれますが、人間がコードを読むときは「マイナスの後ろにスペースが無ければ単項っぽい」くらいの感覚で見ておくとミスが減ります。
優先順位で迷ったときの黄金ルールは「カッコで囲んで動かす」「
IDEや Playground で実際に走らせて結果を見る」の 2 つです。頭の中だけで完璧に追おうとせず、手を動かして確認する習慣のほうが、長い目で見ると圧倒的に早く正確に書けるようになります。
やってみよう
それでは課題に挑戦します。今回のメソッドは calcExpression(int a, int b, int c) で、a + b * c - (a + b) * c / 2 を計算した結果を int で返します。
- 右側のエディタを開く
return a + b * c - (a + b) * c / 2;の 1 行を書く- 「実行」ボタンを押して、テストが緑になることを確認する
わざと優先順位を間違えた return (a + b) * c - (a + b) * c / 2; のような式を書いて、テストが fail する様子を観察するのもおすすめです。エラーの結果を見ることで「ああ、ここで * が先に走るんだ」という感覚が手に入ります。
慣れてきたら、自分でカッコを足した return (a + (b * c)) - (((a + b) * c) / 2); でも同じ結果になることを確認してみてください。「カッコを足しても結果が変わらない式」は、自分が優先順位を正しく理解できている証拠になります。優先順位が頭に入ってくると、後で出てくる for ループの条件式や、if の複雑な判定がぐっと読みやすくなります。
よくある質問
Q. Java と他言語の文法はどう違いますか?
A. Java は静的型付けで、変数宣言時に型を明示します(int x = 0;)。中括弧でブロックを表し、文末にセミコロンが必要です。Python の動的型付けや JavaScript の柔軟さに慣れていると最初は窮屈ですが、コンパイル時にバグが発見できる安全性がメリットです。
Q. コードが動かないときに最初に見るべき場所は?
A. コンパイルエラーは行番号とエラー種別(cannot find symbol、incompatible types など)が表示されます。実行時例外はスタックトレースの最初の at ... が原因行です。IDE(IntelliJ / VS Code)の警告も丁寧に潰すと、半分のバグは未然に防げます。
Q. Java の習得後に学ぶべき技術は何ですか?
A. 基本構文を抑えたら java-intermediate(コレクション / ジェネリクス / Stream / 例外)に進み、Spring Boot や Web 開発に展開するのが王道です。クラウド時代は Kotlin / Scala への展開も視野に入りますが、まずは Java 標準を固めるのが効率的です。
次のレッスン
次は 文字列の連結 で、Java の演算子には算数と同じように優先順位がある を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 優先順位 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 優先順位 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- クラス名は
Solution、メソッド名はcalcExpression、引数はint a, int b, int cの 3 つ、戻り値の型はintにすること - 計算する式は
a + b * c - (a + b) * c / 2で、優先順位は( )→*/→+-の順、同じ優先順位は左から右に評価する / 2はint同士の割り算なので、小数部分は切り捨てて構わない (キャストしてdoubleにしない)
入出力例
test-cases.txt
calcExpression(2, 3, 4) → 4
calcExpression(1, 1, 1) → 1
calcExpression(10, 5, 2) → 5
calcExpression(0, 0, 0) → 0
calcExpression(4, 2, 6) → -2
calcExpression(1, 1, 3) → 1