こんにちは、運営者のハックです。
今回はデイトラJava Silver対策編「第3章の対策」の学習記録を紹介します。
第3章 演算子と判定構造
第3章では主に足し算や掛け算などの計算を行う場合に使用される演算子と、条件に応じて処理を変更することができる条件文(分岐文)について学習します。
引き続き解説の参考と例題の引用は【黒本】と呼ばれる「徹底攻略Java SE 11 Silver問題集」から行っていきます。
演算子と優先度
Javaでよく使われる代入演算子にはいくつかの種類があります。
代入演算子は変数に値を割り当てるために使用され、インクリメントとデクリメント演算子は、変数の値を1増やす、または1減らすために使われる演算子です。また、関係演算子は、2つの値を比較して、その関係性に基づいた真偽値(trueまたはfalse)を返すために使われ、論理演算子は、真偽値(trueまたはfalse)を操作するための演算子です。
それぞれの演算子の種類と特徴をまとめました。
代入演算子
代入演算子 | 使用例 | 意味 |
---|---|---|
= | a=5 | aに5を代入 |
+= | a += 3 | aにa+3の結果を代入(a=a+3と同じ) |
-= | a -= 2 | aにa-2の結果を代入(a=a-2と同じ) |
*= | a *= 2 | aにa*2の結果を代入(a=a*2と同じ) |
/= | a /= 2 | aにa/2の結果を代入(a=a/2と同じ) |
%= | a %= 3 | aにa%3の結果(余りの数を算出)を代入(a=a%3と同じ) |
インクリメントとデクリメント演算子
インクリメント・ デクリメント演算子 | 使用例 | 意味 |
---|---|---|
前置きインクリメント | ++a | 先に変数の値を1増やし、その後その値を使用 例:int a = 5; int b = ++a;とすると、aは6になり、bも6になる |
後置きインクリメント | a++ | 変数の値を使用してから、その値を1増やす 例:int a = 5; int b = a++;とすると、bは5になり、その後aは6になる |
前置きデクリメント | –a | 先に変数の値を1減らし、その後その値を使用 例:int a = 5; int b = –a;とすると、aは4になり、bも4になる |
後置きデクリメント | a– | 変数の値を使用してから、その値を1減らす 例:int a = 5; int b = a–;とすると、bは5になり、その後aは4になる |
関係演算子
関係演算子 | 使用例 | 意味 |
---|---|---|
== | a==b | 「等しい」aとbが等しければtrue |
!= | a!=b | 「等しくない」aとbが等しくなければtrue |
> | a >b | 「~より大きい」aがbよりも大きければtrue |
>= | a >=b | 「~以上」aがb以上であればtrue |
< | a <b | 「~より小さい」aがbよりも小さければtrue |
<= | a <=b | 「~以下」aがb以下であればtrue |
instanceof | a instanceof b | aがbと同じクラスかbのサブクラスのインスタンスであればtrue |
論理演算子
論理演算子 | 使用例 | 意味 |
---|---|---|
&(AND演算子) | a & b | 二つの条件がともに真(true)の場合にのみ、全体の結果が真(true)になる 左オペランド式がfalseであっても右オペランドを評価する |
&&(AND演算子) | a && b | 二つの条件がともに真(true)の場合にのみ、全体の結果が真(true)になる 左オペランド式がfalseなら右オペランドは評価しない |
|(OR演算子) | a | b | 二つの条件のうち、少なくとも一方が真(true)であれば、全体の結果が真になる 左オペランド式がtrueであっても右オペランドを評価する |
||(OR演算子) | a || b | 二つの条件のうち、少なくとも一方が真(true)であれば、全体の結果が真になる 左オペランド式がtrueなら右オペランドは評価しない |
!(NOT演算子) | !a | 単一の条件の真偽値を反転、真(true)なら偽(false)に、偽なら真になる 例:aがtrueであればfalse、falseであればtrue |
論理演算子とか高校の数学でやったよーな気がするのにゃ。
演算子の優先順位
演算子にはそれぞれ決まった優先順位があり、通常はかけ算や割り算のような乗算演算子が足し算や引き算のような加算演算子よりも先に計算されます。演算子の優先順位を変更したい場合は、括弧を使います。括弧内の式は他のどんな演算子よりも先に計算されます。また、インクリメント・デクリメント演算子も最優先で計算されます。
代理演算子や関係演算子などは学生時代に習っていることなので、理解するのは大変ではありませんが、インクリメント・デクリメント演算子はプログラミングを学習して初めて触れた演算子です。
インクリメント・デクリメント演算子は特に説明だけ見てもイマイチ分からないので、実際の例題を見てみましょう。
次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)
1. public class Main{ 2. public static void main(String[] args) { 3. int a = 10; 4. int b = a++ + a + a-- - a-- + ++a; 5. System.out.println(b); 6. } 7. }
A. 7が表示される。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. 32が表示される。
C. 33が発生する。
D. 43が発生する。
E. コンパイルエラーが発生する。
F. 実行時に例外がスローされる。
うわぁ…インクリメント・デクリメント演算子が入り混じってるのにゃ…
えーとぉ…
まずa++だから最初のaは10で、a++の後にaが11になるんだにゃ。
だから「a++ + a」の部分は「10 + 11」で21になるのにゃ。
次はa–だから先に21に11を足してからaから1を引くのにゃ。
だから「a++ + a + a– 」の時点の合計値は32でaの値は10なのにゃ。
その次は「- a–」だから、先に10を引いてからaから1を引くのにゃ。
だから「a++ + a + a– – a–」の時点の合計値は22でaの値は9なのにゃ。
最後は「+ ++a」だから、先にaに1を足して10にしてから今までの合計値に足すのにゃ。
だから22+10で合計は32、bの値は32だから正解は選択肢Bなのにゃ!
正解です!考え方もあってます!
インクリメント・デクリメント演算子の前置と後置の違いによる演算の動作順序に慣れておきましょう。
同一性と同値性とequalsメソッド
Javaでは「同じ」という言葉が2つの意味を持っています。1つ目は同じインスタンスであることを示す「同一性」、もう1つは同じ値であることを示す「同値性」です。「同一性」と「同値性」について、それぞれの特徴を確認しましょう。
同一性(Identity)
同一性かどうかは、2つの参照が文字通り同じインスタンスを指しているかどうかを確認します。つまり、2つの変数がメモリ上の同じ場所、同じインスタンスを参照している場合に、これらは「同一」であると言えます。Javaでは、== 演算子を使ってこの同一性をチェックします。
String str1 = new String("hello");
String str2 = str1;
boolean result = (str1 == str2); // true, 両者は同じインスタンスを参照しているため
同値性(Equality)
同値性かどうかは、2つのインスタンスが内容的に等しいかどうかを確認します。これは、2つのインスタンスが異なるメモリ位置にあっても、その内容(インスタンスが持つデータ)が同じであれば「同値」とみなします。インスタンスの同値性は、.equals() メソッドを使ってチェックします。
String str1 = new String("hello");
String str2 = new String("hello");
boolean result = str1.equals(str2); // true, 内容が同じであるため
簡単に言えば、同一性は「同じオブジェクトか?」を、同値性は「内容が同じか?」を見ています。実際に例題を見てどのようなコードで使用するのか見てみましょう。
次のプログラムを確認してください。
1. public class Sample{ 2. private int num; 3. public Sample(int num){ 4. this.num = num; 5. } 6. public boolean equals(Sample obj){ 7. if(obj == null){ 8. return false; 9. } 10. return this.num == obj.num; 11. } 12. }
このクラスを利用する以下のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)
1. public class Main{ 2. public static void main(String[] args) { 3. Object a = new Sample(10); 4. Object b = new Sample(10); 5. System.out.println(a.equals(b)); 6. } 7. }
A. Sampleクラスでコンパイルエラーが発生する。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. Mainクラスでコンパイルエラーが発生する。
C. 「true」と表示される。
D. 「false」と表示される。
E. 実行時に例外がスローされる。
うーんと、「Main」クラス内の結果だけなら同値性で考えるとaとbは違うインスタンスで生成されているから「false」になるのにゃ。
でも、「Sample」クラスで「Sample」メソッドで使用するnumと「equals」メソッドで定義した「obj」が等しいって言ってるし、その「Sample」クラスからオーバーライドした「equals」を使っているから…あにゃにゃ、よく分からなくなってきたのにゃ。
実はこの例題、「Sample」クラスから「equals」をオーバーライドできていないのです。
Javaでは、インスタンス同士が同値かどうかをチェックするには、「Object」クラスの「equals」メソッドをオーバーライドして、インスタンスの内容が同じかどうかを判断するロジックを記述する必要があります。
しかし、例題のコードでは「equals」メソッドの引数が「Sample」型になっており、これはObject型の引数を取る必要があるObjectクラスのequalsメソッドのシグネチャとは異なります。
その結果、Object型の変数a
とb
で「equals」メソッドを呼び出しても、オーバーライドした「equals」メソッドではなく、「Object」クラスのデフォルトの「equals」メソッドが実行されます。
「Object」クラスのデフォルトの「equals」メソッドは、インスタンスの同一性をチェックするため、異なるインスタンスに対しては常にfalseを返します。なので正解は選択肢Dです。
internメソッド
プログラムに直接記述される文字リテラルはメモリ上の、クラスやインターフェースに関するリテラルやシンボリックな参照を格納する「コンスタントプール(定数プール)」と呼ばれる場所に保存されます。このプールには、そのクラスファイル内で一意の定数やリテラル(例えば文字列リテラル、クラスの完全限定名、メソッドやフィールドの名前など)が含まれます。
internメソッドは、JavaのStringクラスに属するメソッドで、コンスタントプール含むメモリ内の文字列を探して、再利用するためのメソッドです。internメソッドを使用するとメモリの使用効率が良くなる可能性があり、文字列の比較が==演算子を使っても正確に行えるようになります。
簡単に言うと、internメソッドを使うと、同じ内容の文字列オブジェクトがプログラム内で一つだけ存在するようになります。
具体的なコードを例題を通じて確認しましょう。
次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)
1. public class Main{ 2. public static void main(String[] args) { 3. 4. String a = "abc" 5. String b = new String(a); 6. 7. int count = 0; 8. if(a.intern() == "abc" ){ 9. count++; 10. } 11. if(b.intern() == "abc" ){ 12. count++; 13. } 14. if(a.intern() == b.intern()){ 15. count++; 16. } 17. System.out.println(count); 18. } 19. }
A. 0が表示される。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. 1が表示される。
C. 2が発生する。
D. 3が発生する。
この問題はif内の条件を満たした回数を答えたらいいのにゃ。
まず、「a.intern() == “abc”」の条件は「a.intern() 」で「abc」の文字列を戻しているから、「abc == abc」という意味になるのにゃ。だからこの条件式は「true」を返すのにゃ。
次の「b.intern() == “abc” 」は「b == “abc”」だったら「false」だけど「intern」でコンスタントプールからabcを戻しているから「true」を返すと思うのにゃ。
最後の「a.intern() == b.intern()」はそれぞれ「abc」の文字列をコンスタントプールから戻しているから「true」を戻すと思うのにゃ。
これまでに「true」を3回カウントしたから正解は選択肢Dだにゃ!
はい、その通りです!考え方もオッケーです。
if文・if-else文、switch文
if文とswitch文の基本的な文法はデイトラWeb制作初級編13~15「JavaScriptの基本文法を学ぼう(後編)」の記事で説明していますが、今回はもう少し踏み込んだ内容を学習しましょう。
if文の括弧について
Javaにおいて、if 文の条件後の実行ブロックが一行だけの場合、そのブロックを囲む中括弧 {} を省略することが可能です。例えば以下のようにコードを記載できます。
if (条件) 実行文;
ただし、実行する処理が複数行にわたる場合は、中括弧 {} で囲む必要があります。中括弧を省略した場合、if 文の条件に合致すると判断されたときに、直後の一行だけが実行されます。
switch文の条件式に使用できる型と条件
switch文では、case式に使用できる型には制限があります。使用できるのは以下の型です。
- 整数型(基本型:byte、short、int、char 参照型:Byte、Short、Integer、Character)
- 列挙型(
enum
) - 文字列型(
String
)
逆に使用できない方は以下の通りです。
- 浮動小数点型 (float, double及びそれらのラッパークラスFloat, Double)
→値の範囲が広すぎるため - 長整数型 (long及びそのラッパークラスLong)
→値の範囲が広すぎるため - ブール型 (boolean及びそのラッパークラスBoolean)
→trueかfalseの2つの値しか持たないため、switch文で使用する意味がない
また、分岐するために使う「case値」には次の条件を満たす必要があります。
- 条件式が戻す値と同じ型か互換性がある型であること
- 定数であるか、コンパイル時に値を決めることができること
- 定数は「final宣言された変数」か「リテラル」であること
- nullでないこと
つまり、変数はswitch文には使用できません。
if文とswitch文について、それぞれ例題を確認しましょう。
次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)
1. public class Main{ 2. public static void main(String[] args) { 3. int num = 10; 4. if(num < 10) 5. System.out.println("A"); 6. else 7. System.out.println("B"); 8. if(num == 10) 9. System.out.println("C"); 10. } 11. }
A. 「A」「B」「C」と表示される。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. 「A」「C」と表示される。
C. 「B」「C」と発生する。
D. 「A」と表示される。
E. 「B」と表示される。
F. 「C」と表示される。
まず、最初のif文で「num < 10」を判断するのにゃ。「numは10より小さい」という条件を満たさないから、「A」は表示されないのにゃ。
次のelseで「それ以外の場合」が実行されるから「B」は表示されるのにゃ!
最後にまたif文が出てきて、今度は「numが10と等しいか」を判断するのにゃ。numは10が代入されていて等しいから「C」も表示されるのにゃ。
だから正解の選択肢はCなのにゃ!この問題はヨユーなのにゃ!
ねこ奈お見事です!この調子でswitch文の例題も見てみましょう!
次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)
1. public class Main{ 2. public static void main(String[] args) { 3. int num = 1; 4. switch(num){ 5. case 1: 6. case 2: 7. case 3: System.out.println("A"); 8. case 4: System.out.println("B"); 9. default: 10. System.out.println("C"); 11. } 12. } 13. }
A. 「A」と表示される。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. 「A」「B」と表示される。
C. 「A」「B」「C」と発生する。
D. 何も表示されない。
E. コンパイルエラーが発生する。
F. 実行時に例外がスローされる。
うにゃあ?case1とcase2に何も処理が記載されていないのにゃあ。
これってコンパイルエラーになるのかにゃ?
もしコンパイルエラーじゃなかったら「何も表示されない」の選択肢Dが正解かにゃ?
正解は選択肢Cです!
caseの後に何も処理が記載されていない場合「何も処理しない」という処理をするのでコンパイルエラーではありません。
また、今回の例題のコードにはbreakがないため、以降に現れるすべてのcase式の処理が実行されます。このとき、default式も処理の対象となるため「A」「B」「C」すべての文字が表示されます。
まとめ 細かい文法やルールを知らないと解けない
今回は演算子と条件式について学習しました。
演算子と条件式って一番基礎的な内容なのですが、JavaSilverの問題はコードの意味を大雑把に理解している程度の知識では基礎部分の問題ですら到底太刀打ちできません。
各項目の細かい文法やルールを暗記するのではなく、「なぜこのような結果になるのか」と自分なりに考えを持って学習する必要があると感じています。
以上で今回の学習記録を終えます。
ここまでご覧いただきありがとうございました。
コメント