文字列クラスを認識します(文字列とメモリのレイアウトに関連する問題(1))

1.文字列を作成します

文字列を作成する一般的な方法

// 方式一
String str = "Hello Bit";
// 方式二
String str2 = new String("Hello Bit");
// 方式三
char[] array = {
    
    'a', 'b', 'c'};
String str3 = new String(array);

公式ドキュメント(https://docs.oracle.com/javase/8/doc/api/index.html)では、Stringが他の多くの構築方法もサポートしていることがわかります。使用するときに確認できます。

予防:

  • 「hello」のような文字列リテラル定数も文字列です。
  • 文字列は参照型です。String str ="Hello";

コードメモリのレイアウトは次のとおりです。

思い出「引用」

  • 参照は、アドレスを格納するためにスタック上に小さなメモリスペースが開かれることを除いて、C言語のポインタに似ていますが、参照とポインタは同じではなく、ポインタはさまざまな数値演算(ポインタ+ 1)などを実行できます。しかし、参照することはできません。これは一種の「それほど柔軟ではない」ポインタです。
  • さらに、参照は、オブジェクトに「投稿」されるラベルと考えることもできます。オブジェクトには、1つまたは複数のラベルを付けることができます。オブジェクトにラベルがない場合、オブジェクトは次のようになります。 JVMによってガベージとして扱われますオブジェクトはリサイクルされます。
  • Javaの配列、文字列、およびカスタムクラスはすべて参照型です。

Stringは参照型であるため、次のコードの場合

String str1 = "Hello";
String str2 = str1;

メモリレイアウトを図に示します
ここに画像の説明を挿入。str1とstr2はそれに応じて変更されますか?

String str1 = "Hello";
String str2 = str1;
str1 = "world";
System.out.println(str2);
// 执行结果
Hello

str1を「変更」した後、str2は変更されなかったのでしょうか、それともhelloですか?
実際、str1 = "world"のようなコードは「変更された」文字列ではなく、str1の参照が新しい文字列オブジェクトを指すようにします。 。

メモリレイアウト図:
ここに画像の説明を挿入

2.文字列の比較は等しい

2つのint型変数がある場合は、==を使用してそれらが等しいかどうかの判断を完了することができます。

int x = 10 ;
int y = 10 ;
System.out.println(x == y); 
// 执行结果
true

あなたが==がStringgオブジェクトで今使われていると言うなら?

代码1
String str1 = "Hello";
String str2 = "Hello"; 
System.out.println(str1 == str2); 
// 执行结果
true

問題ないようです。別のコードを試してみて、状況があまり良くないことがわかりました。

代码2
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2);
// 执行结果
false

文字列を作成する2つの方法の違いを分析します。

コードメモリレイアウト1:
ここに画像の説明を挿入 str1とstr2が同じオブジェクトを指しているのは、「Hello」がそのような文字列定数であるためです。の文字列定数プール

文字列定数プールについて:
「Hello」などの文字列リテラル定数も、格納するために一定量のメモリスペースが必要です。このような定数には、変更する必要がないという特徴があります(定数)。コードすべての参照で「Hello」を使用する必要がある場合は、定数プール内のこの場所を直接参照でき、「Hello」をメモリに2回格納する必要はありません。

コード2のメモリレイアウト:
ここに画像の説明を挿入

String str1 = new String("Hello");文字列ヒープに、このようなA方式と同等で作成され、「こんにちは」の内容を保存するために追加のスペースを開け、つまりオブジェクト、2の「Hello」のメモリがあります。

文字列==比較は、文字列の内容を比較するのではなく、2つの参照が同じオブジェクトを指しているかどうかを比較します。


オブジェクトの比較についてオブジェクト指向プログラミング言語では、オブジェクトを比較する方法、IDを比較する方法、値を比較する方法、タイプを比較する方法が3つあります。ほとんどのプログラミング言語では、値の比較と比較に==が使用されます。ただしJavaでは==はアイデンティティを比較するために使用されます。
比較値を理解してアイデンティティを比較する方法は?
速達を受け取ることができ、小包ロッカーがあるシーンを想像できます。その上に多くのグリッドがあります。各グリッドには物があります。 。

Javaで文字列の内容を比較する場合は、Stringクラスによって提供されるequalsメソッドを使用する必要があります。

String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2));
// System.out.println(str2.equals(str1)); // 或者这样写也行
// 执行结果
true

