このシリーズの質問については、Java を勉強している人なら誰でも理解できるはずです。もちろん、趣味で Java を学ぶだけでも問題ありません。初級レベルを超えたと思っているけど、よく分からないという方は、ぜひ初級チームに加えてください。
質問 1: 私は何を言っているのでしょうか?
文字列 s = "Hello world!";
多くの人がこれを行っていますが、正確には何を宣言するのでしょうか? 答えは通常、「Hello world!」という内容の文字列です。このような曖昧な答えは、多くの場合、不明確な概念の原因となります。正確に答えたとしたら、おそらく半分の人が間違って答えるでしょう。
このステートメントは、String 型の任意のオブジェクトを指すことができる「s」という名前のオブジェクトへの参照を宣言します。現在、このステートメントは String 型オブジェクト「Hello world!」を指します。これが実際に起こったことです。 String オブジェクトを宣言したのではなく、String オブジェクトのみを指すことができる参照変数を宣言しただけです。したがって、今のステートメントの後に別の文を実行すると、次のようになります。
文字列文字列 = s;
string という名前の String オブジェクトのみを指すことができる別の参照を宣言しました。2 番目のオブジェクトは引き続き元のオブジェクトを指します。つまり、s と同じオブジェクトを指します。
質問 2: 「==」と「equals」メソッドの違いは何ですか?
== 演算子は、変数の値が等しいかどうかを比較するために特に使用されます。より理解しやすいのは次のとおりです。
次のようにコードをコピーします。
int a=10;
int b=10;
この場合、a==b は true になります。
しかし、理解するのが難しいのは次のようなことです。
次のようにコードをコピーします。
String a=new String("foo");
文字列 b=new String("foo");
その場合、a==b は false を返します。
前回の投稿によると、オブジェクト変数は実際には参照であり、その値はオブジェクト自体ではなく、オブジェクトが配置されているメモリ アドレスを指します。 a と b は両方とも new 演算子を使用します。これは、内容が「foo」である 2 つの文字列がメモリ内に生成されることを意味します。「2 つ」あるため、当然、それらは異なるメモリ アドレスに配置されます。 a と b の値は実際には 2 つの異なるメモリ アドレスの値であるため、「==」演算子を使用すると結果は false になります。確かに、a と b が指すオブジェクトの内容は「foo」であり、「等しい」はずですが、== 演算子にはオブジェクトの内容の比較が含まれません。
オブジェクトの内容の比較は、まさに、equals メソッドが行うことです。
Object オブジェクトの等しいメソッドがどのように実装されているかを見てください。
次のようにコードをコピーします。
ブール値等しい(オブジェクト o){
これを返す==o;
}
オブジェクト オブジェクトは、デフォルトで == 演算子を使用します。したがって、自分で作成したクラスがequalsメソッドをオーバーライドしない場合、そのクラスはequalsを使用した場合と==を使用した場合と同じ結果を取得します。また、Object のequals メソッドは、2 つのオブジェクトの内容が等しいかどうかを比較するという、equals メソッドが達成すべき目標を達成していないこともわかります。答えはクラスの作成者が決定する必要があるため、Object はこのタスクをクラスの作成者に任せます。
極端なクラスを見てみましょう。
次のようにコードをコピーします。
クラスモンスター{
プライベート文字列コンテンツ。
...
ブール値等しい(オブジェクト別){ true を返します;}
}
イコールメソッドをオーバーライドしました。この実装により、Monster インスタンス間の比較では、内容に関係なく常に true が返されます。
したがって、equals メソッドを使用してオブジェクトの内容が等しいかどうかを判断するときは、それを当然のことと考えないでください。おそらく、あなたはそれらが等しいと考えているかもしれませんが、このクラスの作成者はそう考えておらず、クラスのequalsメソッドの実装は彼によって制御されているからです。 equals メソッドを使用する必要がある場合、またはハッシュ コード ベースのコレクション (HashSet、HashMap、HashTable) を使用する必要がある場合は、Java ドキュメントを参照して、このクラスの equals ロジックがどのように実装されているかを確認してください。
質問 3: 文字列は変更されましたか?
いいえ。 String は不変クラスとして設計されているため、そのすべてのオブジェクトは不変オブジェクトです。次のコードを見てください。
次のようにコードをコピーします。
文字列 s = "こんにちは";
s = s + "世界!";
sの指す対象は変わったのか? この結論は、このシリーズの最初の記事の結論から簡単に導き出すことができます。何が起こったのか見てみましょう。このコードでは、s は元々「Hello」という内容の String オブジェクトを指しており、その後 s に対して + 操作を実行しました。s が指すオブジェクトは変更されましたか?答えはノーです。この時点では、 s は元のオブジェクトを指していませんが、「Hello world!」という内容を持つ別の String オブジェクトを指しています。元のオブジェクトはまだメモリ内に存在していますが、参照変数 s はそれを指していません。
上記の説明から、文字列がさまざまな方法で頻繁に変更されたり、予期せぬ変更が行われる場合、文字列を表すために String を使用すると、大量のメモリ オーバーヘッドが発生するという別の結論が簡単に得られます。 String オブジェクトは作成後に変更できないため、異なる文字列をそれぞれ表すには String オブジェクトが必要です。現時点では、異なる文字列ごとに新しいオブジェクトを生成するのではなく、変更が可能な StringBuffer クラスの使用を検討する必要があります。さらに、これら 2 つのタイプ間のポリシーの変更は非常に簡単です。
同時に、同じ内容の文字列を使用したい場合、毎回新しい文字列を作成する必要がないこともわかります。たとえば、コンストラクターで s という名前の String 参照変数を初期化し、初期値に設定する場合は、次のようにする必要があります。
次のようにコードをコピーします。
パブリック クラス デモ {
プライベート文字列;
…
公開デモ {
s = "初期値";
}
…
}
s = new String("初期値"); の代わりに
後者は、新しいオブジェクトを生成するたびにコンストラクターを呼び出しますが、パフォーマンスが低く、メモリ消費量が多く、意味がありません。String オブジェクトは変更できないため、同じ内容の文字列を表すために使用できる String オブジェクトは 1 つだけです。 。つまり、上記のコンストラクターを複数回呼び出して複数のターゲットを作成すると、それらの String 型属性はすべて同じターゲットを指すことになります。
上記の結論は、文字列定数の場合、内容が同じであれば、Guangzhou Java Training はそれらが同じ String オブジェクトを表すと考えるという事実にも基づいています。 new キーワードを指定してコンストラクターを呼び出すと、内容が同じかどうかに関係なく、常に新しいターゲットが作成されます。
なぜ String クラスを immutable クラスとして記述する必要があるかについては、その目的によって決まります。実際、String だけでなく、Java 標準クラス ライブラリの多くのクラスも不変です。システムを開発するとき、関連する値のセットを渡すために不変クラスを記述する必要があることがありますが、これも目標指向の考え方の表れです。不変クラスにはいくつかの利点があります。たとえば、その目的は読み取り専用であるため、複数のスレッドによる同時アクセスに問題はありません。もちろん、いくつかの欠点もあります。たとえば、さまざまな状況を表すオブジェクトが必要になるため、機能上の問題が発生する可能性があります。したがって、Java 標準クラス ライブラリには、StringBuffer という変数バージョンも提供されています。