Préface
En Java, un objet doit être correctement initialisé avant de pouvoir être utilisé, qui est stipulé par la spécification Java. Récemment, j'ai découvert une question intéressante, et la réponse à cette question m'a mené les yeux à première vue. Jetez un œil à ces trois catégories:
package com.ds.test; public class upper {String upperstring; public upper () {initializer.Initialize (this); }} package com.ds.test; public class inférieur étend supérieur {String LowerString = null; public inférieur () {super (); System.out.println ("Upper:" + Upperstring); System.out.println ("inférieur:" + LowerString); } public static void main (final string [] args) {new inférieur (); }} package com.ds.test; public class inimitizer {static void initialize (final supérieur anupper) {if (instance anupperof inférieur) {inférieur inférieur = (inférieur) anupper; Lower.Lowerstring = "LowerInited"; } anupper.upperString = "usinenited"; }} Quelle sortie peut être obtenue en exécutant la classe Lower ? Dans cet exemple minimal, toute la situation peut être vue plus facilement, mais cette situation se produit dans la réalité et il y aura beaucoup de code distrayant une personne.
Quoi qu'il en soit, la sortie est comme ceci:
Supérieur: UpperInitedlower: NULL;
Bien que le type String soit utilisé dans le petit exemple, le code réel de la classe Initializer a un objet délégué pour l'enregistrement, qui est le même que Lower - au moins Lower est l'intention. Mais pour une raison quelconque, cela ne fonctionne pas lors de l'exécution de l'application. Au lieu de cela, le chemin par défaut est utilisé et l'objet délégué n'est pas défini (null).
Changez maintenant un peu le code de Lower :
package com.ds.test; public class inférieur étend supérieur {String LowerString; public inférieur () {super (); System.out.println ("Upper:" + Upperstring); System.out.println ("inférieur:" + LowerString); } public static void main (final string [] args) {new inférieur (); }}La sortie ressemble maintenant à ceci:
Supérieur: UpperInitedlower: inférieur
Avez-vous trouvé la différence dans le code?
Oui, ce champ lowerString n'est plus explicitement défini sur vide. Pourquoi cela le rend-il différent? Quoi qu'il en soit, la valeur par défaut du champ de type de référence n'est- String pas vide? Bien sûr, c'est vide. Il s'avère que si ce léger changement ne change évidemment pas le comportement du code, cela rend le résultat différent.
Alors, que s'est-il passé exactement? Tout devient clair lors de la vérification de l'ordre d'initialisation:
1. main() appelle le constructeur Lower .
2. Une instance de Lower est prête. Cela signifie que tous les champs sont créés et remplis de valeurs par défaut, par exemple, la valeur par défaut du type de référence est vide et la valeur par défaut du type booléen est false . À l'heure actuelle, aucune affectation en ligne au champ ne se produit.
3. Le constructeur de classe parent est appelé. Ceci est appliqué par les caractéristiques de la langue. Donc, avant que toute autre chose ne se produise, le constructeur de Upper est appelé.
4.Upper ce constructeur exécute et spécifie une référence à l'instance nouvellement créée de Initializer.initialize() .
5. Initializer attache une nouvelle chaîne à deux champs ( upperString et lowerString ). Attribuer des valeurs à ces deux champs en utilisant une instanceof de vérification des instances un peu sale n'est pas un modèle de conception particulièrement bon, mais cela fonctionne également, ne vous inquiétez pas beaucoup. Une fois que cela se produit, les références à upperString et lowerString ne sont plus vides.
6. L'appel à Initializer.initialize() est terminé et Upper est également terminé.
7. Maintenant, cela devient intéressant: la construction de Lower continue. En supposant que l'affectation =null n'est pas explicitement spécifiée dans la déclaration de champ lowerString , le constructeur Lower reprend l'exécution et imprime deux chaînes connectées au champ.
Cependant, s'il existe une opération qui attribue explicitement NULL, le processus d'exécution sera légèrement différent: lorsque le constructeur de la classe parent est terminé, toute initialisation variable sera effectuée avant le reste des constructeurs (voir la section 12.5 de la spécification du langage Java). Dans ce cas, la référence de chaîne attribuée à lowerString précédemment n'est plus attribuée à NULL. Continuez ensuite à exécuter la construction de la fonction restante, et maintenant imprimer la valeur de lowerString comme: null.
Ceci est un excellent exemple, non seulement pour faciliter notre attention sur certains détails sur la création d'objets (ou pour savoir où vérifier les spécifications de codage Java, imprimées ou en ligne), mais aussi pour montrer pourquoi l'écriture d'initialisations comme celle-ci est mauvaise. Nous ne devrions pas du tout nous soucier de la sous-classe de Upper. Inversement, si pour une raison quelconque, l'initialisation de certains champs ne peut pas être effectuée dans la sous-classe elle-même, elle ne nécessitera qu'une variante de sa propre classe d'assistance d'initialisation. Dans ce cas, si vous utilisez String lowString ou String lowerString = null il n'y a vraiment aucune différence, et ce qu'il devrait être.
Résumer
Ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article sera d'une aide à l'étude ou au travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer.