[Java] JavaのObjectオブジェクトを本当に理解していますか?

ここに画像の説明を挿入

1。概要

元のテキスト:https : //mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484210&idx=1&sn=9d40e2e4c72f0727c7b7925cbe314fc0&chksm=ebd74233dca0cb2560677c7dc7746cf166aedcab195dcab

ここに画像の説明を挿入

実際、これはいくつかに要約できます。

registerNatives()【底层实现、不研究】
hashCode()
equals(Object obj)
clone()
toString()
notify()
notifyAll()
wait(long timeout)【还有重载了两个】
finalize()

オブジェクトには合計11のメソッドがあり、そのうちの1つは基礎となる実装registerNatives()で、そのうちの2つはwait()およびwait(long timeout、int nanos)のオーバーロードメソッドです。

だから私たちが実際に見る必要があるのは8つの方法です

属性がもう1つあります。

 /**
     * Returns the runtime class of this {@code Object}. The returned
     * {@code Class} object is the object that is locked by {@code
     * static synchronized} methods of the represented class.
     *
     * <p><b>The actual result type is {@code Class<? extends |X|>}
     * where {@code |X|} is the erasure of the static type of the
     * expression on which {@code getClass} is called.</b> For
     * example, no cast is required in this code fragment:</p>
     *
     * <p>
     * {@code Number n = 0;                             }<br>
     * {@code Class<? extends Number> c = n.getClass(); }
     * </p>
     *
     * @return The {@code Class} object that represents the runtime
     *         class of this object.
     * @jls 15.8.2 Class Literals
     */
    public final native Class<?> getClass();

equalsおよびhashCodeメソッド

qualsメソッドとhashCodeメソッドはインタビューの重要な質問であると言えますが、Stringの場合、インタビューの質問のどこにでもあると言えます。

まず、ObjectのequalsおよびhashCodeのネイティブ実装を見てみましょう。

ハッシュコード:

public native int hashCode();

等しい:

public boolean equals(Object obj) {
    
    
    return (this == obj);
}

それらはすべて非常にシンプルに見えます:

hashCode()native方法底层实现了。

equals()就直接==判断是否相等了。

彼らが何をしているかをより明確にするために、そのメモを読みましょう:

ここに画像の説明を挿入
ここに画像の説明を挿入

コメントによると、次の点を要約できます。

  1. equals()メソッドを書き換えるには、hashCode()メソッドを書き換える必要があります
  2. equals()メソッドは、==等価演算子を使用して、デフォルトでオブジェクトのアドレスを比較します
  3. hashCode()メソッドには、最下層がハッシュテーブルであるオブジェクトのパフォーマンスを向上させる機能があります。
  4. 同じオブジェクト(オブジェクトが変更されていない場合):繰り返しhashCode()を呼び出すと、返されるintは同じです!
  5. hashCode()メソッドは、デフォルトでオブジェクトのアドレスから変換されます

equals()メソッドにも5つのデフォルトの原則があります。

  1. Reflexivity—> Call equals()はtrueを返します。誰がこれら2つのオブジェクトでequals()を呼び出しても、trueを返します。
  2. 整合性—>オブジェクトが変更されない限り、複数の呼び出しの後に対応する結果が返されます。
  3. Transitivity—> x.equals(y)とy.equals(z)の両方がtrueを返すと、次の結果が得られます。x.equals(z)はtrueを返します
  4. 対称性-> x.equals(y)とy.equals(x)は等しくなければなりません。
  5. 入力パラメータがnullで、戻り値がfalseです

hashCode()がハッシュテーブルを最下層として使用してパフォーマンスを向上させる理由は簡単に理解できます。HashMapの挿入をもう一度見てみましょう。

ここに画像の説明を挿入
ハッシュ値が等しくない場合は、キーが等しくないと直接判断できます!

equalsおよびhashCodeメソッドのオーバーライド

equals()メソッドはデフォルトでオブジェクトのアドレスを比較し、==等価演算子を使用します。しかし、私たちの通常の開発によると、オブジェクトのアドレスを比較しても意味がありません。

