[Java] ¿Realmente comprende el objeto Object de Java?

Inserte la descripción de la imagen aquí

1. Información general

Texto original: https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484210&idx=1&sn=9d40e2e4c72f0727c7b7925cbe314fc0&chksm=ebd74233dca0fcb2560677c7chat6chae

Inserte la descripción de la imagen aquí

De hecho, se puede resumir en algunos:

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

El objeto tiene un total de 11 métodos, uno de los cuales es la implementación subyacente registerNatives (), y dos de ellos son métodos sobrecargados de wait () y wait (tiempo de espera largo, nanos int).

Entonces, lo que realmente necesitamos mirar son 8 métodos

Hay un atributo más:

 /**
     * 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();

métodos equals y hashCode

Se puede decir que los métodos quals y hashCode son las preguntas clave de la entrevista. Con String, se puede decir que están en todas partes en las preguntas de la entrevista.

Primero, echemos un vistazo a la implementación nativa de equals y hashCode en Object:

código hash:

public native int hashCode();

igual a:

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

Todos parecen muy simples:

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

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

Para tener más claridad sobre lo que hacen, leamos sus notas:

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Según los comentarios, podemos resumir los siguientes puntos:

  1. Para reescribir el método equals (), debe reescribir el método hashCode ()
  2. El método equals () tiene como valor predeterminado comparar las direcciones de los objetos, usando el operador equivalente ==
  3. El método hashCode () tiene la función de mejorar el rendimiento de los objetos cuya capa inferior es una tabla hash
  4. El mismo objeto (si el objeto no ha sido modificado): luego llamando repetidamente a hashCode () entonces el int devuelto es el mismo.
  5. El método hashCode () se convierte de forma predeterminada a partir de la dirección del objeto

El método equals () también tiene 5 principios predeterminados:

  1. Reflexividad—> Call equals () devuelve verdadero, no importa quién llame a equals () en estos dos objetos, devuelve verdadero
  2. Consistencia—> ¡Siempre que el objeto no se modifique, el resultado correspondiente se devolverá después de múltiples llamadas!
  3. Transitividad—> x.equals (y) e y.equals (z) ambos devuelven verdadero, entonces puede obtener: x.equals (z) devuelve verdadero
  4. Simetría—> x.equals (y) e y.equals (x) deben ser iguales.
  5. El parámetro entrante es nulo y la devolución es falsa

Es fácil entender por qué hashCode () usa la tabla hash como capa inferior para mejorar el rendimiento. Repasemos la inserción de HashMap nuevamente:

Inserte la descripción de la imagen aquí
Si los valores hash no son iguales, ¡puede juzgar directamente que la clave no es igual!

equals y anulación del método hashCode

El método equals () tiene como valor predeterminado comparar las direcciones de los objetos y utiliza el operador equivalente ==. Pero de acuerdo con nuestro desarrollo normal, comparar la dirección del objeto no tiene sentido.

Generalmente, si tenemos dos objetos Dirección, siempre que el número de provincia, el número de ciudad y el número de calle de estos dos objetos sean iguales, ¡consideramos que estos dos objetos son iguales!

Inserte la descripción de la imagen aquí

Los métodos equals y hashCode implementados por String

Es posible que lo hayamos escuchado cuando aprendimos por primera vez: String ha implementado métodos equals y hashCode.

Es por eso que podemos usar directamente String.equals () para determinar si dos cadenas son iguales.

Echemos un vistazo a su implementación:

Inserte la descripción de la imagen aquí

    /**
     * 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;
    }

método toString

A continuación, miramos el método toString, que también es muy simple:

Inserte la descripción de la imagen aquí
El método toString se utiliza principalmente para identificar el objeto:

Inserte la descripción de la imagen aquí

Podemos ver en los resultados anteriores: no podemos ver nada en los resultados ~

Así que generalmente reescribimos toString (), ¡entonces el resultado impreso es muy conveniente para depurarlo!

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

El siguiente resultado se ve mucho mejor:
Inserte la descripción de la imagen aquí

método de clonación

También echemos un vistazo a su nota principal:

Inserte la descripción de la imagen aquí
Después de leer los comentarios anteriores, podemos resumir los siguientes puntos:

El método de clonación se utiliza para la clonación de objetos. Generalmente, el objeto que desea clonar es independiente (separado del objeto original)

Una copia profunda significa que las variables miembro del objeto (si es una referencia de variable) deben clonarse, y una copia superficial significa que la variable miembro no está clonada.

Echemos un vistazo a la copia superficial: el objeto Empleado se copia, pero su variable miembro contratación no se ha clonado, por lo que apunta al mismo objeto Fecha.

Inserte la descripción de la imagen aquí

uso de clonación

Entonces, ¿cómo clonamos objetos? Tanto la copia superficial como la copia profunda son dos pasos:

  1. El objeto clonado debe implementar la interfaz Cloneable
  2. Reescribe el método de clonación, preferiblemente modificado para público

Copia superficial: solo se copia el objeto Persona, pero la fecha no se copia.

public class Person implements Cloneable {
    
    

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

    @Override
    public Object clone() throws CloneNotSupportedException {
    
    

        return super.clone();
    }

}

Copia profunda: no solo copia el objeto Person, sino también la variable miembro de fecha

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 cuestión de clon protegido en estudio adicional

No sé si alguien tiene la misma pregunta que yo:

Solo quiero una copia superficial, ¿puedo llamar al objeto .clone () directamente para lograrlo?

Por ejemplo, ahora tengo un objeto Dirección:

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;
    }
}

¿Qué opinas del siguiente código?

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

todos sabemos:

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

Podría pensar: el método clone () está definido en la clase Object (modificado con protected), y nuestro objeto Address personalizado hereda implícitamente Object (todos los objetos son subclases de Object), luego la subclase llama Está completamente bien que Object modifique clone () con protected

¡Sin embargo, la realidad del IDE me dice que esta compilación falla!

Inserte la descripción de la imagen aquí
La razón del error pensé de inmediato: ¿Me desvié del modificador protegido?

Las clases y atributos modificados protegidos son visibles para usted, este paquete y sus subclases. Esta oración en sí no es incorrecta. Pero debe agregarse: para miembros o métodos protegidos, si la clase molecular y la superclase están en el mismo paquete. No con la clase base 同一个包中的子类, solo访问自身从基类继承而来的受保护成员,而不能访问基类实例本身的受保护成员。

El código anterior es incorrecto: Dirección y Objeto no están en el mismo paquete, y Dirección accede directamente al método de clonación de Objeto. Esto no funcionará.

Déjame tomar dos fotos y mostrártelas (después de leer las imágenes y luego leer la descripción anterior, puedes entender):
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Cinco, esperar y notificar métodos

Los métodos de espera y notificación son en realidad la API que Java nos proporciona para la comunicación entre hilos.

Como de costumbre, veamos lo que dicen los comentarios:

método de espera:

Inserte la descripción de la imagen aquí

método de notificación:

Inserte la descripción de la imagen aquí

método notifyAll ():
Inserte la descripción de la imagen aquí

Después de leer los comentarios anteriores, podemos resumir los siguientes puntos:

  1. Ya sea esperar, notificar o notificar a todos (), debe ser llamado por el objeto de escucha (objeto de bloqueo)
    1. En pocas palabras: todos se llaman en bloques de código síncronos, de lo contrario, se lanzará una excepción.
  2. Notify () despierta un hilo en la cola de espera (no estoy seguro de cuál se despertará), notifyAll () despierta todos los hilos en la cola de espera
  3. Hay 4 situaciones que hacen que se despierte el hilo de wait ()
    1. El hilo fue interrumpido
    2. espera () se acabó el tiempo
    3. Fue despertado por notificar ()
    4. Despertar por notifyAll ()
  4. El hilo que llama a wait () liberará el bloqueo

De hecho, después de resumir lo anterior, no quedará profundamente impresionado. Puede intentar responder algunas preguntas para profundizar su comprensión de esperar () y notificar ().

¿Por qué esperar y notificar en el método Object?

Desde el principio dijimos: esperar () y notificar () son API que Java nos proporciona la comunicación entre subprocesos. Dado que es un subproceso, ¿qué se define en la clase Object, no en la clase Thread? ?

Debido a que nuestro 锁是对象锁[Si olvida que los estudiantes pueden repasar: comprenda el mecanismo de bloqueo de Java], cada objeto puede convertirse en un bloqueo.让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。

El objeto de bloqueo es arbitrario, por lo que estos métodos deben definirse en la clase Object

¿Qué sucede después de que se llama al método de notificación?

Como se mencionó anteriormente, la notificación activará un hilo en la cola de espera.

Pero cabe señalar que:

Después de llamar al método de notificación, el hilo despertado no obtendrá inmediatamente el objeto de bloqueo. En cambio, el objeto de bloqueo se obtendrá después de que se ejecute el bloque de código sincronizado de notificación.

¿Cuál es la diferencia entre dormir y esperar?

Tanto Thread.sleep () como Object.wait () pueden suspender el hilo actual y liberar el control de la CPU.

La principal diferencia es que Object.wait () libera el control del bloqueo del objeto mientras libera la CPU.
Y Thread.sleep () no liberó el candado

Materiales de referencia:

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

Supongo que te gusta

Origin blog.csdn.net/qq_21383435/article/details/108517875
Recomendado
Clasificación