Aprende algunos conocimientos de Java | Clases inmutables (1)

01 Conocimientos básicos

En pocas palabras, una clase inmutable es una clase definida con la palabra clave final, de la siguiente manera:

final class ClassName{
}

La palabra clave familiar final se usa generalmente para definir constantes. Los métodos y variables declarados con final tienen las siguientes características:

1. No se permite anular los métodos declarados como definitivos.

2. No se permite modificar las variables declaradas como definitivas.
Y usando final, podemos diseñar una "clase inmutable (clase inmutable)" especial de "solo lectura". Cuando se crea un objeto de una "clase inmutable", las propiedades de este objeto no se pueden cambiar y no se pueden derivar nuevas subclases de esta clase . La clase String en el JDK es una instancia de una clase inmutable.

02 Un ejemplo milagroso

Observa el siguiente ejemplo:

public class ExplorationJDKSource {
    public static void main(String[] args) {
        System.out.println(new A());
    }
}

//类A没有定义任何的成员
class A {
}

Ejecutar el código produce el siguiente resultado:

A@4eec7777

¿Por qué existe tal situación? La razón de esto es que, de manera predeterminada, cuando un objeto se imprime directamente como una cadena , se llama al método ` del objeto toString()`para obtener la representación de la cadena.

En este ejemplo, la clase Ano define ningún miembro, por lo que hereda la toString()`implementación del método predeterminado. El método predeterminado ` toString()`devuelve una cadena que consiste en el nombre de la clase y el código hash del objeto . Por lo tanto, la "A" en la cadena de salida "A@4eec7777" es el nombre de la clase y "4eec7777" es el código hash del objeto.

Después de descompilar el archivo .class compilado, verá el siguiente resultado:

PS E:\ProgrammingFiles\Java\CompilerLab\out\production\CompilerLab> javap -c .\ExplorationJDKSource.class
Compiled from "ExplorationJDKSource.java"
public class ExplorationJDKSource {
  public ExplorationJDKSource();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
  public ExplorationJDKSource();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: new           #13                 // class A
       6: dup
       7: invokespecial #15                 // Method A."<init>":()V
      10: invokevirtual #16                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      13: return
}

A partir de la salida descompilada, podemos ver que el código en realidad llama al método ` public void println(Object x) ` en el JDK. Vea el código fuente de la siguiente manera:

    public void println(Object x) {
        String s = String.valueOf(x);
        if (getClass() == PrintStream.class) {
            // need to apply String.valueOf again since first invocation
            // might return null
            writeln(String.valueOf(s));
        } else {
            synchronized (this) {
                print(s);
                newLine();
            }
        }
    }

Continúe mirando la función valueOf y vea su código fuente de la siguiente manera:

    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

Continúe observando la función toString:

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

Continuar para ver hashCode:

@IntrinsicCandidate
public native int hashCode();

En este momento, se encuentra que este método es un método local, y el desarrollador de JVM proporciona la implementación específica , y el método local no se discutirá por el momento. Hasta ahora, está más o menos claro por qué este ejemplo genera una cadena de cadenas extrañas.

03 Modificar el ejemplo

A continuación, modifique ligeramente el ejemplo anterior y el resultado en este momento es completamente diferente:

public class ExplorationJDKSource {
    public static void main(String[] args) {
        System.out.println(new A());
    }
}

//类A没有定义任何的成员
class A {
    @Override
    public String toString() {
        return "This is class A";
    }

}

En este punto, la ejecución generará "Esta es la clase A". ¿por qué? Porque en el código modificado, reescribimos el método toString para devolver la representación de cadena que queremos, en lugar de llamar al método predeterminado.

Supongo que te gusta

Origin blog.csdn.net/qq_59109986/article/details/130733293
Recomendado
Clasificación