Java Silver への道 演算子と判定構造の使用編
代入演算子
値を変数に代入するための演算子のこと
public class Sample { public static void main(String[] args) { int a = 5; int b = a += 5; // 演算によってaに代入された値は受け継がれる int c = a -= 3; int d = a *= 5; int e = a /= 5; System.out.println(b); System.out.println(c); System.out.println(d); System.out.println(e); } }
結果
10
7
35
7
*=(イコール)を記述しないと、aは初期値の「5」として演算される
数値演算のときの型について
数値を演算するとき、演算子の両側のオペランドは同じ型でなければいけない。
もしオペランドの型が異なる場合、小さいほうの型は大きいほうの型に自動変換される。
public class Sample { public static void main(String[] args) { int a = 10; short b = 10; //bはint型に変換される //a+bはint型のため、short型で記述するとコンパイルエラーが生じる int c = a + b; } }
インクリメント
インクリメント演算子「++」とデクリメント演算子「–」は、変数の値に1 を加算したり減算したりするための演算子である。
~前置インクリメント「++a」と後置インクリメント「a++」~
前置インクリメントは、先に加算し、その値をaに代入する。
後置インクリメントは、aは前の値を引き継ぎ、その後加算し、aに代入する。
public class Sample { public static void main(String[] args) { int a = 10; //()のタイミングでインクリメントされ、代入された値は引き継がれる // 10(11)+(12)12+(13)13 int b = a++ + ++a + ++a; System.out.println(b); } }
結果
35
同一性と同値性
同一性:同じインスタンスを参照している
同値性:違うインスタンスではあるけれども、同じ値である
同一性は「==」演算子で表す。戻り値はbooleanであるため、左右のオペランドのインスタンスが合致していた場合「true」が返ってくる。
同値性を確認するためのメソッドはequalsである。ただし、Objectクラスに定義されているメソッドは同一性を確認する実装になっている。そのため、設計者が自ら、どの値の一致を確認するのかequalsメソッドをオーバーライドしなければならない。
public class Sample { // フィールドの設定 private int num; private String name; // コンストラクタの設定 public Sample(int num, String name) { this.num = num; this.name = name; } public boolean equals(Object obj) { // 同一性の確認 if (obj == null) { return false; } // オーバーライドして同値性の確認 if (obj instanceof Sample) { Sample sample = (Sample) obj; return sample.num == this.num; } return false; } }
public class Main{ public static void main(String[] args) { //Sample型のインスタンスを作成 Sample a = new Sample(10, "a"); //Sample型のインスタンスを作成 Sample b = new Sample(10, "b"); //Sample型aのequalsメソッドの引数にsample型のbを入れる System.out.println(a.equals(b)); }
処理の流れ
public class Sample { private int num; private String name; public Sample(int num, String name) { this.num = num; this.name = name; } //暗黙的にSample型はObject型に変換される(Object型の方が大きいため) public boolean equals(Object obj) { // 同一性の確認 if (obj == null) { return false; } //objに入っているbがSampleと同じクラスかインスタンスであることが条件 if (obj instanceof Sample) { //Object型であるobjをSample型にキャストして変数sampleに代入(numはObjectではなくSample型に定義されたフィールドであるため) Sample sample = (Sample) obj; //sampleのnumとaに入っているnumの値を確認 return sample.num == this.num; } return false; } }
結果
true
オーバーロード
オーバーライドと似ている言葉にオーバーロードがある。
オーバーロードとは、同じメソッドでも異なる引数を定義することで、違う結果を導くというものだ。
public class Main { public static void main(String[] args) { Sample a = new Sample(10, "a"); Sample b = new Sample(10, "b"); //bはSample型である System.out.println(a.equals(b)); } }
public class Sample { private int num; private String name; public Sample(int num, String name) { this.num = num; this.name = name; } public boolean equals(Object obj) { if (obj == null) { return false; } if (obj instanceof Sample) { Sample sample = (Sample) obj; return sample.num == this.num; } return false; } //同じメソッドだが、引数をSample型にしてオーバーロードしている public boolean equals(Sample s) { return s.name == this.name; } }
結果
false
コンスタントプール
文字列リテラルは、インスタンスの生成が使いまわしされる。そのため、同じ参照となる。
public class Main { public static void main(String[] args) { String a = "sample"; // 使いまわしされる String b = "sample"; System.out.println(a == b); } }
結果
true
*ただし、new演算子を使って明示的に新しいインスタンスを生成した場合は異なる参照となる
if文・if-else文・if-else if文
- if文中の{}は省略可能で、その場合は次の1文だけが条件に合致した場合の処理として実行される
public class Main { public static void main(String[] args) { if (false) System.out.println("A"); //この部分はif文に含まれていない処理となる System.out.println("B"); } }
結果
B
- if-else文
if (条件式){ //条件に合致した時の処理 }else{ //条件に合致しなかったときの処理 }
public class Main { public static void main(String[] args) { int num = 10; if (num < 10) { // 条件に合致した時の処理 System.out.println("A"); } else { // 条件に合致しなかった時の処理 System.out.println("B"); } //2つ目のif文も実行される if (num == 10) { // 条件に合致した時の処理 System.out.println("C"); } } }
結果
B
C
- if-else if文は、複数の分岐条件を一度に記述することができる。elseとifの間で改行することができず、2つ目のif文はelse文の中にある分岐として解釈される。
public class Main { public static void main(String[] args) { int num = 10; if (num == 100) // 条件Aに合致した時の処理 System.out.println("A"); else if (10 < num) // 条件Bに合致しなかった時の処理 System.out.println("B"); else //上記の条件に合致しなかったとき //else内で分岐 if (num == 10) // 条件に合致した時の処理 System.out.println("C"); else//上記の条件に合致しなかったとき //すでに条件合致しているため以降の処理は実行されない if (num == 10) { System.out.println("D"); } } }
結果
C
三項演算子
条件に合致するかどうかで、戻す値を変更する演算子である。
真偽値 ? trueの場合に評価する式 : falseの場合に評価する式
public class Main { public static void main(String[] args) { String a = "A"; String b = "B"; //AとBの同値性を確かめtrueであればno、falseであればyes String c = a.equals(b) ? "no" : "yes"; System.out.println(c); } }
結果
yes
switch文
条件によって分岐するif文に対してswitch文は値によって処理を分岐する。
case値にマッチする処理が実行され、処理が終わればbrakeを使ってswitch文を抜けるようにする。一致するものがなかったときはdefault文以降が実行される。
switch (条件文) { case 値: 処理 break; case 値: 処理 break; default:処理 break; }
~条件式のルール~
条件式が戻せる値の型には制限があり、以下のような種類がある
種類 | 値名 |
---|---|
int型以下の整数型とそのラッパークラス | int/Integer/short/Short/byte/Byte |
文字と文字列 | char/Character/String |
列挙型 | enum |
~case値のルール~
- 条件式が戻す型と同じ型か互換性があること
- 定数であるか、コンパイル時に値を決めることができること
- nullでないこと
「定数であること」…final宣言された変数かもしくはリテラルを表す
~breakを記述しなかった場合~
条件に合致したあと以降に現れるすべてのcase式の処理がbreakが現れるまで実行される。
そのときdefault式も対象となる。
public static void main(String[] args) { int num = 10; switch (num) { case 9: System.out.println("yeah"); case 10:// 条件に合致したため処理を実行 System.out.println("A"); // breakが記述されていないため以降の処理がすべて実行される case 11: System.out.println("B"); case 12: System.out.println("C"); default:// default式も対象となる System.out.println("D"); } } }
結果
A
B
C
D