JavaインタビューAlchemyシリーズ(1)| Stringに関する一般的なインタビューの質問の分析

例えば:

public static void main(String[] args) {
    //基本数据类型
    int num1 = 100;
    int num2 = 100;
    System.out.println("num1 == num2 : " + (num1 == num2) + "\n");

    //引用类型,其中'System.identityHashCode'可以理解为打印对象地址
    String str1 = "mio4";
    String str2 = "mio4";
    System.out.println("str1 address : " + System.identityHashCode(str1));
    System.out.println("str2 address : " + System.identityHashCode(str1));
    System.out.println("str1 == str2 : " + (str1 == str2) + "\n");

    String str3 = new String("mio4");
    String str4 = new String("mio4");
    System.out.println("str3 address : " + System.identityHashCode(str3));
    System.out.println("str4 address : " + System.identityHashCode(str4));
    System.out.println("str3 == str4 : " + (str3 == str4));
}

上記のコードを実行すると、次の結果が得られます。

num1 == num2:true

str1アドレス:1639705018
str2アドレス:1639705018
str1 == str2:true

str3アドレス:1627674070
str4アドレス:1360875712
str3 == str4:false
str1とstr2のメモリアドレスが両方とも1639705018であることを確認できるため、==を使用してtrueと判断し、

しかし、str3とstr4のアドレスが異なるため、判断は誤りです。

  1. equals()メソッド
    2.1オブジェクトクラスequals()
    Java言語では、すべてのクラスがオブジェクトスーパークラスを継承しています。このクラスにはequals()メソッドもあるので、まずこのメソッドを見てみましょう。

このメソッドは非常に単純で、オブジェクトのメモリアドレスを比較することがわかります。したがって、オブジェクトがこのメソッドをオーバーライドしない場合、デフォルトでこのメソッドが使用されます。つまり、オブジェクトのメモリアドレス値が比較されます。しかし、StringやIntegerのようなクラスは、equals()を書き直しました。以下の例としてStringを取り上げます。

2.2文字列クラスequals()

明らかに、Stringのequals()メソッドはデータ値のみを比較し、オブジェクトのメモリアドレスは比較しません。

テストする例として文字列を取ります:

public static void main(String [] args){ String str1 =“ mio4”; 文字列str2 =“ mio4”;

    String str3 = new String("mio4");
    String str4 = new String("mio4");

    System.out.println("str1 address : " + System.identityHashCode(str1));
    System.out.println("str2 address : " + System.identityHashCode(str1));
    System.out.println("str1.equals(str2) : " + str1.equals(str2) + "\n");

    System.out.println("str3 address : " + System.identityHashCode(str3));
    System.out.println("str4 address : " + System.identityHashCode(str4));
    System.out.println("str3.equals(str4) : " + str3.equals(str4) + "\n");
}

テスト出力は以下のとおりですstr3とstr4のアドレスが異なることがわかりますが、String文字列の内容が同じであるため、equalsの判定はtrueになります。

str1アドレス:1639705018
str2アドレス:1639705018
str1.equals(str2):true

str3アドレス:1627674070
str4アドレス:1360875712
str3.equals(str4):true
3. hashCode()メソッド
3.1なぜそのようなメソッドがあるのですか?
Javaコレクションの使用シナリオ(コレクション)には3つのタイプがあります。1つはリスト、もう1つはキュー、セット内の要素は順序付けされ、要素を繰り返すことができます。次に、セット内に無秩序な要素であるセットのクラスがありますが、要素を繰り返すことはできません。

したがって、これはより深刻な問題です。要素が繰り返されないようにしたい場合、2つの要素が繰り返されているかどうかを判断するための基準は何ですか?これはObject.equalsメソッドです。ただし、要素が追加されるたびにチェックが実行される場合、要素が多いと、セットに追加された要素の比較数が非常に多くなります。つまり、コレクションにすでに1000個の要素がある場合、1001番目の要素がコレクションに追加されると、equalsメソッドが1000回呼び出されます。これは明らかに効率を大幅に低下させます。したがって、Javaはハッシュテーブルの原則を使用します。このように、ハッシュアルゴリズムを使用して、セットに格納する各要素の値を計算し、その値に基づいて配列内の要素の位置を計算します。したがって、新しい要素がコレクションに追加されると、2つのステップに分けることができます。   
最初にこの要素のhashCodeメソッドを呼び出し、次に取得した値に基づいて配列内の要素の位置を計算します。この位置に要素がない場合は、この位置に直接格納します。
すでにこの位置に要素がある場合は、equalsメソッドを呼び出して新しい要素と比較します。同じ場合は格納されません。それ以外の場合は格納されます。この位置は、リンクリストに対応します(JavaでのHashSet、HashMap、およびHashtableの実装は、常にリンクリストの先頭に要素を配置します)。
3.2 hashCode()とequals()の関連付け
 前提:hashCodeに関しては、equalsメソッドと言う必要があります。両方ともObjectクラスのメソッドです。Objectクラスはすべてのクラスの基本クラスであるため、これら2つのメソッドはすべてのクラスでオーバーライドできます。

