Maison  >  Article  >  Java  >  Analyse approfondie des principes finaux d'implémentation en Java (avec exemples)

Analyse approfondie des principes finaux d'implémentation en Java (avec exemples)

不言
不言avant
2018-11-27 16:56:014609parcourir

Ce que cet article vous apporte, c'est une analyse approfondie des principes d'implémentation finale en Java (avec des exemples). J'espère qu'elle a une certaine valeur de référence. cela vous sera utile.

final est un mot-clé réservé en Java qui peut déclarer des variables membres, des méthodes, des classes et des variables locales.

Une fois la déclaration de référence définitive, vous ne pourrez plus modifier la référence. Le compilateur vérifiera le code. Si vous essayez d'initialiser à nouveau la variable, le compilateur signalera une erreur de compilation.

1. Variables finales

Les variables membres finales représentent des constantes et ne peuvent être attribuées qu'une seule fois. La valeur ne changera pas après l'affectation (finale. nécessite une adresse La valeur ne peut pas être modifiée)

Lorsque final modifie un type de données de base, cela signifie que la valeur du type de données de base ne peut pas changer une fois initialisé si final modifie un type de référence, cela signifie que la valeur du type de données de base ne peut pas être modifiée après son initialisation. Il ne peut plus pointer vers d'autres objets, mais le contenu de l'objet pointé par la référence peut changer. Essentiellement, c'est la même chose, car la valeur référencée est une adresse, et final exige que la valeur, c'est-à-dire la valeur de l'adresse, ne change pas.

final modifie une variable membre (attribut) et doit être initialisé explicitement. Il existe deux méthodes d'initialisation. L'une consiste à initialiser la variable lorsqu'elle est déclarée. La deuxième méthode consiste à ne pas attribuer de valeur initiale lors de la déclaration de la variable, mais à l'attribuer à la variable dans tous les constructeurs de la classe où se trouve la variable. .valeur initiale.

2. Méthode finale

Il y a deux raisons d'utiliser la méthode finale.

La première raison est de verrouiller la méthode pour empêcher toute classe héritée de modifier sa signification et ne peut pas être remplacée ;

La deuxième raison est que les méthodes finales sont plus rapides que les méthodes non finales. Rapide, car il est lié statiquement lors de la compilation et n'a pas besoin d'être lié dynamiquement au moment de l'exécution.