一般に、2つのAddressオブジェクトがある場合、これら2つのオブジェクトの州番号、都市番号、および番地が等しい限り、これら2つのオブジェクトは等しいと見なされます!

ここに画像の説明を挿入

Stringによって実装されたequalsおよびhashCodeメソッド

最初に学んだときに聞いたことがあるかもしれません。Stringは、equalsメソッドとhashCodeメソッドを実装しています。

これが、String.equals()を直接使用して、2つの文字列が等しいかどうかを判断できる理由です。

その実装を見てみましょう:

ここに画像の説明を挿入

    /**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
    
    
        int h = hash;
        if (h == 0 && value.length > 0) {
    
    
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
    
    
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

toStringメソッド

次に、これも非常に簡単なtoStringメソッドを見てみましょう。

ここに画像の説明を挿入
toStringメソッドは、主にオブジェクトを識別するために使用されます。

ここに画像の説明を挿入

上記の結果から確認できます:結果には何も表示されません〜

そのため、通常はtoString()を書き換え、出力された結果はデバッグに非常に便利です。

  @Override
    public String toString() {
    
    
        return "Address{" +
                "provinceNo=" + provinceNo +
                ", cityNo=" + cityNo +
                ", streetNo=" + streetNo +
                '}';
    }

次の結果ははるかに良く見えます:
ここに画像の説明を挿入

クローン方式

トップノートも見てみましょう:

ここに画像の説明を挿入
上記のコメントを読んだ後、次の点を要約できます。

cloneメソッドはオブジェクトのクローン作成に使用されます。通常、クローンを作成するオブジェクトは独立しています(元のオブジェクトとは別です)。

ディープコピーは、オブジェクトのメンバー変数(変数参照の場合)を複製する必要があることを意味し、シャローコピーは、メンバー変数が複製されないことを意味します。

浅いコピーを見てみましょう。Employeeオブジェクトはコピーされていますが、そのメンバー変数hireは複製されていないため、同じDateオブジェクトを指しています!

ここに画像の説明を挿入

クローンの使用

それでは、オブジェクトをどのようにクローンしますか?浅いコピーと深いコピーはどちらも2つのステップです。

  1. 複製されたオブジェクトはCloneableインターフェースを実装する必要があります
  2. クローンメソッドを書き換えます。できればパブリックに変更します。

浅いコピー:Personオブジェクトのみがコピーされますが、日付はコピーされません!

public class Person implements Cloneable {
    
    

    // 可变的成员变量
    private Date date;

    @Override
    public Object clone() throws CloneNotSupportedException {
    
    

        return super.clone();
    }

}

ディープコピー:Personオブジェクトだけでなく、日付メンバー変数もコピーします

public class Person implements Cloneable {
    
    

    // 可变的成员变量
    public  Date date;

    @Override
    public Object clone() throws CloneNotSupportedException {
    
    


        // 拷贝Person对象
        Person person = (Person) super.clone();

        // 将可变的成员变量也拷贝
        person.date = (Date) date.clone();


        // 返回拷贝的对象
        return person;
    }

}

4.2クローン質問のさらなる研究が保護されました

私と同じ質問がある人がいるかどうかはわかりません。

浅いコピーのみが必要ですが、オブジェクト.clone()を直接呼び出してそれを実現できますか?

たとえば、Addressオブジェクトができました。

public class Address  {
    
    

    private int provinceNo;
    private int cityNo;
    private int streetNo;

    public Address() {
    
    
    }

    public Address(int provinceNo, int cityNo, int streetNo) {
    
    
        this.provinceNo = provinceNo;
        this.cityNo = cityNo;
        this.streetNo = streetNo;
    }
}

次のコードについてどう思いますか?

Address address = new Address(1, 2, 3);
    address.clone();

私たちは皆知っています:

protected修饰的类和属性,对于自己、本包和其子类可见

あなたは考えるかもしれません:clone()メソッドはObjectクラスで定義され(protectedで変更)、カスタムのAddressオブジェクトは暗黙的にObjectを継承し(すべてのオブジェクトはObjectのサブクラスです)、次にサブクラスが呼び出しますObjectがclone()をprotectedで変更しても問題ありません

ただし、IDEの現実から、このコンパイルは失敗することがわかります。

ここに画像の説明を挿入
私がすぐに思ったエラーの理由:保護された修飾子から逸脱しましたか?

保護された変更済みのクラスと属性は、自分自身、このパッケージとそのサブクラスに表示されます。この文自体は間違っていません。ただし、追加する必要があります。保護されたメンバーまたはメソッドの場合、分子クラスとスーパークラスが同じパッケージにあるかどうか。基本クラス同一个包中的子类ではなく、访问自身从基类继承而来的受保护成员,而不能访问基类实例本身的受保护成员。

上記のコードは間違っています。AddressとObjectは同じパッケージ内になく、AddressはObjectのcloneメソッドに直接アクセスします。これは機能しません。

2つの写真を撮って見せます(写真を読んでから、上記の説明を読んだ後、理解できます)。
ここに画像の説明を挿入
ここに画像の説明を挿入

5、待機して通知するメソッド

待機および通知メソッドは、実際にはJavaがスレッド間の通信のために提供するAPIです。

いつものように、コメントの内容を見てみましょう。

待機方法:

ここに画像の説明を挿入

通知方法:

ここに画像の説明を挿入

notifyAll()メソッド:
ここに画像の説明を挿入

上記のコメントを読んだ後、次の点を要約できます。

  1. wait、notify、notifyAll()のいずれであっても、リスナーオブジェクト(ロックオブジェクト)から呼び出す必要があります。
    1. 簡単に言うと、これらはすべて同期コードブロックで呼び出されます。そうしないと、例外がスローされます。
  2. Notify()は待機キュー内のスレッドを起動し(どのスレッドが起動するかは不明)、notifyAll()は待機キュー内のすべてのスレッドを起動します
  3. wait()のスレッドが起こされる4つの状況があります
    1. スレッドが中断されました
    2. wait()タイムアップ
    3. notify()によって起こされた
    4. notifyAll()によるウェイクアップ
  4. wait()を呼び出すスレッドはロックを解放します

実際、上記を要約した後は、それほど印象に残ることはありませんが、いくつかの質問に答えて、wait()とnotify()の理解を深めることができます。

Objectメソッドに待機して通知するのはなぜですか?

最初から言った:wait()とnotify()は、Javaがスレッド間の通信を提供するAPIです。これはスレッドなので、ThreadクラスではなくObjectクラスで何が定義されていますか? ?

私たちのため锁是对象锁、すべてのオブジェクトがロックになることができます:[Javaのロック機構を理解するあなたは、学生が確認することができます忘れてしまった場合]。让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。

ロックオブジェクトは任意なので、これらのメソッドはObjectクラスで定義する必要があります

notifyメソッドが呼び出された後はどうなりますか?

上記のように、notifyは待機キュー内のスレッドを起こします。

ただし、次のことに注意してください。

notifyメソッドが呼び出された後、起こされたスレッドはすぐにロックオブジェクトを取得しません。代わりに、ロックされたオブジェクトは、通知の同期コードブロックが実行された後に取得されます。

睡眠と待機の違いは何ですか?

Thread.sleep()とObject.wait()はどちらも、現在のスレッドを一時停止してCPU制御を解放できます。

主な違いは、Object.wait()は、CPUを解放しながらオブジェクトロックの制御を解放することです。
そしてThread.sleep()はロックを解放しませんでした

参考資料:

https://blog.csdn.net/lingzhm/article/details/44940823

http://www.cnblogs.com/dolphin0520/p/3920385.html

https://www.cnblogs.com/eer123/p/7880789.html

https://www.jianshu.com/p/f4454164c017

おすすめ

転載: blog.csdn.net/qq_21383435/article/details/108517875