原則1:x.equals(y)が「true」を返す場合、xとyのhashCode()は等しくなければなりません。
原則2:x.equals(y)が「false」を返す場合、xとyのhashCode()は等しいかそうでないかもしれません;
原則3:xとyのhashCode()が等しくない場合、x.equals(y)は「false」を返す必要があります;
原則4:一般的に、equalsメソッドはユーザーに対して呼び出されます、そしてハッシュコードメソッドは一般にユーザーによって呼び出されません;
原則5:オブジェクト型がコレクションオブジェクトの要素として使用される場合、このオブジェクトは独自のequals()およびhashCode()設計を持つ必要があり、前述のいくつかに準拠する必要があります原則として。
要約すると、注意すべき点は次のとおりです。


equals equalsを持つ2つのオブジェクト、hashCode は等しい必要があります等しくないequalsメソッドを持つ2つのオブジェクト、hashCodeは
0x1 に等しい可能性があります

  1. 文字列のソースコードを見たことがありますか?最終変更を使用する理由
    public final class String
    はjava.io.Serializable、Comparable、CharSequenceを実装します{}
    コアとなる説明:

1.文字列プールを実現する

2.スレッドセーフティ用

3.文字列の不変性を実現するためにHashCodeを作成できます

最後に変更された文字列は、文字列の非継承性を表し、最後に変更されたchar []は、格納されたデータの変更不可性を表します。しかし:finalは不変性を表しますが、参照アドレスだけが不変ですが、配列自体が変更されないという意味ではありません。
Finalは配列自体を変更することもできますが、この2つはStringの不変性を保証するため、現時点ではprivateも役立ちます。
文字列が不変である場合にのみ文字列プールを実現できるため、文字列が不変であることを確認してください。文字列プールの実装では、異なる文字列変数がすべてプール内の同じ文字列を指すため、実行時に多くのヒープ領域を節約できます。ただし、文字列が変数の場合、String.intern()は実装されません。この場合、変数がその値を変更すると、この値を指す他の変数の値も変更されるためです。
文字列は不変であるため、HashCodeは作成時にキャッシュされ、再計算する必要はありません。これにより、文字列はMapのキーとして非常に適切になり、文字列の処理速度は他のキーオブジェクトよりも速くなります。これは、HashMapのキーが文字列を使用することが多いということです。
2.
String の初期化メソッドとは何ですか?String型の初期化は、Javaでは2つのタイプに分けられます。

1つのタイプは、文字を二重引用符で囲むことによって初期化され、もう1つのタイプは
、newキーワードを使用して通常のオブジェクトのようにStringインスタンスを初期化します。
前者は定数プールlの定数を開き、対応する参照を返します。後者はヒープの定数を開き、対応するオブジェクトを返します。したがって、2つの参照は明らかに異なります。

public static void main(String…args){ String s1 = "abcd"; String s2 = new String( "abcd"); System.out.println(s1 == s2); // false } そして、定数プール内の定数メモリオーバーヘッドと作成時間のオーバーヘッドを節約するために共有できます(これは、定数プールが導入された理由でもあります)。次に例を示します。




public static void main(String…args){ String s1 = "abcd"; String s2 = "abcd"; System.out.println(s1 == s2); // true } 2つを組み合わせると、実際には別の答えが得られますよくあるインタビューの質問:




public static void main(String ... args){ String s = new String( "abcd"); } この文はいくつのオブジェクトを作成しますか?


まず、「abcd」自体が定数プールに配置されるオブジェクトであることは間違いありません。ここでは新しいキーワードが使用されているため、sで取得したオブジェクトをヒープ内に作成する必要があります。つまり、ここでは実際に2つのオブジェクトが作成されています。

この関数が呼び出される前に文字列 "abcd"が他の場所にすでに存在している場合は、事前に定数プールに作成されていることに注意してください。この時点では、1つのオブジェクト、つまり、ヒープに作成された新しい文字列( "abcd")オブジェクトのみが作成されます。

  1. 文字列スレッドは安全ですか?
    Stringは不変のクラスです。いったんStringオブジェクトが作成されると、その値を変更することはできません。したがって、スレッドセーフであり、マルチスレッド環境で安全に使用できます。

  2. HashMapを使用するときに、なぜStringをキーとして使用することが多いのですか?
    文字列は不変であるため、文字列が作成されると、そのハッシュコードはキャッシュされ、再度計算する必要はありません。HashMapの内部実装は、キーのハッシュコードを使用して値の格納場所を決定するため、他のオブジェクトよりも高速です。これが、通常、HashMapオブジェクトとしてStringを使用する理由です。

  3. Stringのintern()メソッドとは何ですか?
    String.intern()メソッドは、実行時に定数プールに定数を追加できます。それが機能する方法は次のとおりです。

定数プールにこの文字列の値と正確に等しい定数がある場合、intern()メソッドは定数プールに存在する定数への参照を返します。
定数プールにたまたまこの文字列の値と等しい定数がない場合、新しい定数が定数プールに作成され、この文字列の値が定数プールに新しく作成された定数に割り当てられます。intern()メソッドは、この新しく作成された定数への参照を返します。
例:

public static void main(String…args){ String s1 =“ abcd”; 文字列s2 = new String(“ abcd”);

/**
 * s2.intern() will first search String constant pool,
 * of which the value is the same as s2.
 */
String s3 = s2.intern();
// As s1 comes from constant pool, and s3 is also comes from constant pool, they're same.
System.out.println(s1 == s3);
// As s2 comes from heap but s3 comes from constant pool, they're different.
System.out.println(s2 == s3); 

}

/ **

  • 出力:
  • 本当
  • false
    * /
    最初の最初の部分を思い出して、なぜintern()関数を導入するのですか?これは、定数プールに「abcd」が割り当てられているにもかかわらず、新しい文字列(「abcd」)が使用されると、値がabcdの新しいオブジェクトがヒープに作成されるためです。想像してみてください。そのような文が100個ある場合、ヒープ内に同じ値のオブジェクトを100個作成する必要はありませんか。これは、非効率的な操作とスペースの無駄になります。

したがって、intern()が導入されると、定数プールに直接移動して、同じ値のStringオブジェクトがあるかどうかを確認します。これにより、スペースが大幅に節約され、操作効率が向上します。

  1. 定数プールに関するプログラミングの質問(1)
    文字列s1 = "ab";
    文字列s2 = "abc";
    文字列s3 = s1 + "c";

System.out.println(s3 == s2); // falseは等しくない、s1は変数、コンパイル時に値を判別できない、値はメモリに作成され、s3はヒープメモリにある s2は定数プールにあるため、等しくありません。
System.out.println(s3.equals(s2)); // trueは、2つのオブジェクトの値が等しいかどうかを比較します。
上記のコードの説明:

文字列s1 =“ abc”; 文字列s2 =“ abc”;

s1は定数プールに作成され、s2はまず定数プールに何かがあるかどうかをチェックし、ある場合はそれをポイントし、ない場合は定数プールに1つ作成してポイントします。したがって、s1とs2の2つの比較は同じです。

  1. 定数プールに関するプログラミングの質問(2)
    文字列s1 = new String( "Hello");
    文字列s2 = new String( "Hello");
    答えは3つのオブジェクトです。

まず、1行目は文字列プール内の「hello」オブジェクトです。

2番目の行は、ヒープメモリ内の値が「hello」の新しい文字列です。

3番目に、2行目は、ヒープメモリに "hello"を含む新しい文字列です。ここでは、「hello」文字列プールの文字列が再利用されています。

  1. String、StringBuffer、StringBuilderの違いについて話してください。
    Stringは不変のクラスであり、Stringを操作するたびに、常に新しい文字列が作成されます。Stringの操作は非常に多くのリソースを必要とするため、JavaはStringを操作するための2つのツールクラス、StringBufferとStringBuilderを提供します。
    StringBufferとStringBuilderは変更可能なクラス、StringBufferはスレッドセーフ、StringBuilderはスレッドセーフではありません。したがって、複数のスレッドが同じ文字列を操作する場合は、StringBufferを選択する必要があります。マルチスレッドを処理する必要がないため、StringBuilderはStringBufferよりも効率的です。
    Amazonレビューwww.yisuping.com

おすすめ

転載: blog.csdn.net/weixin_45032957/article/details/108599271