1. 序章

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

Javaの作成者であるJamesGoslingは、インタビューで、いつ不変を使用すべきかを尋ねられたことがあります。

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

彼はさらに、キャッシング、セキュリティ、複製なしでの簡単な再利用など、不変性が提供する機能を述べている彼の議論を支持しています。

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

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

不変オブジェクトは、完全に作成された後も内部状態が一定のままであるオブジェクトです。 つまり、オブジェクトが変数に割り当てられると、参照を更新したり、内部状態を変更したりすることはできません。

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

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

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

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

3.1. Stringプールを紹介します

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

Java文字列プールは文字列がJVMによって格納される特別なメモリ領域です。  Strings はJavaで不変であるため、JVMは、各リテラル String のコピーを1つだけプールに格納することにより、それらに割り当てられるメモリの量を最適化します。 このプロセスはインターンと呼ばれます。

String s1 = "Hello World";
String s2 = "Hello World";
         
assertThat(s1 == s2).isTrue();

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

Java Stringプール専用の別の記事があります。 詳細については、その記事に進んでください。

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オブジェクトへの参照がまだあることに注意してください。

文字列が変更可能である場合、更新を実行するまでに、セキュリティチェックを実行した後でも、受け取った文字列が安全であるかどうかを確認できません。信頼できない呼び出し元メソッドにはまだ参照があります整合性チェックの合間にStringを変更できます。 したがって、この場合、クエリはSQLインジェクションを起こしやすくなります。 したがって、変更可能な Strings は、時間の経過とともにセキュリティの低下につながる可能性があります。

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

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

3.3. 同期

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

したがって、 一般に、不変オブジェクトは、同時に実行されている複数のスレッド間で共有できます。 また、スレッドセーフですスレッドが値を変更した場合、同じものを変更する代わりに、新しいで作成されますプール。 したがって、Stringsはマルチスレッドに対して安全です。

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

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

不変性は、Stringsの値が変更されないことを保証します。 そう hashCode()メソッドは、キャッシュを容易にするためにStringクラスでオーバーライドされます。これにより、ハッシュは最初のhashCode()呼び出し中に計算およびキャッシュされ、それ以降は同じ値が返されます。

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

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

3.5. パフォーマンス

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

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

4. 結論

この記事を通じて、文字列は正確に不変であるため、参照を通常の変数として扱い、メソッド間およびスレッド間で、実際の文字列オブジェクトが指しているかどうかを気にすることなく、文字列を渡すことができます。変更されます。

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