Heim >Java >javaLernprogramm >Eingehende Analyse der endgültigen Implementierungsprinzipien in Java (mit Beispielen)

Eingehende Analyse der endgültigen Implementierungsprinzipien in Java (mit Beispielen)

不言
不言nach vorne
2018-11-27 16:56:014730Durchsuche

Dieser Artikel bietet Ihnen eine ausführliche Analyse der Prinzipien der endgültigen Implementierung in Java (mit Beispielen). Ich hoffe, dass er einen gewissen Referenzwert hat Es wird Ihnen nützlich sein.

final ist ein reserviertes Schlüsselwort in Java, das Mitgliedsvariablen, Methoden, Klassen und lokale Variablen deklarieren kann.

Sobald Sie die Referenzdeklaration abgeschlossen haben, können Sie die Referenz nicht mehr ändern. Wenn Sie versuchen, die Variable erneut zu initialisieren, meldet der Compiler einen Kompilierungsfehler.

1. Endgültige Variablen

Endgültige Mitgliedsvariablen stellen Konstanten dar und können nur einmal zugewiesen werden. Der Wert ändert sich nach der Zuweisung nicht (endgültig). erfordert eine Adresse. Der Wert kann nicht geändert werden.

Wenn final einen Basisdatentyp ändert, bedeutet dies, dass sich der Wert des Basisdatentyps nach der Initialisierung nicht ändern kann Der Wert des Basisdatentyps kann nach der Initialisierung nicht mehr geändert werden. Er kann nicht mehr auf andere Objekte verweisen, aber der Inhalt des Objekts, auf das die Referenz zeigt, kann sich ändern. Im Wesentlichen ist es dasselbe, da der referenzierte Wert eine Adresse ist und final erfordert, dass sich der Wert, also der Wert der Adresse, nicht ändert.

final ändert eine Mitgliedsvariable (Attribut) und muss explizit initialisiert werden. Es gibt zwei Initialisierungsmethoden: Die eine besteht darin, die Variable bei der Deklaration zu initialisieren. Die zweite Methode besteht darin, bei der Deklaration der Variablen keinen Anfangswert zuzuweisen, sondern ihn der Variablen in allen Konstruktoren der Klasse zuzuweisen, in der sich die Variable befindet . Anfangswert.

2. Endgültige Methode

Es gibt zwei Gründe für die Verwendung der endgültigen Methode.

Der erste Grund besteht darin, die Methode zu sperren, um zu verhindern, dass eine geerbte Klasse ihre Bedeutung ändert und nicht überschrieben werden kann.

Der zweite Grund ist die Effizienz. Schnell, da es während der Kompilierung statisch gebunden wird und zur Laufzeit nicht dynamisch gebunden werden muss.

(Hinweis: Private Methoden einer Klasse werden implizit als endgültige Methoden bezeichnet)

3. Letzte Klasse

Wenn eine Klasse mit final geändert wird, bedeutet dies, dass diese Klasse nicht vererbt werden kann.

Member-Variablen in der finalen Klasse können bei Bedarf auf final gesetzt werden. Beachten Sie jedoch, dass alle Member-Methoden in der finalen Klasse implizit als finale Methoden bezeichnet werden.

Wenn Sie final zum Ändern einer Klasse verwenden, sollten Sie sorgfältig auswählen, es sei denn, diese Klasse wird in Zukunft wirklich nicht mehr zur Vererbung verwendet oder aus Sicherheitsgründen. Versuchen Sie, die Klasse nicht als finale Klasse zu entwerfen.

4. Zusammenfassung der Verwendung von final

Vorteile des finalen Schlüsselworts:

(1) Das letzte Schlüsselwort verbessert die Leistung. Sowohl JVM- als auch Java-Anwendungen speichern endgültige Variablen im Cache.

(2) Endgültige Variablen können in einer Multithread-Umgebung ohne zusätzlichen Synchronisierungsaufwand sicher gemeinsam genutzt werden.

(3) Mit dem letzten Schlüsselwort optimiert die JVM Methoden, Variablen und Klassen.

Wichtige Wissenspunkte zu final

1 Das Schlüsselwort final kann für Mitgliedsvariablen, lokale Variablen, Methoden und Klassen verwendet werden.

2. Letzte Mitgliedsvariablen müssen initialisiert werden, wenn sie im Konstruktor deklariert oder initialisiert werden, andernfalls wird ein Kompilierungsfehler gemeldet.

3. Sie können einer endgültigen Variablen keinen erneuten Wert zuweisen.

4. Lokalen Variablen muss bei der Deklaration ein Wert zugewiesen werden.

5. Alle Variablen in anonymen Klassen müssen endgültige Variablen sein.

6. Die letzte Methode kann nicht überschrieben werden.

7. Abschlussklassen können nicht vererbt werden.

8. Das Schlüsselwort „final“ unterscheidet sich vom Schlüsselwort „final“, das für die Ausnahmebehandlung verwendet wird.

9. Das Schlüsselwort final kann leicht mit der Methode finalize() verwechselt werden. Letztere ist eine Methode, die in der Object-Klasse definiert ist und von der JVM vor der Garbage Collection aufgerufen wird.

10. Alle in der Schnittstelle deklarierten Variablen sind endgültig.

11. Die beiden Schlüsselwörter final und abstract sind antikorreliert und die finale Klasse kann nicht abstrakt sein.

12. Die endgültige Methode wird während der Kompilierungsphase gebunden, was als statische Bindung bezeichnet wird.

