JavaのString型を徹底解説。文字列操作のよくある疑問をすべて解決します
プログラミング学習で必ず出会うJavaのString型について、基本的な使い方から少し踏み込んだ内容まで、丁寧に解き明かしていきます。
この記事では、文字列の比較や便利な機能、さらにはパフォーマンス面で気をつけるべき点も網羅的に紹介します。読み終える頃には、String型への苦手意識がなくなり、自信を持って文字列を扱えるようになっているでしょう。
この記事で学べること
- JavaのString型の基本的な概念
- 初心者が間違いやすい文字列比較の正しい方法
- 実務で役立つ便利な文字列操作の機能
- パフォーマンスを考慮した効率的なコーディング
- よくあるエラーの原因と対策
そもそもJavaのString型ってなんだろう?
このセクションでは、JavaにおけるString型の基本的な立ち位置を解説します。プログラムの中で文字列がどのように扱われるのか、その第一歩を学びましょう。
- 文字列を扱うための設計図
- プリミティブ型との決定的な違い
String型は文字列を扱うための設計図
String型は、Javaで「文字列」を扱うために用意された、いわば設計図のようなものです。「こんにちは」や「Java Programming」といった、一連の文字の並びをデータとして保持できます。
プログラムの中でメッセージを表示したり、ユーザーが入力した名前を記録したりと、用途は非常に幅広いです。
// String型の変数 `greeting` を宣言し、「こんにちは」という文字列を代入 String greeting = "こんにちは"; // コンソールに変数 `greeting` の中身を出力 System.out.println(greeting);
実行結果
こんにちは
プリミティブ型との決定的な違い
Javaのデータ型には、大きく分けて「プリミティブ型」と「参照型」の2種類があります。
int
(整数)やdouble
(小数)といったプリミティブ型は、変数の中に直接データ値が格納されます。一方で、String型は「参照型」というグループに属する点が、int型などとの大きな違いです。
参照型は、データそのものではなく、データがメモリ上のどこに保存されているかの「住所(アドレス)」を変数の中に保持します。たくさんの文字を扱う文字列のデータを効率良く管理するための仕組み、と考えると分かりやすいでしょう。
まずは覚えたい!Java String型の基本的な使い方
ここでは、String型を扱う上で最も基本となる操作を紹介します。変数の宣言から文字列の連結、長さの取得まで、最初のステップを確実にマスターしましょう。
- 文字列を箱(変数)に入れる方法
- 文字列同士をくっつける方法
- 文字の数を数える方法
文字列を箱(変数)に入れる方法
String型の変数を使いたいときは、まず「こういう名前の箱を用意します」という宣言が必要です。宣言した後、=
を使って文字列を代入します。宣言と代入は同時に行うのが一般的です。
// String型の変数 `language` を宣言して初期化 String language = "Java"; // String型の変数 `emptyString` を宣言して空の文字列で初期化 String emptyString = "";
文字列同士をくっつける方法
文字列同士を連結するには、+
演算子を使います。数値の計算と同じ記号ですが、String型に使うと文字列連結として機能します。
String str1 = "Java"; String str2 = "の学習"; // + 演算子で文字列を連結 String connectedString = str1 + str2; System.out.println(connectedString);
実行結果
Javaの学習
数値と文字列を連結すると、数値も文字列に変換されてから連結されます。
String language = "Java"; int version = 17; // 文字列と数値を連結 String message = language + " " + version; System.out.println(message);
実行結果
Java 17
文字の数を数える方法
文字列の長さを知りたい場合は、length()
メソッドを使います。このメソッドは、文字数をint
型の数値で返却します。
String message = "Hello, World!"; // messageの文字数を取得 int length = message.length(); System.out.println("この文字列の長さは " + length + " です。");
実行結果
この文字列の長さは 13 です。
Javaプログラマー最初の壁!文字列の比較をマスターしよう
多くのJava学習者が一度は混乱する、文字列の比較について解説します。==
とequals()
の使い分けを正しく理解することが、バグの少ないコードを書くための第一歩です。
- 絶対に間違えてはいけない `==` と `equals()` の違い
- 文字列比較は `equals()` が鉄則の理由
- 大文字と小文字を無視して比較するには?
絶対に間違えてはいけない `==` と `equals()` の違い
String型の比較において、==
演算子とequals()
メソッドは全く異なる働きをします。
==
演算子
2つの変数が、メモリ上の「全く同じモノ」を指しているか比較します。equals()
メソッド
2つの変数が持つ文字列の「内容」が同じかどうかを比較します。
文字列の内容が同じかどうかを比べたいときは、必ず`equals()`メソッドを使いましょう。
// 図解:== と equals の違い // s1とs2は同じ内容だが、メモリ上では別の場所に確保されている String s1 = new String("Java"); String s2 = new String("Java"); // s3はs1と同じ場所を指している String s3 = s1; // メモリのイメージ [ s1 ] ---> ["Java"というデータの実体] [ s2 ] ---> ["Java"というデータの実体(別の場所)] [ s3 ] ---> [ s1と同じ場所を指す ]
比較方法 | 比較対象 | 説明 |
---|---|---|
== 演算子 |
参照(メモリ上のアドレス) | 変数が指しているデータの置き場所が同じか調べる。 |
equals() メソッド |
値(文字列の内容) | 文字列の中身そのものが同じか調べる。 |
文字列比較は `equals()` が鉄則の理由
次のコードを見ると、なぜequals()
を使うべきかが明確に分かります。
String s1 = "こんにちは"; String s2 = "こんにちは"; String s3 = new String("こんにちは"); // s1とs2は同じ内容か? -> true System.out.println("s1.equals(s2): " + s1.equals(s2)); // s1とs3は同じ内容か? -> true System.out.println("s1.equals(s3): " + s1.equals(s3)); System.out.println("---"); // s1とs2は同じモノを指しているか? -> true (Javaの最適化による) System.out.println("s1 == s2: " + (s1 == s2)); // s1とs3は同じモノを指しているか? -> false (newで新しいオブジェクトを作ったため) System.out.println("s1 == s3: " + (s1 == s3));
実行結果
s1.equals(s2): true s1.equals(s3): true --- s1 == s2: true s1 == s3: false
new String()
でオブジェクトを生成すると、たとえ内容は同じでもメモリ上に新しい領域が確保されます。そのため、s1 == s3
はfalse
(偽)になります。見た目が同じ文字列でも、`==`での比較は意図しない結果を招く危険があるのです。
大文字と小文字を無視して比較するには?
ユーザーIDのチェックなどで、大文字と小文字を区別せずに比較したい場面があります。その場合はequalsIgnoreCase()
メソッドが便利です。
String str1 = "Java"; String str2 = "java"; // equals() は大文字・小文字を区別する System.out.println("equals()の結果: " + str1.equals(str2)); // equalsIgnoreCase() は大文字・小文字を区別しない System.out.println("equalsIgnoreCase()の結果: " + str1.equalsIgnoreCase(str2));
実行結果
equals()の結果: false equalsIgnoreCase()の結果: true
これだけは押さえたい!String型の便利メソッド7選
ここでは、実務での文字列操作を格段に効率化してくれる、String型の便利なメソッドを7つ厳選して紹介します。これらのメソッドを使いこなせると、コーディングの幅が大きく広がります。
startsWith() / endsWith():始まりと終わりをチェック
文字列が特定の文字で始まっているか、または終わっているかを確認できます。ファイルの拡張子チェックなどで役立ちます。
String fileName = "document.pdf"; // "document"で始まるか? -> true System.out.println(fileName.startsWith("document")); // ".txt"で終わるか? -> false System.out.println(fileName.endsWith(".txt"));
contains():特定の文字が含まれるかチェック
文字列の中に、指定したキーワードが含まれているかどうかを判定します。
String sentence = "明日の天気は晴れです"; // "天気"という単語が含まれるか? -> true System.out.println(sentence.contains("天気"));
substring():一部分を切り出す
文字列の一部分を抜き出したいときに使います。開始位置と、オプションで終了位置を指定可能です。
String dateTime = "2025/08/13"; // 5文字目から最後までを切り出す (インデックスは0から始まる) String date = dateTime.substring(5); System.out.println(date); // 結果: 08/13 // 0文字目から4文字目の直前までを切り出す String year = dateTime.substring(0, 4); System.out.println(year); // 結果: 2025
split():特定の文字で分割する
指定した区切り文字で文字列を分割し、String型の配列として結果を受け取れます。CSVデータの処理などに応用できます。
String csvData = "山田,東京,30"; // ","を区切り文字として分割 String[] data = csvData.split(","); // 配列の各要素を出力 System.out.println("名前: " + data[0]); System.out.println("住所: " + data[1]); System.out.println("年齢: " + data[2]);
実行結果
名前: 山田 住所: 東京 年齢: 30
replace() / replaceAll():文字を置き換える
文字列内の特定の文字や文字列を、別のものに置き換えます。
String sentence = "Javaは楽しい。Javaは最高。"; // "Java"を"Python"に置き換える String replaced = sentence.replace("Java", "Python"); System.out.println(replaced);
実行結果
Pythonは楽しい。Pythonは最高。
trim():前後の空白を削除する
ユーザー入力フォームなどでは、意図しない空白が前後に付加されることがあります。trim()
は、文字列の先頭と末尾にある半角スペースや改行などの空白文字を取り除いてくれます。
String userInput = " 山田太郎 "; String trimmed = userInput.trim(); System.out.println("元の文字列: [" + userInput + "]"); System.out.println("trim後: [" + trimmed + "]");
実行結果
元の文字列: [ 山田太郎 ] trim後: [山田太郎]
toUpperCase() / toLowerCase():大文字・小文字に変換
文字列全体を、すべて大文字またはすべて小文字に変換します。検索キーワードの正規化などに便利です。
String mixedCase = "AbCdEfG"; // すべて大文字に変換 System.out.println(mixedCase.toUpperCase()); // 結果: ABCDEFG // すべて小文字に変換 System.out.println(mixedCase.toLowerCase()); // 結果: abcdefg
少し高度なJavaのString型の知識
基本を押さえたら、次は少し応用的な知識を身につけましょう。nullの扱いや文字列のフォーマットなど、より実践的なテクニックを紹介します。
- `null` と空文字 `""` はどう違う?
- 見やすい文字列を作るフォーマット術
- 長い文章も楽々!テキストブロックの使い方
`null` と空文字 `""` はどう違う?
null
と空文字""
は、似ているようで全く異なります。この違いの理解は、予期せぬエラーを防ぐ上で非常に重要です。
null
変数の箱自体は存在するものの、中身が「空っぽ」で、何も参照していない状態です。- 空文字
""
長さが0の文字列オブジェクトを、変数がきちんと参照している状態です。
`null`の変数に対してメソッドを呼び出そうとすると、`NullPointerException`という有名なエラーが発生します。
String nullStr = null; String emptyStr = ""; // System.out.println(nullStr.length()); // ここでNullPointerExceptionが発生する System.out.println(emptyStr.length()); // こちらは正しく 0 が出力される
見やすい文字列を作るフォーマット術
String.format()
メソッドを使うと、テンプレートとなる文字列に後から値を埋め込めます。+
で連結するよりもコードがすっきりと見やすくなります。
String name = "鈴木"; int age = 28; double height = 175.5; // %sは文字列、%dは整数、%.1fは小数点第一位までの浮動小数点数に置き換えられる String formattedString = String.format("%sさんの年齢は%d歳、身長は%.1fcmです。", name, age, height); System.out.println(formattedString);
実行結果
鈴木さんの年齢は28歳、身長は175.5cmです。
長い文章も楽々!テキストブロックの使い方
Java 15から導入されたテキストブロックを使うと、複数行にわたる文字列を直感的に記述できます。三重のダブルクォート"""
で文字列を囲みます。
改行やインデントが見たまま反映されるので、SQL文やJSONなどをコード内に記述する際に非常に便利です。
// 従来の複数行文字列 String html_old = "<html>\n" + " <body>\n" + " <p>Hello, World!</p>\n" + " </body>\n" + "</html>"; // テキストブロックを使った場合 String html_new = """ <html> <body> <p>Hello, World!</p> </body> </html> """; System.out.println(html_new);
なぜ?String型が「不変(immutable)」である理由
JavaのString型が持つ最も重要な特性の一つが「不変(immutable)」であることです。このセクションでは、不変とは何か、そしてなぜそのような設計になっているのかを深掘りします。
- 一度作ったら変えられないStringのルール
- 「不変」がもたらす3つのメリット
一度作ったら変えられないStringのルール
String型が「不変」であるとは、一度生成されたStringオブジェクトの中身(文字列データ)は、後から変更できないということです。
例えばreplace()
メソッドを使っても、元の文字列オブジェクトが変更されるわけではありません。実際には、変更後の内容を持つ「新しい」Stringオブジェクトが生成され、返却されています。
// 図解:Stringの不変性 String s = "Java"; // sの参照先が変わるだけで、元の"Java"オブジェクトは変更されない s = s.replace('J', 'L'); // "Lava"という新しいオブジェクトが生成される // メモリのイメージ [ s ] ---> ["Lava"という新しいオブジェクト] ["Java"という元のオブジェクト] ← 誰からも参照されなくなり、いずれ消滅
「不変」がもたらす3つのメリット
一見不便に思える「不変」の特性ですが、次のような大きなメリットがあります。
- 安全性の向上
データが勝手に書き換えられないため、プログラムの意図しない動作を防ぎます。特に複数の処理がデータを共有する場面で安全性が高まります。 - パフォーマンスの最適化
同じ内容の文字列は、メモリ上で共有されることがあります(文字列定数プール)。不変でなければ、ある場所での変更が別の場所に影響を与えてしまうため、このような共有はできません。 - キャッシュへの利用
Stringオブジェクトは内容が変わらないため、計算結果のキャッシュとして安心して利用できます。キーとしてStringを使い、値を紐づけるような場面で効果を発揮します。
パフォーマンスを意識したJavaの文字列操作
大量の文字列を扱う際には、パフォーマンスへの配慮が重要になります。ここでは、特に文字列連結で注意すべき点と、その解決策を解説します。
- 大量連結はNG? `+` 演算子の落とし穴
- 文字列連結の救世主 `StringBuilder`
- `StringBuilder` と `StringBuffer` の使い分け
大量連結はNG? `+` 演算子の落とし穴
+
演算子による文字列連結は手軽ですが、ループ処理の中で何度も繰り返し行うとパフォーマンスが著しく低下する場合があります。
なぜなら、`+`演算子による連結のたびに、内部では新しいStringオブジェクトが生成されているからです。ループ回数が多くなると、オブジェクトの生成と破棄が頻繁に発生し、処理速度の低下やメモリの圧迫に繋がります。
// このコードはパフォーマンスが悪い例 String result = ""; for (int i = 0; i < 10000; i++) { result += "a"; // ループのたびに新しいStringオブジェクトが作られる }
文字列連結の救世主 `StringBuilder`
何度も文字列を連結するような場面では、StringBuilder
クラスを使うのが定石です。StringBuilder
は、内部に可変のバッファを持ち、文字列を追加しても新しいオブジェクトを毎回生成しません。
文字列の追加にはappend()
メソッドを使い、最後にtoString()
メソッドで最終的なStringオブジェクトを取得します。
// StringBuilderを使った効率的な連結 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb.append("a"); // 内部バッファに文字を追加するだけ } String result = sb.toString(); // 最後に一度だけStringオブジェクトを生成
`StringBuilder` と `StringBuffer` の使い分け
Javaには、StringBuilder
とよく似たStringBuffer
というクラスも存在します。両者の主な違いは「スレッドセーフ」であるかどうかです。
基本的には、処理速度の速い`StringBuilder`の使用を推奨します。
クラス名 | 同期処理 | スピード | 推奨される場面 |
---|---|---|---|
StringBuilder |
なし(非同期) | 速い | 単一のスレッドで動く通常のプログラム |
StringBuffer |
あり(同期) | 遅い | 複数のスレッドから同時にアクセスされる可能性がある特殊な環境 |
参照 > Java™ Platform, Standard Edition 17 API Specification - StringBuilder
String型のよくある質問とエラー対策
String型を扱う上で遭遇しやすい、代表的なエラーとその対策をQ&A形式で解説します。原因を理解し、未然に防ぐコーディングを心がけましょう。
- `NullPointerException` を回避するには?
- `StringIndexOutOfBoundsException` って何?
`NullPointerException` を回避するには?
A. 変数が`null`でないことを確認してから、メソッドを呼び出す習慣をつけましょう。
NullPointerException
は、null
状態の変数に対してequals()
やlength()
などのメソッドを呼び出そうとすると発生します。これを防ぐには、事前にif文でチェックするのが基本です。
String text = null; // 何らかの処理でnullが代入されたと仮定 // 事前にnullチェックを行う if (text != null) { System.out.println(text.length()); } else { System.out.println("文字列はnullです。"); }
また、定数や値が確定している文字列を`equals()`の呼び出し元にするのも、有効なテクニックです。万が一比較対象がnull
でも、エラーにならずに済みます。
String userInput = null; // この書き方なら userInput が null でもエラーにならない if ("admin".equals(userInput)) { System.out.println("管理者です。"); }
`StringIndexOutOfBoundsException` って何?
A. 文字列の長さを超えた範囲を指定して文字を取得しようとしたときに発生するエラーです。
例えば、substring()
やcharAt()
で、文字列の実際の長さよりも大きなインデックス(位置番号)を指定すると発生します。インデックスは0から始まることを忘れないようにしましょう。
String word = "Java"; // 長さは4 (インデックスは0, 1, 2, 3) // char charAt(int index) は指定した位置の文字を返す char c = word.charAt(3); // 'a' が取得できる // char error = word.charAt(4); // ここで StringIndexOutOfBoundsException が発生
このエラーを防ぐには、メソッドを使う前に文字列の長さをlength()
で確認し、指定するインデックスが有効な範囲(0から長さ-1まで)に収まっているかチェックすることが重要です。
まとめ
本記事では、JavaのString型について、基礎から応用、パフォーマンスに至るまで幅広く掘り下げました。
本記事の要点
- String型は文字列を扱うための「参照型」である
- 文字列の内容比較には
==
ではなくequals()
メソッドを用いる - ループ内での文字列連結には
StringBuilder
を使いパフォーマンスを意識する - String型は「不変(immutable)」であり、メソッドは新しいオブジェクトを返す
null
チェックを怠るとNullPointerException
の原因になる
String型を正しく理解し使いこなすことは、品質の高いJavaアプリケーションを開発するための基礎体力となります。ここで得た知識を土台に、さらなる学習へと進んでいきましょう。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。