Modification des champs finaux privés par reflection

class WithPrivateFinalField { private final Ssortingng s = "I'm totally safe"; public Ssortingng toSsortingng() { return "s = " + s; } } WithPrivateFinalField pf = new WithPrivateFinalField(); System.out.println(pf); Field f = pf.getClass().getDeclaredField("s"); f.setAccessible(true); System.out.println("f.get(pf): " + f.get(pf)); f.set(pf, "No, you're not!"); System.out.println(pf); System.out.println(f.get(pf)); 

Sortie:

 s = I'm totally safe f.get(pf): I'm totally safe s = I'm totally safe No, you're not! 

Pourquoi cela fonctionne-t-il de cette façon, pouvez-vous expliquer s’il vous plaît? La première impression nous indique que le champ “s” privé n’a pas été modifié, comme prévu. Mais si nous obtenons le champ par reflection, le deuxième tirage indique qu’il est mis à jour.

Cette réponse est plus qu’exhaustive sur le sujet.

JLS 17.5.3 Modification ultérieure des champs finaux

Même alors, il y a un certain nombre de complications. Si un champ final est initialisé à une constante de compilation dans la déclaration de champ, les modifications apscopes au champ final peuvent ne pas être observées, car les utilisations de ce champ final sont remplacées au moment de la compilation par la constante de compilation.

Mais si vous lisez attentivement le paragraphe ci-dessus, vous pouvez trouver un moyen de contourner ce problème (définissez le champ private final dans le constructeur plutôt que dans la définition du champ):

 import java.lang.reflect.Field; public class Test { public static void main(Ssortingng[] args) throws Exception { WithPrivateFinalField pf = new WithPrivateFinalField(); System.out.println(pf); Field f = pf.getClass().getDeclaredField("s"); f.setAccessible(true); System.out.println("f.get(pf): " + f.get(pf)); f.set(pf, "No, you're not!"); System.out.println(pf); System.out.println("f.get(pf): " + f.get(pf)); } private class WithPrivateFinalField { private final Ssortingng s; public WithPrivateFinalField() { this.s = "I'm totally safe"; } public Ssortingng toSsortingng() { return "s = " + s; } } } 

La sortie est alors la suivante:

 s = I'm totally safe f.get(pf): I'm totally safe s = No, you're not! f.get(pf): No, you're not! 

J’espère que ça aide un peu.

Ce

 class WithPrivateFinalField { private final Ssortingng s = "I'm totally safe"; public Ssortingng toSsortingng() { return "s = " + s; } } 

comstack réellement comme ceci:

 class WithPrivateFinalField { private final Ssortingng s = "I'm totally safe"; public Ssortingng toSsortingng() { return "s = I'm totally safe"; } } 

C’est -à-dire que les constantes à la compilation sont intégrées. Voir cette question. Le moyen le plus simple d’éviter l’inclusion est de déclarer la Ssortingng comme ceci:

 private final Ssortingng s = "I'm totally safe".intern(); 

Pour les autres types, un appel de méthode sortingvial fait l’affaire:

 private final int integerConstant = identity(42); private static int identity(int number) { return number; } 

Voici un décomstack du fichier de classe WithPrivateFinalField (je l’ai mis dans une classe séparée pour plus de simplicité):

  WithPrivateFinalField(); 0 aload_0 [this] 1 invokespecial java.lang.Object() [13] 4 aload_0 [this] 5 ldc  [8] 7 putfield WithPrivateFinalField.s : java.lang.Ssortingng [15] 10 return Line numbers: [pc: 0, line: 2] [pc: 4, line: 3] [pc: 10, line: 2] Local variable table: [pc: 0, pc: 11] local: this index: 0 type: WithPrivateFinalField // Method descriptor #22 ()Ljava/lang/Ssortingng; // Stack: 1, Locals: 1 public java.lang.Ssortingng toSsortingng(); 0 ldc  [23] 2 areturn Line numbers: [pc: 0, line: 6] Local variable table: [pc: 0, pc: 3] local: this index: 0 type: WithPrivateFinalField 

Notez que dans la toSsortingng() , la constante utilisée à l’adresse 0 [ 0 ldc [23] ] indique que le compilateur a déjà concaténé le littéral de chaîne "s = " et le champ final privé " I'm totally safe" ensemble à l’avance et stocké. La méthode toSsortingng () renverra toujours "s = I'm totally safe" quelle que soit la modification de la variable d’instance.

Étant final , le compilateur s’attendait à ce que la valeur ne change pas, donc il a probablement codé la chaîne directement dans votre méthode toSsortingng .