2010-12-15 8 views
9

DZone refcard intitulé "Core Java Concurrency" déclare:Java: gel de champ final sur l'objet accessible à partir de champs final

Une fois défini, les valeurs de champ finale ne peut pas être changé. Marquer un champ de référence d'objet comme final ne pas empêcher les objets référencés de ce champ de changer plus tard. Pour l'exemple , un champ ArrayList final ne peut pas être modifié en un objet ArrayList différent, mais des objets peuvent être ajoutés ou supprimés dans l'instance de liste.

et

gel final sur le terrain ne comprend pas seulement les champs final de l'objet, mais aussi tous les objets accessible à partir de ces champs finaux.

Je ne suis pas entièrement clair sur la deuxième déclaration. Cela signifie-t-il que si j'ai un champ final dans la classe A de type B, qui possède à son tour un champ final de type Integer, alors le gel définitif du champ pour une instance de classe A se termine seulement après le gel final du champ b.c arrivé?

public class A{ 

    public final B b = new B(); 

} 

public class B{ 

    public final Integer c = 10; 

} 

Répondre

8

Est-ce que cela signifie que si j'ai un champ finale en classe A de type classe B, qui ont à leur tour un champ final de type entier, puis champ final gel pour une instance de la classe A complète seulement après le gel du champ final pour bc ont déjà eu lieu?

Je pense que je dirais bien que le gel final sur le terrain dans ce cas signifie que lorsque vous créez une instance de A et publier en toute sécurité, les autres objets ne verront jamais une valeur non initialisée pour b ou c.

Je voudrais aussi dire que lorsque vous créez l'instance de B dans A, un autre code d'initialisation dans A ne verra jamais une valeur non initialisée pour c.

Un cas où je l'ai rencontré de vraies questions autour de gel final sur le terrain est par exemple une classe qui contient un (mutable) HashMap, destiné uniquement pour la lecture, initialisé lors de la construction:

public class DaysOfWeek { 
    private final Map daysOfWeek = new HashMap(); 
    public DaysOfWeek() { 
     // prepopulate my map 
     daysOfWeek.put(0, "Sunday"); 
     daysOfWeek.put(1, "Monday"); 
     // etc 
    } 

    public String getDayName(int dayOfWeek) { 
     return daysOfWeek(dayOfWeek); 
    } 
} 

La question se pose ici: En supposant que cet objet soit publié en toute sécurité, et étant donné qu'il n'y a pas de synchronisation ici, est-il sûr que d'autres threads appellent getDayName()?La réponse est oui, car le gel final du champ garantit que la HashMap et tout ce qui est accessible (ici ce sont juste des chaînes, mais peuvent être des objets arbitrairement complexes) est gelé à la fin de la construction. [Si vous voulez réellement modifier cette carte après la construction, alors vous aurez besoin d'une synchronisation explicite autour des lectures et écritures.] Voici un lengthier blog explorant le sujet et vérifier les commentaires pour des réponses intéressantes par des gens comme Brian Goetz.

BTW Je suis l'auteur du refcard

+1

Merci Alex pour la réponse détaillée. Vraiment apprécié. BTW J'ai vraiment aimé la Refcard aussi. –

+1

>> ... le gel définitif des champs dans ce cas signifie que lorsque vous créez une instance de A et la publiez en toute sécurité, les autres objets ne verront jamais une valeur non initialisée pour b ou c << - non, Requaired ici. La seule exigence: 'ceci' ne doit pas échapper au constructeur – Male

+0

@Alex Miller "Final field freeze" est un terme officiel? – nish1013

1

Il n'est pas vraiment logique de parler de ce qui devient final avant quoi d'autre. Pour votre programme, une fois que votre objet est créé (en fait, à partir du moment où le champ est assigné une fois), la référence ne peut plus changer. Puisque l'instance B est créée avant l'instance A, vous pouvez dire que c devient final avant b, mais cela n'a pas vraiment d'importance.

Lorsque la commande est importante est lorsque vous avez plusieurs champs finaux dans une seule classe. Si vous souhaitez utiliser la valeur d'un champ final dans l'affectation d'un autre, vous devez uniquement accéder aux champs qui ont déjà été initialisés.

Pour être honnête, cette phrase de «gel de champ final» n'a pas beaucoup de sens pour moi.

4

Java Concurrency in Practice mentionne ceci dans l'article 16.3:

Initialisation garanties de sécurité qui pour correctement construits objets, toutes les discussions vont voir les corriger les valeurs des champs finaux qui ont été mis par le constructeur, quelle que soit de la façon dont l'objet est publié. En outre, les variables qui peuvent être atteint à travers un champ final d'un bien objet construit (tels que les éléments d'un tableau définitif ou la contenu d'une table de hachage référencé par un champ final) sont également garantis être visible aux autres fils. Pour les objets avec les champs finaux, l'initialisation interdit de réorganiser toute pièce de construction avec la charge initiale d'une référence à cet objet.Tous écrit aux champs finales du constructeur , ainsi que des variables accessibles par ces champs, devenir « gelé » lorsque le constructeur est terminé, et tout fil qui obtient une référence à cet objet est garanti à voir une valeur qui est au moins aussi à jour que la valeur gelée. Écrit que initialiser variables atteignables par les champs finaux ne sont pas réorganisés avec opérations suivant le gel post-construction .

+0

Qu'est-ce exactement auteur a voulu dire par « quelle que soit la façon dont l'objet est publié »? Je crois bien construit lui-même des moyens dont la référence n'échappent pas pendant la construction. – sjain

2

Droite. Cela découle de JMM

Rechercher paragraphe:

Un objet est considéré comme initialisé complètement lorsque ses finitions constructeur. Un thread qui ne peut voir une référence à un objet qu'une fois cet objet a été complètement initialisé est garanti de voir les valeurs initialisées correctement pour les champs finaux de cet objet.

Depuis constructeur ne sera pas terminé jusqu'à ce que la classe B que le gel de initialise garantie de b.c

2

La garantie est plus forte que vous semblez le penser. La sémantique finale du champ s'applique même aux objets mutables affectés aux champs finaux (avec les restrictions habituelles). SO étendre votre exemple pour rendre A.b privé et B mutable (mais pas mutable de l'extérieur).

public class A { 
    private final B b = new B(); 
    public Integer get() { return b.c; } 
} 

public class B { 
    public Integer c = 10; 
} 

Dans ce cas, A.get ne retourneront jamais null même dans la publication non sécuritaire. Bien sûr, cet exemple est complètement abstrait et donc vide de sens. Généralement, il est important pour les tableaux (par exemple dans String) et les collections.

+0

si vous mettez votre init final à la fin du constructeur et avez un mauvais code qui fuit cela du constructeur, alors vous pouvez obtenir null –

Questions connexes