13. Endgültige Variablen, die bei der Deklaration nicht initialisiert werden, werden als leere endgültige Variablen bezeichnet. Sie müssen im Konstruktor initialisiert oder durch Aufruf von this() initialisiert werden. Wenn Sie dies nicht tun, meldet der Compiler die Fehlermeldung „Die letzte Variable (Variablenname) muss initialisiert werden“.

14. Durch die Deklaration von Klassen, Methoden und Variablen als endgültig kann die Leistung verbessert werden, sodass die JVM die Möglichkeit hat, Schätzungen vorzunehmen und diese dann zu optimieren.

15. Gemäß den Java-Codierungskonventionen sind Endvariablen Konstanten und Konstantennamen werden normalerweise großgeschrieben.

16. Wenn Sie ein Sammlungsobjekt als endgültig deklarieren, bedeutet dies, dass die Referenz nicht geändert werden kann, Sie jedoch Inhalte hinzufügen, löschen oder ändern können.

5. Letztes Prinzip

Es ist am besten, zuerst das Java-Speichermodell Java-Parallelität (2) zu verstehen: Java-Speichermodell

Für die endgültige Domäne, den Compiler und den Prozessor müssen zwei Neuordnungsregeln einhalten:

1. Das Schreiben eines endgültigen Felds im Konstruktor und das anschließende Zuweisen der Referenz des konstruierten Objekts zu einer Referenzvariablen kann zwischen diesen beiden Vorgängen nicht neu angeordnet werden.

(schreiben Sie zuerst die letzte Variable und rufen Sie dann die Objektreferenz auf)

Grund: Der Compiler fügt nach dem Schreiben des letzten Felds

eine StoreStore-Barriere ein

2. Das erste Lesen eines Verweises auf ein Objekt, das ein letztes Feld enthält, und das anschließende erste Lesen dieses letzten Feldes können zwischen diesen beiden Vorgängen nicht neu angeordnet werden.

(zuerst die Referenz des Objekts lesen, dann die letzte Variable lesen)

Der Compiler fügt eine LoadLoad-Barriere vor der Operation zum Lesen des letzten Felds ein

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

Der erste Fall: Der Vorgang des Schreibens des gewöhnlichen Felds wird vom Compiler außerhalb des Konstruktors neu angeordnet

Der Vorgang des Schreibens des Das endgültige Feld wird als „final“ geschrieben. Die Regeln für die Neuordnung von Feldern sind im Konstruktor „begrenzt“, und Leserthread B liest den Wert der endgültigen Variablen nach der Initialisierung korrekt.

Das Schreiben von Neuordnungsregeln für endgültige Felder kann sicherstellen, dass die endgültigen Felder des Objekts korrekt initialisiert wurden, bevor die Objektreferenz für jeden Thread sichtbar ist, während für gewöhnliche Felder diese Garantie nicht gilt.

Der zweite Fall: Der Vorgang des Lesens des gewöhnlichen Felds des Objekts wird vom Prozessor neu angeordnet, bevor die Objektreferenz gelesen wird

beim Lesen Die Neuordnungsregeln des letzten Felds „beschränken“ den Vorgang des Lesens des letzten Felds des Objekts auf nach dem Lesen der Objektreferenz. Zu diesem Zeitpunkt wurde das letzte Feld vom A-Thread initialisiert, was ein korrekter Lesevorgang ist .

Die Neuordnungsregeln für das Lesen von Endfeldern stellen sicher, dass vor dem Lesen des Endfelds eines Objekts zuerst der Verweis auf das Objekt gelesen werden muss, das das Endfeld enthält.

Beispiel 2: Wenn das letzte Feld ein Referenztyp ist

Für Referenztypen schreiben Sie das letztes Feld Die Neuordnungsregeln fügen Compilern und Prozessoren die folgenden Einschränkungen hinzu:

Schreiben in die Mitgliedsfelder eines endgültigen referenzierten Objekts innerhalb des Konstruktors und anschließendes Schreiben der Referenz auf das konstruierte Objekt außerhalb des Konstruktors. Zuweisung zu einer Referenz Die Variable kann zwischen diesen beiden Vorgängen nicht neu angeordnet werden.

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

Angenommen, der erste Thread A führt die Methode writeOne() aus, nach der Ausführung führt Thread B die Methode writeTwo() aus und nach der Ausführung führt Thread C die Methode read() aus.

In der obigen Abbildung schreibt 1 in das letzte Feld, 2 schreibt in das Mitgliedsfeld des Objekts, auf das dieses letzte Feld verweist. 3 besteht darin, die Referenz des konstruierten Objekts einer Referenzvariablen zuzuweisen. Darüber hinaus können 1 nicht nachbestellt werden, 3, 2 und 3 auch nicht nachbestellt werden.

JMM kann sicherstellen, dass der Lesethread C zumindest das Schreiben des Mitgliedsfelds des endgültigen Referenzobjekts durch den Schreibthread A im Konstruktor sehen kann. Das heißt, C kann zumindest erkennen, dass der Wert des Array-Index 0 1 ist. Das Schreiben von Array-Elementen durch den schreibenden Thread B kann für den lesenden Thread C sichtbar sein oder auch nicht. JMM garantiert nicht, dass das Schreiben von Thread B für den Lesethread C sichtbar ist, da zwischen dem Schreibthread B und dem Lesethread C ein Datenwettbewerb besteht und die Ausführungsergebnisse zu diesem Zeitpunkt unvorhersehbar sind.

Das obige ist der detaillierte Inhalt vonEingehende Analyse der endgültigen Implementierungsprinzipien in Java (mit Beispielen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen