注目記事

【Javaコード入門】基本文法_⑤演算子

演算子(オペレーター)とは、与えられた変数やリテラルに対して演算を行うための記号のことです。演算子によって処理される対象のことを被演算子(オペレーター)と言います。演算子は、(1)算術演算子、(2)代入演算子、(3)比較演算子、(4)論理演算子、(5)ビット演算子に分類できます。

算術演算子

四則演算をはじめ、主に数学的な演算を行う演算子です。

 

主な算術演算子
演算子 概要
+ 加算 1 + 2 // 3
減算 5 – 2 // 3
* 乗算 3 * 4 // 12
/ 除算 5.0 / 2 // 2.5
% 剰余 5 % 2 // 1
+ 文字列の連結 “A” + “B” // AB
++ インクリメント x = 1;x++; // 2
– – デクリメント x = 1;x- -; // 0

以下に、主な演算子について、いくつかポイントとなる点をまとめます。

(1)インクリメント/デクリメント演算子

インクリメント/デクリメント演算子は、演算子をオペランドの前後いずれに置くかによって処理が異なります。x++は代入した後にインクリメントしますが、++xは代入する前にインクリメントします。

OpeIncrement.java
  1. package com.example.mynavi.basic;
  2.  
  3. public class OpeIncrement {
  4.  public static void main(String[​] args) {
  5.   int x = 1;
  6.   int y = x++;
  7.   System.out.println("yは" + y);  // 結果:yは1
  8.   int a = 1;
  9.   int b = ++a;
  10.   System.out.println("bは" + b);  // 結果:bは2
  11.  }
  12. }

(2)浮動小数点数の演算

浮動小数点数は、内部的に2進数で演算されているという性質上、演算に正しい結果を期待してはいけません。例えば10進数の0.1ですら、2進数では0.000110011…のような無限循環小数となってしまうからです。このため、たとえば以下のようなごく単純な計算ですら、正しい解を得られません。

  1. System.out.println(1.0 - 0.1 - 0.2 - 0.3); // 結果:0.39999999999999997

厳密な結果を得る場合には、BigDecimalというクラスを利用してください。以下は、うえのコードをBigDecimalクラスで書き換えたものです。

OpeFloat.java
  1. package com.example.mynavi.basic;
  2.  
  3. import java.math.BigDecimal;
  4.  
  5. public class OpeFloat {
  6.  
  7.  public static void main(String[​] args) {
  8.   BigDecimal bd1 = new BigDecimal("1.0");
  9.   BigDecimal bd2 = new BigDecimal("0.1");
  10.   BigDecimal bd3 = new BigDecimal("0.2");
  11.   BigDecimal bd4 = new BigDecimal("0.3");
  12.   System.out.println(bd1.subtract(bd2).subtract(bd3).subtract(bd4));
  13.  // 結果:0.4
  14.  }
  15. }

subtractメソッドが減算を意味します。その他、add(加算)、multiply(積算)、divide(除算)などを利用できます。メソッドの呼び出しになった分、コードは冗長になりますが、誤差は確かに解消されています。

(3)非数の加算を追加

「+」演算子は、オペランド(演算される値)のいずれかが文字列の場合は、文字列連結演算子となるので、注意してください。

  1. System.out.println("x" + 1);  // 結果:x1
  2. System.out.println(1 + "x");  // 結果:1x
  3. System.out.println("2" + 1);  // 結果:21

代入演算子

変数に値を代入します。算術演算やビット演算と組み合わせた複合代入演算子もあります。

 

主な代入演算子
演算子 概要
= 右辺の値を左辺に代入 i = 10
+= 右辺の値を加算した結果を代入 i += 5 // 15
-= 右辺の値を減算した結果を代入 i -= 5 // 5
*= 右辺の値を乗算した結果を代入 i *= 5 // 50
/= 右辺の値で除算した結果を代入 i *= 5 // 2
%= 右辺の値で除算した余りを代入 i %= 5 // 0
&= 右辺の値で論理積演算した結果を代入 i &= 5 // 0
|= 右辺の値で論理和演算した結果を代入 i |= 5 // 15
^= 右辺の値で排他的論理和演算した結果を代入 i ^= 5 // 15
<<= 右辺の値だけ左シフトした結果を代入 i <<= 5 // 320
>>= 右辺の値だけ右シフトした結果を代入 i >>= 5 // 0
>>>= 右辺の値だけ右シフトした結果を代入 i >>>= 5 // 0

以下に、主な演算子について、いくつかポイントとなる点をまとめます。

(1)基本型と参照型の違い

「データ型」節でも触れたように、Javaのデータ型は大きく基本型と参照型に分類できます。基本型と参照型との違いは、変数に格納する方法です。基本型は値そのものを変数に格納するのに対して、参照型では値そのものは別の場所に格納しておいて、変数にはその場所を表す値(参照値)を格納します。
このような違いによって、プログラミング上でもさまざまな違いが発生するのですが、その代表的な状況が「代入」の局面です。以下の例を見てみましょう。

OpeEqual.java
  1. package com.example.mynavi.basic;
  2.  
  3. import java.util.Arrays;
  4.  
  5. public class OpeEqual {
  6.  
  7.  public static void main(String[​] args) {
  8.   // 基本型の代入
  9.   int x = 1;
  10.   int y = x;
  11.   y++;
  12.   System.out.println(x);  // 結果:1
  13.   System.out.println(y);  // 結果:2
  14.  
  15.   // 参照型の代入
  16.   int[​] data1 = { 1, 2, 3 };
  17.   int[​] data2 = data1;
  18.   data2[0] = 100;
  19.   System.out.println(Arrays.toString(data1)); // 結果:[100, 2, 3]
  20.   System.out.println(Arrays.toString(data2)); // 結果:[100, 2, 3]
  21.  }
  22. }

まず、基本型の代入については、値をそのままコピーするだけなので直観的です。代入された側(変数y)の値を変更したとしても、この変更が代入元の変数xに影響することはありません。
注目すべきは、参照型の代入です。参照型では代入に際しても、その参照値を代入するだけです。つまり、代入した時点で代入先data2、代入元data1共に同じ値を指しているということです。よって、片方の値を書き換えた時には、この値はdata1/data2双方に反映されることになります。

(2)定数への再代入

「定数」項で定数は一度代入した値を変更することはできないと述べました。しかし、それには少し嘘が混じっています。正しくは「再代入できない」であって、必ずしも変更できないわけではありません。具体的には、以下のような場合です。

OpeConst.java
  1. package com.example.mynavi.basic;
  2.  
  3. import java.util.Arrays;
  4.  
  5. public class OpeConst {
  6.  public static void main(String[​] args) {
  7.   final int[​] data = { 1, 2, 3 };
  8.   System.out.println(Arrays.toString(data));  // 結果:[1, 2, 3]
  9.   data[0] = 100;
  10.   System.out.println(Arrays.toString(data));  // 結果:[100, 2, 3]
  11.  }
    }

これは配列自体(その参照値)はそのままに配列の中身だけを変更しているので、問題ありません。ただし、以下のような代入は配列そのもの(参照値)を再代入しているので、エラーとなります。

  1. data = { 100, 200, 300 };

比較演算子

左辺と右辺の値を比較し、その結果をtrue/falseで返します。一般的には、制御構文と合わせて利用します。

主な比較演算子
演算子 概要
== 左辺と右辺が等しければtrue 5 == 5 // true
!= 左辺と右辺が等しくなければtrue 5 != 5 // false
< 左辺が右辺より小さければtrue 5 < 7 // true
<= 左辺が右辺以下であればtrue 5 <= 3 // false
> 左辺が右辺より大きければtrue 7 > 5 // true
>= 左辺が右辺以上であればtrue 5 >= 7 // false
?: 「条件式 ? 式1 : 式2」。条件式がtrueなら式1、falseなら式2 i >= 1 ? “真” : “偽”

いずれも直観的に理解しやすい演算子ですが、「==」演算子について、少しだけ補足しておきます。
「==」演算子は、正しくは同一性(Identity)――同じモノであるかを確認するための演算子です。基本型では、値が等しければ同じものであるので問題ありません。しかし、参照型では同じ値であっても、同じものであるとは限りません。たまたま同じ値を持っているだけで、異なる場所に格納されている異なるものである可能性があるからです。
例えば以下の例で、変数data1/data2は同じ値を持った配列ですが、異なる場所で作られた異なるものなので、「==」演算子はfalseを返します。

OpeIdentity.java
  1. package com.example.mynavi.basic;
  2.  
  3. public class OpeIdentity {
  4.  public static void main(String[​] args) {
  5.   final int[​] data1 = { 1, 2, 3 };
  6.   final int[​] data2 = { 1, 2, 3 };
  7.   System.out.println(data1 == data2);  // 結果:false
  8.  }
  9. }

参照型を比較するには、同値性(Equivalence)を確認するためのequalsメソッドを利用してください。

  1. System.out.println(Arrays.equals(data1, data2)); // 結果:true

論理演算子

複数の条件式を論理的に結合し、その結果をtrue/falseで返します。

主な論理演算子
演算子 概要
&& 左辺右辺がともにtrueの場合はtrue true && false // false
|| 左辺右辺どちらかがtrueの場合はtrue true || false // true
! 式がfalseの場合はtrue !false // true

論理演算子を利用する場合には、ショートカット演算という言葉を理解しておく必要があります。以下のような式を例にしてみましょう。

  1. a == 1 && b == 2
  2. x == 1 || y == 2

「&&」演算子は左式がfalseの場合は右式の値に関わらず、式全体がfalseとなります。同様に「||」演算子は左式がtrueの場合は右式の値に関わらず、式全体がtrueとなります。そのような状況では、右式が評価(実行)されずにスキップされるのです。これがショートカット演算の意味です(上の例であれば、aが1以外、xが1の場合に、それぞれ右式が実行されません)。
これを利用することで、左式がtrue/falseの場合にだけ、右式を実行するようなコードを記述することも可能です。

ビット演算子

整数を2進数で表した時、それぞれの桁を論理演算し、その結果を返します。

主なビット演算子
演算子 概要
& 論理積。左右双方の式にセットされているビット 10 & 2 // 2
| 論理和。左右いずれかの式にセットされているビット 10 | 2 // 10
^

排他的論理和。左右いずれかにセットされており、
かつ、双方にはセットされていないビット

10 ^ 2 // 8
~ ビットを反転 ~10 // -11
<< ビットを左シフト 10 << 2 // 40
>> ビットを右シフト 10 >> 2 // 2
>>> ビットを右シフト、左端は0埋め 10 >>> 2 // 2

演算子の優先順位と結合則

式の中に複数の演算子が含まれている場合、どの順序で演算するかを決めるのが優先順位です。以下に、優先順位の高いものからまとめます。

  • (引数)、[​]、.、new、++、–(後置)
  • !、~、+、-(単項)、++、–(前置)
  • ( キャスト )
  • *、/、%
  • +、-(算術)
  • <<、>>、>>>
  • >、>=、<、<=、instanceof
  • ==、!=
  • &
  • ^
  • |
  • &&
  • ||
  • ?:
  • =、+=、-=、*=、/=、% =、&=、^=、|=、<<=、>>=、>>>=

ただし、これだけある演算子の優先順位を正確に覚えることは、生産的なことではありません。複雑な式では、適宜、塊を丸かっこで括るようにしましょう。丸かっこで括られた部分は優先して演算されます。
また、優先順位が等しい演算子が並んでいる場合には、結合則と呼ばれるルールに沿って演算されます。結合則が「左⇒右」の場合は左から順に、「右⇒左」の場合は右から順に、それぞれ演算されます。

(1)左⇒右

  • 算術演算子(+、-、*、/、%、++(後置)、–(後置))
  • 比較演算子(<、<=、>、>=、==、!=、===、!==)
  • 論理演算子(&&、||)
  • ビット演算子(<<、>>、>>>、&、^、|)

(2)右⇒左

  • 算術演算子(++(前置)、–(前置))
  • 代入演算子(=、+=、-=、*=、/=、% =、&=、^=、|=)
  • 論理演算子(!)
  • ビット演算子(~)
  • 条件演算子(?:)
基本文法_目次
変数を宣言する
定数を宣言する – finalキーワード
データ型
型キャスト
演算子
コメント

監修

山田祥寛(有限会社 WINGSプロジェクト)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NEC にてシステム企画業務に携わるが、2003年 4 月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト(https://wings.msn.to/)」の代表でもある。主な著書に『改訂 3 版JavaScript 本格入門』(技術評論社)、「独習シリーズ(C#・Python・PHP・Ruby・JSP&サーブレットなど)」(翔泳社)、「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravel など)」(Amazon Kindle)など。売り上げの累計は100万部を超える。