Learn some java knowledge | Immutable classes (1)

01 Basic knowledge

Simply put, an immutable class is a class defined with the final keyword, as follows:

final class ClassName{
}

The familiar final keyword is usually used to define constants. Methods and variables declared with final have the following characteristics:

1. Methods declared as final are not allowed to be overridden.

2. Variables declared as final are not allowed to be changed.
And using final, we can design a special "read-only" "immutable class (immutable class)". When an object of an "immutable class" is created, the properties of this object cannot be changed , and new subclasses cannot . The String class in the JDK is an instance of an immutable class.

02 A miraculous example

Observe the following example:

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

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

Running the code yields the following output:

A@4eec7777

Why is there such a situation? The reason for this is that by default, when an object is printed directly as a string , the object's ` method is called toString()`to get the string representation.

In this example, the class Adoes not define any members, so it inherits the default ` toString()`method implementation. The default ` toString()`method returns a string consisting of the class name and the hash code of the object . Therefore, the "A" in the output string "A@4eec7777" is the class name, and "4eec7777" is the hash code of the object.

After decompiling the compiled .class file, you will see the following output:

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
}

From the decompiled output, we can see that the code actually calls the ` public void println(Object x) ` method in the JDK. View the source code as follows:

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

Continue to look at the valueOf function and see its source code as follows:

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

Continue to look at the toString function:

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

Continue to view hashCode:

@IntrinsicCandidate
public native int hashCode();

At this time, it is found that this method is a local method, and the specific implementation is provided by the JVM developer, and the local method will not be discussed for the time being. So far, it is roughly clear why this example outputs a string of strange strings.

03 Modify the example

Next, modify the previous example slightly, and the output at this time is completely different:

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

}

At this point, the run will output "This is class A". why? Because in the modified code, we rewrite the toString method to return the string representation we want, instead of calling the default method.

Guess you like

Origin blog.csdn.net/qq_59109986/article/details/130733293