equalsの使用に関する注意
2つの文字列strと "Hello"が等しいかどうかを比較する必要がありますが、どのように記述しますか?

String str = new String("Hello");
// 方式一
System.out.println(str.equals("Hello"));
// 方式二
System.out.println("Hello".equals(str));

「方法2」を使用することをお勧めします。strがnullになると、方法1のコードは例外をスローしますが、方法2は例外をスローしません。

String str = null;
// 方式一
System.out.println(str.equals("Hello"));  // 执行结果抛出java.lang.NullPointerException异常
// 方式二
System.out.println("Hello".equals(str)); //执行结果 false

注:「Hello」のようなリテラル定数は基本的にStringオブジェクトであり、equalsやその他のStringオブジェクトメソッドを使用できます。

3.文字列定数プール

上記の例では、Stringクラスの2つのインスタンス化操作、直接割り当てと新しい新しい文字列があります
。1。直接割り当て

String str1 = "hello" ;
String str2 = "hello" ; 
String str3 = "hello" ; 
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3);   //true

ここに画像の説明を挿入新しいヒープメモリスペースが今開かれていないのはなぜですか?

Stringクラスのデザインは、共有デザインパターンを使用します

実際、オブジェクトプール(文字列定数プール)はJVMの下部に自動的に維持されます

  • Stringクラスのオブジェクトをインスタンス化するために直接割り当てモードが使用されている場合、インスタンス化されたオブジェクト(文字列コンテンツ)はこのオブジェクトプールに自動的に保存されます。
  • 次回も直接代入モードを使用してStringクラスオブジェクトを宣言する場合、この時点でオブジェクトプールに指定されたコンテンツがあると、直接参照されます。
  • そうでない場合は、新しい文字列オブジェクトを開き、次の使用のためにオブジェクトプールに保存します

「プール」(プール)を理解する

  • 「プール」は、プログラミングの効率を向上させるための一般的で重要な方法です。今後の学習では、さまざまな「メモリプール」、「スレッドプール」、「データベース接続プール」に遭遇します...
  • しかし、
    気の概念はコンピューターに限ったことではなく、生命に由来します。たとえば、実生活では「緑茶」と呼ばれる女神がいます。高福愛と物について話している間、彼は他のディックと関わっていることもあります。あいまいです。現時点では、このディアオシは「スペアタイヤ」と呼ばれていますが、なぜスペアタイヤを持っているのですか?高福水と別れるとすぐにスペアタイヤを見つけることができるので、より効率的です。
  • この女神が同時に多くのディックで曖昧である場合、これらのスペアタイヤはスペアタイヤプールと呼ばれます。

2.構築メソッドの使用構築メソッドを使用して
クラスオブジェクトをインスタンス化するのが標準的な方法です。次の手順を分析します。

String str = new String("hello");

ここに画像の説明を挿入このアプローチには2つの欠点があります。

  1. 文字列構築方法を使用すると、2つのヒープメモリスペースが開かれ、ヒープメモリスペースの1つがガベージスペースになります(文字列定数「hello」も匿名オブジェクトです。一度使用すると、使用時間が長くなると、ガベージスペースになります。JVMによって自動的にリサイクルされます)。
  2. 文字列共有の問題。同じ文字列が複数回保存される可能性があり、スペースの無駄になります。

Stringのinternメソッドを使用して、Stringオブジェクトを文字列定数プールに手動で追加できます。

// 该字符串常量并没有保存在对象池之中
String str1 = new String("hello") ; 
String str2 = "hello" ; 
System.out.println(str1 == str2);
// 执行结果
false

String str1 = new String("hello").intern() ;
String str2 = "hello" ; 
System.out.println(str1 == str2);
// 执行结果
true

ここに画像の説明を挿入インタビューの質問:Stringクラスの2つのタイプのオブジェクトのインスタンス化の違いを説明してください

  1. 直接割り当て:ヒープメモリスペースの一部のみが開かれ、文字列オブジェクトは次回の使用のためにオブジェクトプールに自動的に保存されます。
  2. 構築メソッド:2つのヒープメモリスペースが開かれ、オブジェクトプールに自動的に保存されません。intern()メソッドを使用して、手動でプールに入ることができます。

したがって、通常、直接割り当てを使用してStringオブジェクトを作成します。

おすすめ

転載: blog.csdn.net/qq_47364122/article/details/112968111