Dans la programmation Java, un membre est modifié en utilisant le mot-clé privé. Seule la classe où se trouve ce membre et la méthode de cette classe peut être utilisée, et d'autres classes ne peuvent pas accéder à ce membre privé.
Ce qui précède décrit les fonctions de base du modificateur privé. Aujourd'hui, étudions la situation de l'échec de la fonction privée.
Classes internes Java
À Java, je crois que beaucoup de gens ont utilisé des classes internes. Java permet de définir une autre classe dans une classe. La classe de la classe est une classe interne, également appelée classe imbriquée. Une simple implémentation de classe interne peut être la suivante
class OUTERCLASS {class InnerClass {}}Le problème d'aujourd'hui est lié aux classes internes de Java et n'implique que certaines connaissances de classe interne liées aux recherches de cet article. Nous présenterons les articles suivants sur les classes internes Java.
La première fois qu'il a échoué?
Un scénario que nous utilisons souvent dans la programmation consiste à accéder aux variables de membres privés ou aux méthodes de classes externes dans une classe interne, ce qui est OK. Tel qu'implémenté dans le code suivant.
classe publique OuterClass {private String Language = "en"; Région de chaîne privée = "US"; classe publique InnerClass {public void printouterClassPrivateFields () {String Fields = "Language =" + Language + "; Region =" + Region; System.out.println (champs); }} public static void main (string [] args) {uterClass exter = new outerClass (); EterClass.innerClass Inner = outer.new innerclass (); inner.printouterClassPrivateFields (); }}Pourquoi est-ce? Un membre modifié privé n'est-il pas accessible uniquement par la classe décrite par le membre? Le privé est-il vraiment invalide?
Le compilateur se déconnecte?
Nous utilisons la commande javap pour afficher les deux fichiers générés
Résultats de la décompilation de laccasse extérieure
15:30 $ Javap -c OUTERCLASSSCOMPILET à partir de la classe publique "OUTERCLASS.JAVA" OUTERCLASS étend Java.lang.Object {public OuterClass (); CODE: 0: ALOAD_0 1: InvokeSpecial # 11; // Méthode java / lang / objet. "<Init>" :() v 4: aload_0 5: ldc # 13; // String en 7: putfield # 15; // Langue de champ: Ljava / Lang / String; 10: Aload_0 11: LDC # 17; // String Us 13: Putfield # 19; // Région de champ: Ljava / Lang / String; 16: RETOURPUBLIC STATIC VOID MAIN (Java.lang.String []); Code: 0: Nouveau # 1; // Classe OUTERCLASS 3: DUP 4: InvokeSpecial # 27; // Méthode "<Init>" :() V 7: Store_1 8: Nouveau # 28; // Classe OUTERCLASS $ innerclass 11: dup 12: Aload_1 13: dup 14: invokevirtual # 30; // Méthode java / lang / object.getClass :() ljava / lang / class; 17: Pop 18: InvocoSpecial # 34; // Méthode OUTERCLASS $ innerclass. "<Init>" :( LouterClass;) V 21: Store_2 22: aload_2 23: invokevirtual # 37; // Méthode OuterClass $ innerclass.printouterClassPrivatefields :() v 26: returnstatic java.lang.string accès 0 $ (OuterClass); Code: 0: ALOAD_0 1: GetField # 15; // Langue de champ: Ljava / Lang / String; 4: AreTurnstatic Java.lang.String Access 1 $ (OUTERCLASS); Code: 0: ALOAD_0 1: Getfield # 19; // Région de champ: Ljava / Lang / String; 4: Areturn}Hein? Non, nous ne définissons pas ces deux méthodes dans OUTERCLASS
Statique Java.lang.String Access 0 $ (OUTERCLASS); Code: 0: ALOAD_0 1: GetField # 15; // Langue de champ: Ljava / Lang / String; 4: AreTurnstatic Java.lang.String Access 1 $ (OUTERCLASS); Code: 0: ALOAD_0 1: Getfield # 19; // Région de champ: Ljava / Lang / String; 4: Areturn}
À en juger par les commentaires donnés, l'accès à 0 $ renvoie l'attribut de langue d'OUTERCLASS; Accès à 1 $ Renvoie l'attribut de la région d'OUTERCLASS. Et les deux méthodes acceptent une instance d'OUTERCLASS comme paramètre. Pourquoi ces deux méthodes sont-elles générées et quelles sont leurs fonctions? Examinons les résultats de décompilation de la classe interne.
Résultat de décompilation de la classe innercale extérieure
15:37 $ Javap -c OUTERCLASS / $ InnerClassCuled à partir de la classe publique "OUTERCLASS.JAVA" OUTERCLASS $ Innerclass étend Java.lang.Object {final OUTERCLASS ce 0 $; public OUTERCLASS $ Innerclass (OUTERCLASS); Code: 0: aload_0 1: aload_1 2: putfield # 10; // Field Ce 0 $: LouterClass; 5: ALOAD_0 6: InvokeSpecial # 12; // Méthode java / lang / objet. "<Init>" :() v 9: returnPublic void printouterClassPrivateFields (); Code: 0: Nouveau # 20; // Classe Java / Lang / StringBuilder 3: DUP 4: LDC # 22; // Langue de chaîne = 6: InvokeSpecial # 24; // Méthode java / lang / stringBuilder. "<Init>" :( ljava / lang / string;) v 9: aload_0 10: getField # 10; // Field Ce 0 $: LouterClass; 13: invokestatique # 27; // Méthode outerClass.Access 0 $: (LouterClass;) Ljava / Lang / String; 16: Invokevirtual # 33; // Méthode java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 19: LDC # 37; // String; Region = 21: invokeVirtual # 33; // Méthode java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 24: Aload_0 25: Getfield # 10; // Field Ce 0 $: LouterClass; 28: invokestatique # 39; // Méthode outerClass.Access 1 $: (LouterClass;) Ljava / Lang / String; 31: Invokevirtual # 33; // Méthode java / lang / stringBuilder.append: (ljava / lang / string;) ljava / lang / stringBuilder; 34: Invokevirtual # 42; // Méthode java / lang / stringbuilder.tostring :() ljava / lang / string; 37: Store_1 38: GetStatic # 46; // Field Java / Lang / System.out: ljava / io / printStream; 41: Aload_1 42: invokevirtual # 52; // Méthode java / io / printstream.println: (ljava / lang / string;) v 45: return}Le code suivant appelle le code d'accès à 0 $, dans le but d'obtenir la propriété privée linguistique d'OUTERCLASS.
13: invokestatique # 27; // Méthode outerClass.Access 0 $: (LouterClass;) Ljava / Lang / String;
Le code suivant appelle le code d'accès à 1 $, dans le but d'obtenir la propriété privée de la région d'OUTERCLASS.
28: invokestatique # 39; // Méthode outerClass.Access 1 $: (LouterClass;) Ljava / Lang / String;
Remarque: Lors de la construction d'une classe intérieure, la référence à la classe extérieure sera transmise et utilisée comme propriété de la classe intérieure, de sorte que la classe interne conservera une référence à sa classe extérieure.
Ce 0 $ est la référence de classe externe détenue par la classe intérieure, qui passe la référence et attribue la valeur via le constructeur.
Final OUTERCLASS Ce 0 $; public OUTERCLASS $ Innerclass (OUTERCLASS); Code: 0: aload_0 1: aload_1 2: putfield # 10; // Field Ce 0 $: LouterClass; 5: ALOAD_0 6: InvokeSpecial # 12; // Méthode java / lang / objet. "<Init>" :() v 9: retour
résumé
Cette partie du privé semble invalide, mais elle n'est pas invalide, car lorsque la classe intérieure appelle les propriétés privées de la classe extérieure, son exécution réelle est d'appeler les méthodes statiques des attributs générés par le compilateur (c'est-à-dire access 0, accès à 1 $, etc.) pour obtenir ces valeurs d'attribut. Tout cela est une manipulation spéciale du compilateur.
Cette fois, c'est invalide?
Si la méthode d'écriture ci-dessus est très couramment utilisée, cette méthode d'écriture est-elle rarement exposée, mais elle peut être exécutée.
classe publique une autre autreInterClass {public static void main (String [] args) {innerclass inner = new AnotherOnUterClass (). new InnerClass (); System.out.println ("innerclass filed =" + inner.x); } classe innerclass {private int x = 10; }}Comme ci-dessus, utilisez Javap pour décompiler et jeter un œil. Mais cette fois, nous examinons d'abord les résultats de laclasse interne
16:03 $ javap -c une autre (classe InnerClassCouild de "autre outre-ultClass de" autre. Code: 0: Aload_0 1: Aload_1 2: putfield # 12; // Field ce 0 $: LanotheUterClass; 5: Aload_0 6: InvokeSpecial # 14; // Méthode java / lang / objet. "<Init>" :() v 9: Aload_0 10: bipush 10 12: putfield # 17; // champ x: i 15: returnStatic int Access 0 $ (une autreClass $ innerclass); Code: 0: ALOAD_0 1: Getfield # 17; // champ x: i 4: ireturn}
Il apparaît à nouveau et le compilateur génère automatiquement une méthode de porte dérobée pour obtenir des attributs privés accéder à 0 $ une fois pour obtenir la valeur de x.
Résultats de la décompilation de la classe.
16:08 $ javap -c une autre classe de cercle à partir de "une autre classe publique de" java.java "Une autreclasse étend java.lang.object {public une autre autre. CODE: 0: ALOAD_0 1: InvokeSpecial # 8; // Méthode java / lang / objet. "<Init>" :() v 4: returnPublic static void main (java.lang.string []); Code: 0: Nouveau # 16; // Classe un autre outoClass $ innerclass 3: dup 4: nouveau # 1; // Classe AnotherOutClass 7: DUP 8: InvokeSpecial # 18; // Méthode "<Init>" :() V 11: DUP 12: invokevirtual # 19; // Méthode java / lang / object.getClass :() ljava / lang / class; 15: Pop 16: InvocoSpecial # 23; // Méthode AnotherOnterClass $ innerclass. "<Init>" :( lanotheouterclass;) v 19: store_1 20: getStatic # 26; // Field Java / Lang / System.out: ljava / io / printStream; 23: Nouveau # 32; // Classe Java / Lang / StringBuilder 26: DUP 27: LDC # 34; // String InnerClass Filed = 29: InvokeSpecial # 36; // Méthode java / lang / stringBuilder. "<Init>" :( ljava / lang / string;) v 32: aload_1 33: invokestatique # 39; // Méthode AnotherOnterClass $ InnerClass.Access 0 $: (LanotherouterClass $ innerclass;) i 36: invokeVirtual # 43; // Méthode java / lang / stringBuilder.append: (i) ljava / lang / stringBuilder; 39: Invokevirtual # 47; // Méthode java / lang / stringbuilder.tostring :() ljava / lang / string; 42: Invokevirtual # 51; // Méthode java / io / printstream.println: (ljava / lang / string;) v 45: return}Cet appel est le fonctionnement de la classe externe pour obtenir l'attribut privé x via une instance de la classe interne.
33: invokestatique # 39; // Méthode AnotheroTerClass $ innerclass.access 0 $: (lanotheroterclass $ innerclass;) i
Ayons un autre résumé
Il y a une peine dans le document Java officiel
Si le membre ou le constructeur est déclaré privé, l'accès est autorisé si et seulement s'il se produit dans le corps de la classe de niveau supérieur (§7.6) qui enferme la déclaration du membre ou du constructeur.
Ce qui signifie que les membres et les constructeurs de la classe intérieure) sont définis comme des modificateurs privés, qui sont autorisés si et seulement si leur classe externe accède.
Comment empêcher les membres privés des classes internes d'être accessibles par
Je crois qu'après avoir lu les deux parties ci-dessus, vous sentirez qu'il est difficile pour les membres privés des classes internes d'éviter d'être accessibles par des classes externes. Qui peut faire du compilateur "désagréable fougueux"? Cela peut être fait. C'est-à-dire utiliser des classes intérieures anonymes.
Étant donné que le type d'objet Mrunnable est exécuté, pas le type de classe intérieure anonyme (nous ne pouvons pas l'obtenir normalement), et qu'il n'y a pas de propriété X dans Runanble, mrunnable.x n'est pas autorisé.
classe publique privateToouter {runnable mrunnable = new Runnable () {private int x = 10; @Override public void run () {System.out.println (x); }}; public static void main (String [] args) {privateToouter p = new privateToouter (); //System.out.println("Anonymous class private filed = "+ p.mrunnable.x); // non autorisé p.mrunnable.run (); // autorisé }}Résumé final
Dans cet article, le privé semble invalide en surface, mais en fait il ne le fait pas. Au lieu de cela, les propriétés privées sont obtenues via des méthodes indirectes lorsqu'ils sont appelés.
La construction de classes internes de Java contient des applications aux classes externes, mais C ++ ne le fait pas, ce qui est différent de C ++.
Des livres qui vont profondément dans les détails de Java
Idées de programmation Java
Série de technologies de base de Sun Company: Version chinoise Java efficace Comprendre profondément la machine virtuelle Java: fonctionnalités avancées et meilleures pratiques de JVM
Ce qui précède est une compilation des informations sur les modificateurs privés Java. Nous continuerons d'ajouter des informations pertinentes à l'avenir. Merci pour votre soutien à ce site!