Java の知識を学ぶ | 不変クラス (1)

01 基礎知識

簡単に言えば、不変クラスは、次のように、final キーワードで定義されたクラスです。

final class ClassName{
}

おなじみの Final キーワードは通常、定数を定義するために使用されます。Final で宣言されたメソッドと変数には次の特性があります。

1. Final として宣言されたメソッドはオーバーライドできません。

2. Final として宣言された変数は変更できません。
そして、final を使用すると、特別な「読み取り専用」の「不変クラス (immutable class)」を設計できます。 「不変クラス」のオブジェクトが作成される と、このオブジェクトのプロパティを変更したりこのクラスから新しいサブクラスを派生したりすることはできませんJDK の String クラスは、不変クラスのインスタンスです。

02 奇跡の一例

次の例を見てください。

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

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

コードを実行すると、次の出力が得られます。

A@4eec7777

なぜそのような状況が起こるのでしょうか?その理由は、デフォルトでは、オブジェクトがstring として直接出力されるときに、オブジェクトの ` メソッドが呼び出されてtoString()`文字列表現が取得されるためです。

この例では、クラスはAメンバーを定義していないため、デフォルトの `toString()`メソッド実装を継承します。デフォルトの `メソッドは、クラス名オブジェクトのハッシュ コードで構成される文字列toString()`を返します。したがって、出力文字列「A@4eec7777」の「A」はクラス名、「4eec7777」はオブジェクトのハッシュコードです。

コンパイルされた .class ファイルを逆コンパイルすると、次の出力が表示されます。

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
}

逆コンパイルされた出力から、コードが実際にJDK の` public void println(Object x) ` メソッドを呼び出していることがわかります。ソース コードは次のように表示されます。

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

引き続き valueOf 関数を見て、次のようにそのソース コードを確認します。

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

引き続き toString 関数を見てみましょう。

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

引き続きハッシュコードを表示します。

@IntrinsicCandidate
public native int hashCode();

現時点では、このメソッドはローカル メソッドであることがわかり、具体的な実装はJVM開発者によって提供されるため、当面はローカル メソッドについては説明しません。これまでのところ、この例が奇妙な文字列を出力する理由はほぼ明らかです。

03 例を変更する

次に、前の例を少し変更すると、この時点での出力はまったく異なります。

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

}

この時点で、実行により「This is class A」と出力されます。なぜ?変更されたコードでは、デフォルトのメソッドを呼び出す代わりに、必要な文字列表現を返すように toString メソッドを書き換えているためです。

おすすめ

転載: blog.csdn.net/qq_59109986/article/details/130733293