Java
 Computer >> コンピューター >  >> プログラミング >> Java

なぜJavaのStringクラスは不変(イミュータブル)かつfinalとして設計されているのか?

Stringが不変(イミュータブル)であるとは?

JavaにおいてStringクラスが「不変(immutable)」であるということは、オブジェクトそのものの状態を後から変更できないことを意味します。ただし、オブジェクトを参照する変数(参照先)は別のオブジェクトに差し替えることが可能です。また、Stringクラスがfinalとして宣言されているのは、他のクラスによる継承を禁止し、この不変性を破壊されるのを防ぐためです。もし継承が許されていれば、サブクラスで挙動を上書きされ、Stringの安全性が損なわれてしまいます。

では、なぜJavaの設計者はStringを不変にしたのでしょうか?主な理由は以下の4点にまとめられます。

1. セキュリティ

ネットワーク接続、データベース接続URL、ユーザー名やパスワードなどの機密性の高いパラメータは、一般的にString型で表現されます。もしStringが可変(ミュータブル)だった場合、これらの重要な値がプログラムの実行中に容易に書き換えられてしまう危険性があります。不変であることで、不正な改ざんから情報を守ることができます。

2. 同期と並行処理(スレッドセーフ)

Stringが不変であることで、自動的にスレッドセーフになります。複数のスレッドから同時にアクセスしても内部状態が変わらないため、同期(synchronization)に関する問題を意識せずに安全に共有できます。

3. キャッシュとパフォーマンス

コンパイラはStringオブジェクトを最適化する際、「文字列プール(String Pool)」という仕組みを利用します。例えば a = "test"b = "test" のように同じ値を持つ文字列が2つ存在する場合、実際には1つのStringオブジェクトだけを作成し、aとbの両方が同じオブジェクトを参照します。これによりメモリ使用量が削減され、パフォーマンスが向上します。

4. クラスローディングの安全性

クラスのロード時には、クラス名がString型の引数として渡されます。もしStringが可変だった場合、悪意あるコードによってクラス名が途中で変更され、意図しない(誤った)クラスがロードされる恐れがあります。不変であることで、クラスローディングの整合性が保たれます。

動作確認のためのサンプルコード

以下のコードは、String変数への再代入によって「新しいオブジェクト」が生成されていることを、hashCode()の変化から確認できる例です。

public class StringImmutableDemo {
    public static void main(String[] args) {
        String st1 = "Tutorials";
        String st2 = "Point";
        System.out.println("st1のhashCode = " + st1.hashCode());
        System.out.println("st2のhashCode = " + st2.hashCode());
        st1 = st1 + st2;
        System.out.println("st1変更後のhashCode : " + st1.hashCode());
    }
}

実行結果

st1のhashCode = -594386763
st2のhashCode = 77292912
st1変更後のhashCode : 962735579

ご覧のとおり、st1 = st1 + st2; を実行するとhashCodeが変化しています。これは元のオブジェクトが書き換えられたのではなく、連結結果として新しいStringオブジェクトが新たに作成され、参照がそちらへ切り替わったことを示しています。まさに「オブジェクト自体は変更できないが、参照は変更できる」というStringの不変性を表す好例です。

  1. Javaのfinalキーワードとは?変数・メソッド・クラスでの使い方を解説

    finalは、Javaにおける非アクセス修飾子のひとつです。クラス、メソッド、変数の実装を「確定」させるために使用され、これらが後から変更されることを防ぐ役割を持ちます。本記事では、finalキーワードの基本的な概念と、変数・メソッド・クラスそれぞれでの使い方をコード例とともに解説します。final変数final変数は、明示的に初期化できるのは一度だけです。また、finalで宣言された参照変数は、別のオブジェクトを参照するように再代入することはできません。ただし、オブジェクト内部のデータ自体は変更可能です。つまり、オブジェクトの「状態」は変えられるものの、「参照先」は変えられないという点に注意

  2. Javaで多重継承がサポートされていない理由とは?曖昧性の問題をわかりやすく解説

    Javaでは、1つのクラスが複数のクラスを同時に継承(extends)することはできません。そのため、次のようなコードはコンパイルエラーとなり、不正な記述として扱われます。不正なコードの例public class extends Animal, Mammal{}一方で、Javaのクラスは1つ以上のインターフェースを実装(implements)することが可能です。この仕組みによって、多重継承が持つ利点の多くを補完できるようになっています。多重継承が禁止されている理由最大の理由は、曖昧性(あいまいさ)の発生を防ぐためです。具体的に考えてみましょう。仮にクラスBがクラスAとクラスCの両方を継承できる