ホームページ  >  記事  >  Java  >  Java での最終実装原則の詳細な分析 (例付き)

Java での最終実装原則の詳細な分析 (例付き)

不言
不言転載
2018-11-27 16:56:014609ブラウズ

この記事は、Java の最終的な実装原理を詳細に分析したものです (例を示します)。必要な方は参考にしていただければ幸いです。あなたに。

Final は、メンバー変数、メソッド、クラス、ローカル変数を宣言できる Java の予約キーワードです。

参照宣言を最終的にすると、その参照を変更できなくなります。変数を再度初期化しようとすると、コンパイラはコンパイル エラーを報告します。

1. 最終変数

最終メンバー変数は定数を表し、値は代入後に変更されません。アドレスが必要です。値は変更できません)

final が基本データ型を変更する場合、final が参照型を変更すると、基本データ型の値は一度初期化されると変更できないことを意味します。初期化されると、他のオブジェクトを指すことはできなくなりますが、参照が指すオブジェクトの内容は変更される可能性があります。参照される値はアドレスであり、final はその値、つまりアドレスの値が変更されないことを要求するため、本質的には同じことです。

Final はメンバー変数 (プロパティ) を変更するため、明示的に初期化する必要があります。初期化方法は 2 つあり、1 つは変数の宣言時に初期化する方法で、2 つ目は変数の宣言時に初期値を代入せず、その変数が存在するクラスのすべてのコンストラクターで初期値を変数に代入する方法です。 。 初期値。

2. 最後の方法

最後の方法を使用する理由は 2 つあります。

最初の理由は、継承されたクラスがその意味を変更してオーバーライドできないようにするためにメソッドをロックするためです。

2 番目の理由は、final メソッドよりも効率が高いことです。コンパイル中に静的にバインドされ、実行時に動的にバインドする必要がないため、高速です。

(注: クラスのプライベート メソッドは暗黙的に最終メソッドとして指定されます)

3. 最終クラス

クラスがfinalで変更された場合、このクラスは継承できないことを示します。

最終クラスのメンバー変数は、必要に応じて Final に設定できますが、最終クラスのすべてのメンバー メソッドが暗黙的に Final メソッドとして指定されることに注意してください。

final を使用してクラスを変更する場合は、このクラスが将来継承に実際に使用されない場合やセキュリティ上の理由がない限り、クラスを Final クラスとして設計しないように注意してください。

4.final の使用の概要

final キーワードの利点:

(1) 最後のキーワードによりパフォーマンスが向上します。 JVM と Java アプリケーションはどちらも最終変数をキャッシュします。

(2) 最終変数は、同期オーバーヘッドを追加することなく、マルチスレッド環境で安全に共有できます。

(3) Final キーワードを使用すると、JVM はメソッド、変数、クラスを最適化します。

final に関する重要な知識ポイント

1.final キーワードは、メンバー変数、ローカル変数、メソッド、およびクラスに使用できます。

2. 最終メンバー変数は、コンストラクターで宣言または初期化するときに初期化する必要があります。そうしないと、コンパイル エラーが報告されます。

3. 最終変数に値を再度代入することはできません。

4. ローカル変数には、宣言時に値を割り当てる必要があります。

5. 匿名クラス内のすべての変数は、final 変数でなければなりません。

6. 最後のメソッドはオーバーライドできません。

7. 最終クラスは継承できません。

8.final キーワードは、例外処理に使用されるfinally キーワードとは異なります。

9. Final キーワードは、finalize() メソッドと混同されやすいですが、後者は Object クラスで定義されたメソッドであり、ガベージ コレクションの前に JVM によって呼び出されます。

10. インターフェイスで宣言されたすべての変数は最終的なものです。

11. Final と Abstract の 2 つのキーワードは逆相関であるため、final クラスを抽象クラスにすることはできません。

12. 最後のメソッドはコンパイル段階でバインドされます。これは静的バインディングと呼ばれます。

13. 宣言時に初期化されない最終変数は、空の最終変数と呼ばれます。コンストラクター内で初期化するか、this() を呼び出して初期化する必要があります。これを行わないと、コンパイラは「最終変数 (変数名) を初期化する必要があります」というエラーを報告します。

14. クラス、メソッド、変数を Final として宣言すると、JVM が推定して最適化できるようになるため、パフォーマンスが向上します。

15. Java コードの規則によれば、final 変数は定数であり、通常は定数名を大文字にする必要があります。

16. コレクション オブジェクトを Final として宣言すると、参照は変更できませんが、内容を追加、削除、変更することはできます。

5. 最後の原則

最初に Java メモリ モデルを理解することが最善です Java 同時実行性 (2): Java メモリ モデル

最後のドメイン、コンパイラとプロセッサについては、

