Java Silver SE17では、StringとStringBuilderの実行結果を問うコード問題がよく登場します。
特に間違えやすいのが、==とequals()の違い、Stringプール、文字列の不変性、substring()の範囲、StringBuilderの変更系メソッドです。
この記事では、Java Silver対策として押さえておきたいString・StringBuilder問題を10問用意しました。最初にコードを見て実行結果を予想し、そのあとに「答えと解説を見る」を開いて確認してください。
先に答えを見ず、自分で「参照を比較しているのか」「文字列の内容を比較しているのか」「戻り値を代入しているのか」を確認するのがポイントです。
Java SilverのString・StringBuilderで間違えやすい5つのポイント
1.==とequals()を同じ比較だと思ってしまう
参照型に対する==は、原則として2つの変数が同じオブジェクトを参照しているかを比較します。一方、Stringクラスのequals()は文字列の内容を比較します。
2.文字列リテラルはStringプールで共有される
"Java"のような文字列リテラルはStringプールで管理されます。同じ内容のリテラルは、同じStringオブジェクトを参照することがあります。
ただし、new String("Java")は新しいオブジェクトを生成するため、リテラルと==で比較すると通常はfalseです。
3.Stringのメソッドが元の文字列を書き換えると思ってしまう
Stringは不変(immutable)です。concat()、replace()、trim()などを呼び出しても、元のStringオブジェクトは変わりません。変更結果を使うには、戻り値を変数へ代入する必要があります。
4.substring()の終了位置を含めてしまう
substring(beginIndex, endIndex)では、開始位置は含み、終了位置は含みません。「begin以上、end未満」と覚えましょう。
5.StringBuilderも不変だと考えてしまう
StringBuilderは可変(mutable)です。append()、insert()、delete()、reverse()などを呼び出すと、原則として同じオブジェクトの内容が変更されます。
Java Silver SE17のString・StringBuilder問題10選
問題1:文字列リテラルとnew String()
public class Main {
public static void main(String[] args) {
String a = "Java";
String b = new String("Java");
System.out.print(a == b);
System.out.print(":" + a.equals(b));
}
}
選択肢:
true:truetrue:falsefalse:truefalse:false
答えと解説を見る
正解は3.false:trueです。
aはStringプールにある文字列リテラルを参照します。一方、new String("Java")は新しいStringオブジェクトを生成します。
そのため、参照先が同じかを調べる==はfalseです。文字列の内容を比較するequals()は、どちらも「Java」なのでtrueになります。
問題2:コンパイル時に連結される文字列
public class Main {
public static void main(String[] args) {
String a = "Java";
String b = "Ja" + "va";
System.out.print(a == b);
System.out.print(":" + a.equals(b));
}
}
選択肢:
true:truetrue:falsefalse:true- コンパイルエラー
答えと解説を見る
正解は1.true:trueです。
"Ja" + "va"は、どちらもコンパイル時に値が決まる文字列リテラルです。そのため、コンパイラによって"Java"としてまとめられます。
aとbはStringプール内の同じ文字列を参照するため、==もequals()もtrueです。
ただし、連結部分に実行時に値が決まる変数が含まれると、同じ結果になるとは限りません。「リテラル同士の定数式か」を確認しましょう。
問題3:Stringプールとintern()
public class Main {
public static void main(String[] args) {
String a = new String("Silver");
String b = a.intern();
String c = "Silver";
System.out.print(a == c);
System.out.print(":" + (b == c));
}
}
選択肢:
true:truetrue:falsefalse:truefalse:false
答えと解説を見る
正解は3.false:trueです。
aはnewによって生成された別のStringオブジェクトを参照するため、リテラルを参照するcとは==で一致しません。
intern()は、Stringプールにある同じ内容の文字列への参照を返します。そのため、bとcを==で比較した結果はtrueです。
問題4:Stringの不変性
public class Main {
public static void main(String[] args) {
String text = "Java";
text.toUpperCase();
text.replace("J", "L");
System.out.print(text);
}
}
選択肢:
JAVALavaLAVAJava
答えと解説を見る
正解は4.Javaです。
Stringは不変なので、toUpperCase()もreplace()も元の文字列を書き換えません。各メソッドは処理結果を表す新しいStringを返しますが、このコードでは戻り値を受け取っていません。
変更結果を使うなら、text = text.toUpperCase();のように再代入する必要があります。
問題5:concat()の戻り値
public class Main {
public static void main(String[] args) {
String a = "Java";
String b = a.concat("Silver");
System.out.print(a);
System.out.print(":" + b);
}
}
選択肢:
Java:JavaSilverJavaSilver:JavaSilverJava:Silver- コンパイルエラー
答えと解説を見る
正解は1.Java:JavaSilverです。
concat("Silver")は「Java」と「Silver」を連結した新しいStringを返します。その戻り値を受け取ったbは「JavaSilver」です。
元のStringであるaは変更されないため、引き続き「Java」です。「メソッドを呼んだか」だけでなく、「戻り値をどこへ代入したか」を追ってください。
問題6:substring()の開始位置と終了位置
public class Main {
public static void main(String[] args) {
String text = "Silver";
System.out.print(text.substring(1, 4));
}
}
選択肢:
Sililvilvelve
答えと解説を見る
正解は2.ilvです。
文字列「Silver」のインデックスは、S=0、i=1、l=2、v=3、e=4、r=5です。
substring(1, 4)はインデックス1を含み、インデックス4を含みません。そのため、1〜3番目のilvが返されます。
2つの引数を取るsubstring()は「開始位置以上、終了位置未満」です。
問題7:trim()とreplace()の連続呼び出し
public class Main {
public static void main(String[] args) {
String a = " Java ";
String b = a.trim().replace('a', 'X');
System.out.print(a.length());
System.out.print(":" + b);
}
}
選択肢:
4:JXvX6:JXvX6:Java4:Java
答えと解説を見る
正解は2.6:JXvXです。
aには前後の半角スペースも含まれるため、長さは6です。Stringは不変なので、trim()を呼んでもa自体は変わりません。
trim()が返した「Java」に対してreplace('a', 'X')が実行され、その結果「JXvX」がbへ代入されます。
メソッドチェーンでは、左側のメソッドが返したオブジェクトに対して、次のメソッドが呼び出されます。
問題8:StringBuilderのappend()とinsert()
public class Main {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("Java");
sb.append("17").insert(4, "SE");
System.out.print(sb);
}
}
選択肢:
Java17SEJavaSE17SEJava17- コンパイルエラー
答えと解説を見る
正解は2.JavaSE17です。
まずappend("17")によって内容は「Java17」になります。
次のinsert(4, "SE")は、インデックス4の位置へ「SE」を挿入します。「Java」の直後で「17」の直前に挿入されるため、結果は「JavaSE17」です。
StringBuilderの変更系メソッドは同じStringBuilderを返すため、このように続けて呼び出せます。
問題9:StringBuilderのdelete()とreverse()
public class Main {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("abcdef");
sb.delete(1, 4).reverse();
System.out.print(sb);
}
}
選択肢:
feafebaaeffedcba
答えと解説を見る
正解は1.feaです。
delete(1, 4)では、インデックス1以上4未満の文字、つまりbcdが削除されます。この時点で内容は「aef」です。
続いてreverse()が実行されるため、「aef」が逆順になり、最終結果は「fea」です。
delete()もsubstring()と同様に、開始位置を含み、終了位置を含みません。
問題10:StringとStringBuilderの代入
public class Main {
public static void main(String[] args) {
String text = new StringBuilder("Java");
System.out.print(text);
}
}
選択肢:
JavaStringBuilder- 実行時に例外が発生する
- コンパイルエラー
答えと解説を見る
正解は4.コンパイルエラーです。
StringとStringBuilderは別のクラスであり、継承関係もありません。String型の変数へStringBuilderオブジェクトをそのまま代入することはできません。
Stringへ変換するには、toString()を呼び出します。
String text = new StringBuilder("Java").toString();
この形ならStringBuilderが表す文字列をStringとして受け取れるため、正常にコンパイルできます。
==とequals()の違いを問題で見抜く方法
Stringの比較問題では、まず比較演算子を確認します。
| 比較方法 | 確認するもの | 主な判断材料 |
|---|---|---|
== | 同じオブジェクトを参照しているか | リテラル、new、intern()、定数式 |
equals() | 文字列の内容が同じか | 大文字・小文字、空白、文字数、並び |
次に、文字列がどのように作られたかを確認します。
- 同じ文字列リテラル同士:Stringプールで共有される可能性がある
new String():新しいオブジェクトを生成する- リテラルだけの定数式:コンパイル時に連結される
intern()の戻り値:Stringプール内の文字列を参照する
実務では文字列の内容を比較するとき、基本的にequals()を使います。試験ではあえて==を使い、Stringプールや参照の理解を確認する問題が出されます。
Stringの不変性を問題で見抜く方法
Stringのメソッドを見つけたら、戻り値が利用されているか確認してください。
String text = "java";
text.toUpperCase();
System.out.println(text);
このコードの結果はjavaです。toUpperCase()が返す新しいStringを受け取っていないからです。
String text = "java";
text = text.toUpperCase();
System.out.println(text);
こちらは戻り値を再代入しているため、結果はJAVAです。
次のメソッドが登場したら、特に代入先を確認しましょう。
concat()replace()substring()trim()toUpperCase()、toLowerCase()
StringBuilderの実行結果を追う方法
StringBuilderでは、処理のたびに現在の内容を書き出すと間違いにくくなります。
| メソッド | 動作 | 範囲の注意点 |
|---|---|---|
append(value) | 末尾に追加 | 現在の長さの後ろへ追加 |
insert(index, value) | 指定位置に挿入 | 指定インデックスの文字の前へ挿入 |
delete(start, end) | 範囲を削除 | start以上、end未満 |
deleteCharAt(index) | 1文字削除 | 指定位置の文字を削除 |
reverse() | 文字順を反転 | 変更後の全体を逆順にする |
setCharAt(index, ch) | 1文字置換 | 戻り値はvoid |
たとえば、append()とinsert()が連続している場合、最後の状態だけを頭の中で考えると混乱します。
StringBuilder sb = new StringBuilder("Java"); // Java
sb.append("17"); // Java17
sb.insert(4, "SE"); // JavaSE17
このように1行ずつ内容を更新すると、複雑な問題でも正確に追えます。
StringとStringBuilderの違い
| 比較項目 | String | StringBuilder |
|---|---|---|
| 変更可能か | 不変 | 可変 |
| 変更系メソッド | 新しいStringを返す | 自身の内容を変更するものが多い |
| 内容の比較 | equals()で内容を比較 | equals()は内容比較用にオーバーライドされていない |
| Stringへの変換 | 変換不要 | toString()を使う |
| 向いている処理 | 変更が少ない文字列 | 文字列を何度も編集・連結する処理 |
StringBuilderのequals()についても注意が必要です。Stringのような内容比較にはならないため、2つのStringBuilderの文字列内容を比較したい場合は、Stringへ変換して比較します。
sb1.toString().equals(sb2.toString())
Java SilverのString問題を解く5ステップ
- 変数の型がStringかStringBuilderか確認する
==かequals()か確認する- リテラル、
new、intern()の有無を確認する - Stringメソッドの戻り値が代入されているか確認する
- StringBuilderは1メソッドごとに変更後の内容を書き出す
選択肢に「コンパイルエラー」がある場合は、StringとStringBuilderを直接代入していないか、メソッドの引数や戻り値の型が正しいかも確認してください。
10問の正答一覧
| 問題 | 正解 | 学習ポイント |
|---|---|---|
| 1 | false:true | ==は参照、equals()は内容を比較 |
| 2 | true:true | リテラルだけの定数式はコンパイル時に連結 |
| 3 | false:true | intern()はプール内の参照を返す |
| 4 | Java | Stringは不変 |
| 5 | Java:JavaSilver | concat()は新しいStringを返す |
| 6 | ilv | 終了インデックスは含まない |
| 7 | 6:JXvX | 元のStringとメソッドチェーンの戻り値を分ける |
| 8 | JavaSE17 | append()後にインデックス4へ挿入 |
| 9 | fea | delete()後にreverse() |
| 10 | コンパイルエラー | StringBuilderはStringへ直接代入できない |
まとめ:Stringは戻り値、StringBuilderは変更後の状態を追おう
Java Silver SE17のString・StringBuilder問題では、次のポイントが重要です。
==は参照、Stringのequals()は内容を比較する- 同じ文字列リテラルはStringプールで共有される
new String()は新しいオブジェクトを生成する- Stringは不変なので、メソッドの戻り値を追う
substring()やdelete()の終了位置は含まない- StringBuilderは可変なので、処理ごとの内容を追う
- StringBuilderをStringにするには
toString()を使う
ルールを読んで理解しただけでは、少し形を変えられた問題で迷ってしまいます。コードを見て実行結果を予想する練習を繰り返し、判断手順を身につけましょう。
当サイトでは、Java Silver SE17対応のコード読解問題と模擬試験を無料で公開しています。登録不要で利用できるので、String以外の分野も続けて確認してみてください。
例外処理も復習したい方は、Java Silver SE17の例外処理問題10選もあわせてご覧ください。

コメント