Java の文字列クラス(String)概要
Java において、テキストデータを扱う中心的なクラスは java.lang.String です。このクラスは言語レベルでサポートされており、以下の重要な特性を持っています。
- パッケージ:
java.lang(自動インポートされます) - 実装インターフェース:
Serializable、CharSequence、Comparable - 内部構造:本質的に変更不可能(immutable)なデータ構造を持ちます。
インスタンス化と不変性(Immutability)
String オブジェクトを作成する方法はいくつかありますが、特に重要なのはその「不変性」です。一度生成された String の内容は後から変更することができず、値を変更しようとすると新しいオブジェクトが生成されます。
package com.example.stringdemo;
public class StringConceptDemo {
// カスタム参照型を持つヘルパークラス
static class DataHolder {
public String message;
public DataHolder(String msg) {
this.message = msg;
}
}
public static void main(String[] args) {
// 1. リテラルによる定義(文字列プールを使用)
String literalStr = "initial";
String refToLiteral = literalStr;
literalStr = "updated"; // 新しい String オブジェクトへの参照付け替え
// refToLiteral は元のオブジェクトを指したままなので変化しない
System.out.println("リテラル再割り当て後: " + refToLiteral); // initial
// 2. コンストラクタによる明示的な生成(ヒープに新規作成)
String constructedStr = new String("data");
System.out.println(constructedStr.equals("data")); // true (内容比較)
// 3. 参照型の挙動との対比
DataHolder holder = new DataHolder("contentA");
DataHolder alias = holder;
holder.message = "contentB"; // 内部フィールドの変更
// alias も同じオブジェクトを指すため、影響を受ける
System.out.println("参照型の共有結果: " + alias.message); // contentB
}
}
上記のように、標準的な参照型(DataHolder など)ではメモリアドレスは同一のまま内容が変わりますが、String では再代入時に内部配列(過去は char[]、新版では byte[])自体が final で固定されているため、実質的に値そのものが変わるとみなされ、新しいメモリ領域が確保されます。
代表的なメソッドの使用例
String クラスには多数のユーティリティメソッドが用意されています。主に比較、取得、変換、分割に分けて確認します。
比較およびハッシュ計算
// == 演算子 vs equals メソッド
String strX = new String("test");
String strY = new String("test");
System.out.println(strX == strY); // false (アドレス比較)
System.out.println(strX.equals(strY)); // true (内容比較)
// ハッシュコードの仕組み
// 公式: h = 31 * previous_h + character_code
System.out.println("abc".hashCode());
// 'a'(97), 'b'(98), 'c'(99) の順で計算されます
compareTo メソッドを使うと辞書順での比較が可能です。辞書順が正しい場合 0 を返し、異なる場合は差の符号付き整数を返します。
// 辞書順比較
int result = "apple".compareTo("apply");
// 'e' (101) - 'y' (121) = -20
System.out.println(result);
// 長さが異なる場合
System.out.println("longer".compareTo("short"));
// 先頭が一致せず、長さで決まる場合は末尾までの桁数差が返されます
文字列操作と抽出
文字を取得したり、部分文字列を切り出したりする処理は頻繁に行われます。
String text = "JavaProgramming";
// 単一文字の取得(インデックスは 0 から始まります)
char firstChar = text.charAt(0); // 'J'
// サブストリングの抽出(左端インクルード、右端エキスクルート)
String sub = text.substring(0, 4); // "Java"
// 連結(頻繁に行う場合は StringBuilder を推奨)
String joined = "Hello".concat(", World");
// キーワード検索・置換
boolean hasJava = text.contains("Java");
String replaced = text.replace("Programming", "Development");
// 大文字小文字の統一(等価チェック用)
if ("TEST".equalsIgnoreCase("test")) {
// true
}
text.toLowerCase(); // すべて小文字に変換した新オブジェクト
正規表現による分割
split メソッドを使用して文字列を配列に変換できます。デリミタとして正規表現を利用するため、特殊文字には注意が必要です。
String raw = "user:id:12345";
String[] parts = raw.split(":");
// ["user", "id", "12345"]
// 特殊文字バックスラッシュ (\) の場合
String path = "C:\\temp\\file.txt";
// 正規表現エスケープのため \\\\\\\\ が必要になります
String[] segments = path.split("\\\\\\\\");
// ピリオド (.) や_pipe|_も同様にエスケープが必要です
String data = "val1|val2|val3";
String[] tokens = data.split("\\|");
可変な文字列クラスとの使い分け
ループ内での繰り返し結合など、頻繁な変更が発生するシナリオでは String の利用は非効率です(大量の一時オブジェクトが発生するため)。その代わりに、以下の変更可変クラスを使用します。
- StringBuilder: スレッドセーフではないため、シングルスレッド環境で最も高速です。
- StringBuffer: メソッドが synchronized されており、マルチスレッド環境での使用に適しています。
StringBuilder builder = new StringBuilder("Start");
builder.append("-Middle-");
builder.append("-End");
// メモリアドレスを維持したまま内容拡張が可能