1. コンストラクターで最終フィールドを書き込み、その後構築されたオブジェクトへの参照を参照変数に割り当てることは、これら 2 つの操作の間で並べ替えることはできません。

(最初に最後の変数を書き込んでから、オブジェクト参照を呼び出します)

理由: コンパイラは、最後のフィールドを書き込んだ後に StoreStore バリアを挿入します

2. 最終フィールドを含むオブジェクトへの参照の最初の読み取りと、その後の最終フィールドの最初の読み取りは、2 つの操作の間で順序を変更できません。

(最初にオブジェクトの参照を読み取り、次に最後の変数を読み取ります)

コンパイラは、最後のフィールドの読み取り操作の前に LoadLoad バリアを挿入します

例 1:

public class FinalExample {
    int i; // 普通变量
    final int j; // final 变量
    static FinalExample obj;
    public void FinalExample() { // 构造函数
        i = 1; // 写普通域
        j = 2; // 写 final 域
    }
    public static void writer() { // 写线程 A 执行
        obj = new FinalExample();
    }
    public static void reader() { // 读线程 B 执行
        FinalExample object = obj; // 读对象引用
        int a = object.i; // 读普通域         a=1或者a=0或者直接报错i没有初始化
        int b = object.j; // 读 final域      b=2
    }
}

最初のケース: 通常のフィールドの書き込み操作は、コンストラクターの外側でコンパイラによって並べ替えられます

そして、書き込み操作最終フィールドは、final として記述されます。 フィールドの並べ替えルールはコンストラクター内で「制限され」、リーダー スレッド B は、初期化後に、final 変数の値を正しく読み取ります。

最終フィールドの並べ替えルールを記述すると、オブジェクト参照がスレッドに表示される前に、オブジェクトの最終フィールドが正しく初期化されていることを保証できますが、通常のフィールドにはこの保証がありません。

#2 番目のケース: オブジェクトの通常のフィールドを読み取る操作は、オブジェクト参照を読み取る前に読み取るようにプロセッサによって並べ替えられます。 And read 最終フィールドの並べ替えルールにより、オブジェクト参照の読み取り後にオブジェクトの最終フィールドを読み取る操作が「制限」されます。この時点では、最終フィールドは A スレッドによって初期化されており、これは正しいことです。読み取り操作。

最終フィールドを読み取るための並べ替えルールにより、オブジェクトの最終フィールドを読み取る前に、最終フィールドを含むオブジェクトへの参照が最初に読み取られる必要があります。

例 2: 最後のフィールドが参照型の場合

参照型の場合は、最終フィールド 並べ替えルールにより、コンパイラとプロセッサに次の制約が追加されます。

コンストラクター内で最終参照オブジェクトのメンバー フィールドを書き込み、その後、コンストラクターの外で構築されたオブジェクトへの参照を書き込みます。 参照への代入これら 2 つの操作の間で変数の順序を変更することはできません。

public class FinalReferenceExample {
    final int[] intArray; // final 是引用类型
    static FinalReferenceExample obj;
    public FinalReferenceExample() { // 构造函数
        intArray = new int[1]; // 1
        intArray[0] = 1; // 2
    }
    public static void writerOne() { // 写线程 A 执行
        obj = new FinalReferenceExample(); // 3
    }
    public static void writerTwo() { // 写线程 B 执行
        obj.intArray[0] = 2; // 4
    }
    public static void reader() { // 读线程 C 执行
        if (obj != null) { // 5
            int temp1 = obj.intArray[0]; // 6  temp1=1或者temp1=2,不可能等于0
        }
    }
}

最初のスレッド A が WriterOne() メソッドを実行し、実行後にスレッド B が WriterTwo() メソッドを実行し、実行後にスレッド C が Reader() メソッドを実行するとします。

上の図では、1 は最終フィールドに書き込み、2 はこの最終フィールドによって参照されるオブジェクトのメンバー フィールドに書き込みます。 . , 3 は、構築されたオブジェクトの参照を参照変数に代入します。前述の 1 と 3 を並べ替えることはできないことに加え、2 と 3 も並べ替えることはできません。

JMM は、読み取りスレッド C が、コンストラクター内の書き込みスレッド A による最終参照オブジェクトのメンバー フィールドの書き込みを少なくとも確認できることを保証できます。つまり、C は少なくとも配列インデックス 0 の値が 1 であることを確認できます。書き込みスレッド B による配列要素の書き込みは、読み取りスレッド C から見える場合と見えない場合があります。 JMM は、書き込みスレッド B と読み取りスレッド C の間でデータ競合があり、この時点での実行結果は予測できないため、スレッド B の書き込みが読み取りスレッド C に見えることを保証しません。

以上がJava での最終実装原則の詳細な分析 (例付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。