(Remarque : les méthodes privées d'une classe seront implicitement désignées comme méthodes finales)

Classe finale

Lorsqu'une classe est modifiée avec final, cela indique que cette classe ne peut pas être héritée.

Les variables membres de la classe finale peuvent être définies sur final selon les besoins, mais sachez que toutes les méthodes membres de la classe finale seront implicitement désignées comme méthodes finales.

Lorsque vous utilisez final pour modifier une classe, vous devez choisir avec soin, à moins que cette classe ne soit vraiment pas utilisée pour l'héritage à l'avenir ou pour des raisons de sécurité, essayez de ne pas concevoir la classe comme une classe finale.

4. Récapitulatif de l'utilisation du mot-clé final :

(1) Le mot-clé final améliore les performances. Les applications JVM et Java mettent en cache les variables finales. (2) Les variables finales peuvent être partagées en toute sécurité dans un environnement multithread sans surcharge de synchronisation supplémentaire. (3) En utilisant le mot-clé final, la JVM optimisera les méthodes, les variables et les classes.

Points de connaissance importants sur final

1 Le mot-clé final peut être utilisé pour les variables membres, les variables locales, les méthodes et les classes.

2. Les variables membres finales doivent être initialisées lorsqu'elles sont déclarées ou initialisées dans le constructeur, sinon une erreur de compilation sera signalée. 3. Vous ne pouvez plus attribuer une valeur à une variable finale.

4. Les variables locales doivent se voir attribuer une valeur lorsqu'elles sont déclarées.

5. Toutes les variables des classes anonymes doivent être des variables finales.

6. La méthode finale ne peut pas être remplacée.

7. Les classes finales ne peuvent pas être héritées.

8. Le mot-clé final est différent du mot-clé final, qui est utilisé pour la gestion des exceptions.

9. Le mot-clé final se confond facilement avec la méthode finalize(). Cette dernière est une méthode définie dans la classe Object et est appelée par la JVM avant le garbage collection.

10. Toutes les variables déclarées dans l'interface sont définitives.

11. Les deux mots-clés final et abstract sont anti-corrélés, et la classe finale ne peut pas être abstraite.

12. La méthode finale est liée pendant la phase de compilation, appelée liaison statique.

13. Les variables finales qui ne sont pas initialisées lorsqu'elles sont déclarées sont appelées variables finales vides. Elles doivent être initialisées dans le constructeur ou initialisées en appelant this(). Si vous ne le faites pas, le compilateur signalera une erreur « la variable finale (nom de la variable) doit être initialisée ».

14. Déclarer les classes, méthodes et variables comme finales peut améliorer les performances, afin que la JVM ait la possibilité d'estimer puis d'optimiser.

15. Selon les conventions de codage Java, les variables finales sont des constantes et les noms de constantes doivent généralement être en majuscules.

16. Déclarer un objet de collection comme final signifie que la référence ne peut pas être modifiée, mais que vous pouvez y ajouter, supprimer ou modifier du contenu.

5. Principe final

Il est préférable de d'abord comprendre le modèle de mémoire Java Concurrence Java (2) : Modèle de mémoire Java

Pour le domaine final, le compilateur et le processeur doit respecter deux règles de réorganisation :

1. L'écriture d'un champ final dans le constructeur et l'attribution ultérieure de la référence de l'objet construit à une variable de référence ne peuvent pas être réordonnées entre ces deux opérations.

(écrivez d'abord la variable finale, puis appelez la référence de l'objet)

Raison : Le compilateur insérera une barrière StoreStore après avoir écrit le champ final

2. La première lecture d'une référence à un objet contenant un champ final et la première lecture ultérieure de ce champ final ne peuvent pas être réordonnées entre ces deux opérations.

(lisez d'abord la référence de l'objet, puis lisez la variable finale)

Le compilateur insérera une barrière LoadLoad devant l'opération de lecture du champ final

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

Premier cas : l'opération d'écriture du champ ordinaire est réordonnée par le compilateur en dehors du constructeur

L'opération d'écriture du champ ordinaire Le champ final est réorganisé par le compilateur. Les règles de réorganisation pour l'écriture des champs finaux sont "limitées" dans le constructeur, et le thread de lecture B lit correctement la valeur de la variable finale après l'initialisation.

L'écriture de règles de réorganisation pour les champs finaux peut garantir que les champs finaux de l'objet ont été correctement initialisés avant que la référence de l'objet ne soit visible par n'importe quel thread, alors que les champs ordinaires n'ont pas cette garantie.

Le deuxième cas : l'opération de lecture du champ ordinaire de l'objet est réordonnée par le processeur avant la lecture de la référence de l'objet

pendant la lecture Les règles de réorganisation du champ final "limiteront" l'opération de lecture du champ final de l'objet à après la lecture de la référence de l'objet. A ce moment, le champ final a été initialisé par le thread A, ce qui est une opération de lecture correcte. .

Les règles de réorganisation pour la lecture des champs finaux garantissent qu'avant de lire le champ final d'un objet, la référence à l'objet contenant le champ final doit être lue en premier.

Exemple 2 : Si le champ final est un type référence

Pour les types référence, écrivez le champ final Les règles de réorganisation ajoutent les contraintes suivantes aux compilateurs et aux processeurs :

Écriture dans les champs membres d'un objet référencé final au sein du constructeur, puis écriture de la référence à l'objet construit en dehors du constructeur Affectation à une référence la variable ne peut pas être réorganisée entre les deux opérations.

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

Supposons que le premier thread A exécute la méthodewriterOne(), après l'exécution, le thread B exécute la méthodewriterTwo() et après l'exécution, le thread C exécute la méthode reader().

Dans la figure ci-dessus, 1 écrit dans le champ final, 2 écrit dans le champ membre de l'objet référencé par ce champ final, 3 consiste à affecter la référence de l'objet construit à une variable de référence. En plus de ce qui précède, le 1 ne peut pas être commandé à nouveau avec le 3, le 2 et le 3 ne peuvent pas non plus être commandés à nouveau.

JMM peut garantir que le thread de lecture C peut au moins voir l'écriture du champ membre de l'objet de référence final par le thread d'écriture A dans le constructeur. Autrement dit, C peut au moins voir que la valeur de l’index du tableau 0 est 1. L'écriture des éléments du tableau par le thread d'écriture B peut être visible ou non par le thread de lecture C. JMM ne garantit pas que l'écriture du thread B soit visible pour le thread de lecture C, car il existe une concurrence de données entre le thread d'écriture B et le thread de lecture C, et les résultats d'exécution à ce moment sont imprévisibles.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer