Javaで文字列が不変なのはなぜですか?

Javaで文字列が不変である理由

1. 前書き

Javaでは、文字列は不変です。 インタビューでよく見られる明らかな質問は、「Javaで文字列が不変として設計されている理由」です。

Javaの作成者であるJamesGoslingは、was once asked in an interviewはいつ不変を使用する必要があるかについて、次のように答えています。

可能な限り、不変を使用します。

さらに、キャッシング、セキュリティ、レプリケーションなしでの簡単な再利用など、不変性が提供する機能を示す引数をサポートしています。

このチュートリアルでは、Java言語の設計者がStringを不変に保つことにした理由をさらに詳しく説明します。

2. 不変オブジェクトとは何ですか?

不変オブジェクトはobject whose internal state remains constant after it has been entirely createdです。 つまり、オブジェクトが変数に割り当てられると、参照を更新したり、内部状態を変更したりすることはできません。

不変オブジェクトについて詳しく説明する別の記事があります。 詳細については、Immutable Objects in Javaの記事をご覧ください。

3. なぜStringはJavaで不変なのですか?

このクラスを不変に保つことの主な利点は、キャッシュ、セキュリティ、同期、およびパフォーマンスです。

これらがどのように機能するかについて説明しましょう。

3.1. Stringプールを紹介する

Stringは、最も広く使用されているデータ構造です。 Stringリテラルをキャッシュして再利用すると、異なるString変数がStringプール内の同じオブジェクトを参照するため、ヒープスペースを大幅に節約できます。 Stringインターンプールはまさにこの目的を果たします。

Java文字列プールはthe special memory region where Strings are stored by the JVMです。 StringsはJavaで不変であるため、JVMは、各リテラルStringのコピーを1つだけプールに格納することにより、それらに割り当てられるメモリの量を最適化します。 このプロセスはインターンと呼ばれます:

String s1 = "Hello World";
String s2 = "Hello World";

assertThat(s1 == s2).isTrue();

前の例にはStringプールが存在するため、2つの異なる変数がプールから同じStringオブジェクトを指しているため、重要なメモリリソースを節約できます。

image

JavaStringプール専用の別の記事があります。 詳細については、head on over to that articleを参照してください。

3.2. セキュリティ

Stringは、ユーザー名、パスワード、接続URL、ネットワーク接続などの機密情報を格納するためにJavaアプリケーションで広く使用されています。 また、クラスのロード中にJVMクラスローダーによって広く使用されます。

したがって、Stringクラスを保護することは、一般にアプリケーション全体のセキュリティに関して重要です。 たとえば、次の簡単なコードスニペットを考えてみましょう。

void criticalMethod(String userName) {
    // perform security checks
    if (!isAlphaNumeric(userName)) {
        throw new SecurityException();
    }

    // do some secondary tasks
    initializeDatabase();

    // critical task
    connection.executeUpdate("UPDATE Customers SET Status = 'Active' " +
      " WHERE UserName = '" + userName + "'");
}

上記のコードスニペットで、信頼できないソースからStringオブジェクトを受け取ったとしましょう。 最初に必要なすべてのセキュリティチェックを実行して、Stringが英数字のみであるかどうかを確認し、その後、さらにいくつかの操作を行います。

信頼性の低いソース呼び出し元メソッドには、まだこのuserNameオブジェクトへの参照があることに注意してください。

If Strings were mutable, then by the time we execute the update, we can’t be sure that the String we received, even after performing security checks, would be safe.信頼できない呼び出し元メソッドにはまだ参照があり、整合性チェックの間にStringを変更できます。 したがって、この場合、クエリをSQLインジェクションになりやすくします。 したがって、変更可能なStringsは、時間の経過とともにセキュリティの低下につながる可能性があります。

また、StringuserNameが別のスレッドに表示され、整合性チェック後にその値が変更される可能性もあります。

一般に、この場合、結果に影響を与える可能性のある操作のインターリーブが少ないため、値が変更されないときに機密性の高いコードを操作する方が簡単であるため、不変性が役立ちます。

3.3. 同期

不変であると、Stringスレッドは複数のスレッドからアクセスされたときに変更されないため、自動的に安全になります。

したがって、immutable objects, in general, can be shared across multiple threads running simultaneously. They’re also thread-safeは、スレッドが値を変更した場合、同じ値を変更する代わりに、新しいStringStringプールに作成されるためです。 したがって、Stringsはマルチスレッドに対して安全です。

3.4. ハッシュコードキャッシング

Stringオブジェクトはデータ構造として豊富に使用されているため、HashMapHashTableHashSetなどのハッシュ実装でも広く使用されています。 これらのハッシュ実装を操作する場合、バケット化のためにhashCode()メソッドが頻繁に呼び出されます。

不変性は、Stringsの値が変更されないことを保証します。 したがって、the hashCode() method is overridden in String class to facilitate caching, such that the hash is calculated and cached during the first hashCode() call and the same value is returned ever since.

これにより、Stringオブジェクトで操作するときにハッシュ実装を使用するコレクションのパフォーマンスが向上します。

一方、可変Stringsは、操作後にStringの内容が変更された場合、挿入時と取得時に2つの異なるハッシュコードを生成し、Mapの値オブジェクトを失う可能性があります。

3.5. パフォーマンス

前に見たように、Stringsは不変であるため、Stringプールが存在します。 次に、Strings.で操作すると、ヒープメモリが節約され、ハッシュ実装へのアクセスが高速化されるため、パフォーマンスが向上します。

Stringは最も広く使用されているデータ構造であるため、Stringのパフォーマンスを向上させることは、一般にアプリケーション全体のパフォーマンスを向上させる上でかなりの効果があります。

4. 結論

この記事を通じて、Strings are immutable precisely so that their references can be treated as a normal variable and one can pass them around, between methods and across threads, without worrying about whether the actual String object it’s pointing to will change.は次のように結論付けることができます。

また、Java言語設計者がこのクラスを不変にするように促した他の理由が何であるかについても学びました。