En la programación Java, un miembro se modifica utilizando la palabra clave privada. Solo la clase donde se encuentra este miembro y el método de esta clase se puede usar, y otras clases no pueden acceder a este miembro privado.
Lo anterior describe las funciones básicas del modificador privado. Hoy, estudiemos la situación de la falla de la función privada.
Clases internas de Java
En Java, creo que muchas personas han usado clases internas. Java permite definir otra clase en una clase. La clase en una clase es una clase interna, también llamada clase anidada. Una implementación de clase interna simple puede ser la siguiente
Clase OuterClass {Class InnClass {}}El problema de hoy está relacionado con las clases internas de Java, y solo implica algún conocimiento de clase interna relacionada con la investigación de este artículo. Presentaremos los siguientes artículos sobre clases internas de Java.
La primera vez que falló?
Un escenario que a menudo usamos en la programación es acceder a variables de miembros privados o métodos de clases externas en una clase interna, lo cual está bien. Como se implementa en el siguiente código.
Public Class OuterClass {private String Language = "EN"; Región de cadena privada = "US"; public class InnClass {public void PrintouterClassPrivateFields () {String Fields = "Language =" + lenguaje + "; región =" + región; System.out.println (campos); }} public static void main (string [] args) {outerClass outer = new OuterClass (); OuterClass.innerClass Inner = Outer.new innerClass (); inner.printouterClassPrivateFields (); }}¿Por qué es esto? ¿No es un miembro modificado privado accesible solo por la clase descrita por el miembro? ¿El privado es realmente inválido?
¿El compilador está jugando?
Usamos el comando javap para ver los archivos de dos clases generados
Resultados de descompilación de clase externa
15:30 $ Javap -c OuterClassCompiled de "OuterClass.java" Public ClassClass se extiende java.lang.object {public OuterClass (); Código: 0: Aload_0 1: Invokespecial #11; // método java/lang/objeto. "<Init>" :() V 4: Aload_0 5: LDC #13; // String en 7: Putfield #15; // lenguaje de campo: ljava/lang/string; 10: Aload_0 11: LDC #17; // String US 13: Putfield #19; // región de campo: ljava/lang/string; 16: returnpublic static void main (java.lang.string []); Código: 0: nuevo #1; // clase externa 3: DUP 4: Invokespecial #27; // Método "<Init>" :() v 7: store_1 8: nuevo #28; // Clase OuterClass $ InnClass 11: Dup 12: Aload_1 13: Dup 14: InvokeVirtual #30; // método java/lang/object.getClass :() ljava/lang/class; 17: Pop 18: Invokespecial #34; // Método OuterClass $ InnClass. "<Init>" :( LuterClass;) V 21: Store_2 22: Aload_2 23: InvokeVirtual #37; // Método OuterClass $ InnClass.PrinTouterClassPrivateFields :() V 26: returnstatic java.lang.string Access $ 0 (OuterClass); Código: 0: Aload_0 1: Getfield #15; // lenguaje de campo: ljava/lang/string; 4: Areturnstatic java.lang.String Access $ 1 (OuterClass); Código: 0: Aload_0 1: Getfield #19; // región de campo: ljava/lang/string; 4: Areturn}¿Eh? No, no definimos estos dos métodos en la clase externa
Java.lang.String estático Acceso $ 0 (OuterClass); Código: 0: Aload_0 1: Getfield #15; // lenguaje de campo: ljava/lang/string; 4: Areturnstatic java.lang.String Access $ 1 (OuterClass); Código: 0: Aload_0 1: Getfield #19; // región de campo: ljava/lang/string; 4: Areturn}
A juzgar por los comentarios dados, el acceso $ 0 devuelve el atributo de idioma de OuterClass; Access $ 1 Devuelve el atributo de región de OuterClass. Y ambos métodos aceptan una instancia de clase externa como parámetro. ¿Por qué se generan estos dos métodos y cuáles son sus funciones? Veamos los resultados de descompilación de la clase interna.
Resultado de descompilación de la clase externa $ InnerClass
15:37 $ Javap -c OuterClass/$ InnClassCompilado de "OuterClass.java" clase pública externa $ InnClass se extiende java.lang.object {Clase externa final este $ 0; publicClass PublicClass $ InnClass (OuterClass); Código: 0: Aload_0 1: Aload_1 2: Putfield #10; // Field this $ 0: LouterClass; 5: Aload_0 6: Invokespecial #12; // método java/lang/object. "<Init>" :() V 9: returnpublic void impRinTouterClassPrivateFields (); Código: 0: nuevo #20; // clase java/lang/stringBuilder 3: dup 4: ldc #22; // lenguaje de cadena = 6: Invokespecial #24; // método java/lang/stringBuilder. "<Init>" :( ljava/lang/string;) V 9: Aload_0 10: Getfield #10; // Field this $ 0: LouterClass; 13: Invokestatic #27; // Método OuterClass.access $ 0: (LouterClass;) ljava/lang/string; 16: InvokeVirtual #33; // método java/lang/stringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 19: LDC #37; // string; región = 21: InvokeVirtual #33; // método java/lang/stringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 24: Aload_0 25: Getfield #10; // Field this $ 0: LouterClass; 28: Invokestatic #39; // método outerClass.access $ 1: (lOUterClass;) ljava/lang/string; 31: InvokeVirtual #33; // método java/lang/stringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 34: InvokeVirtual #42; // método 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étodo java/io/printstream.println: (ljava/lang/string;) v 45: return}El siguiente código llama al código de acceso $ 0, con el propósito de obtener la propiedad privada de lenguaje de OuterClass.
13: Invokestatic #27; // Método OuterClass.access $ 0: (LouterClass;) ljava/lang/string;
El siguiente código llama al código de acceso de $ 1, con el propósito de obtener la propiedad privada de la región de OuterClass.
28: Invokestatic #39; // método outerClass.access $ 1: (lOUterClass;) ljava/lang/string;
Nota: Al construir una clase interna, la referencia a la clase exterior se pasará y se usará como propiedad de la clase interna, por lo que la clase interna tendrá una referencia a su clase externa.
Este $ 0 es la referencia de clase externa mantenida por la clase interna, que pasa la referencia y asigna el valor a través del constructor.
Final OuterClass Este $ 0; Public OuterClass $ InnClass (OuterClass); Código: 0: Aload_0 1: Aload_1 2: Putfield #10; // Field this $ 0: LouterClass; 5: Aload_0 6: Invokespecial #12; // método java/lang/objeto. "<Init>" :() V 9: return
resumen
Esta parte de privado parece ser inválida, pero no es inválida, porque cuando la clase interna llama las propiedades privadas de la clase externa, su ejecución real es llamar a los métodos estáticos de los atributos generados por el compilador (es decir, acceder $ 0, acceder a $ 1, etc.) para obtener estos valores de atributos. Todo esto es un manejo especial del compilador.
Esta vez no es válido?
Si el método de escritura anterior se usa muy comúnmente, entonces este método de escritura rara vez se expuso, pero se puede ejecutar.
clase pública OtrooUterClass {public static void main (String [] args) {innerClass inner = new OtroyoUterClass (). New InnClass (); System.out.println ("InnerClass archivado =" + inner.x); } clase Innclass {private int x = 10; }}Como arriba, use Javap para descompilar y echar un vistazo. Pero esta vez primero miramos los resultados de InnerClass
16:03 $ javap -c OtroyouterClass/$ InnclassCompiled de "OtrotheroUterClass.java" clase OtrooUoUterClass $ InnerClass extiende java.lang.object {final OtrooUoUterClass this $ 0; OtrooUterClass $ InnerClass (otrauuterclass); Código: 0: Aload_0 1: Aload_1 2: Putfield #12; // Field este $ 0: LanotherouterClass; 5: Aload_0 6: Invokespecial #14; // método java/lang/objeto. "<Init>" :() V 9: Aload_0 10: Bipush 10 12: Putfield #17; // Campo X: I 15: ReturnStatic int Código: 0: Aload_0 1: Getfield #17; // Campo X: I 4: Ireturn}Aparece nuevamente, y el compilador genera automáticamente un método de puerta trasera para obtener atributos privados, acceda a $ 0 una vez para obtener el valor de x.
OTROS RESULTADOS DE DESCOMPILACIÓN
16:08 $ javap -c otro UCLASSCOMPLECIDO DE "OTROUVERCLASS.JAVA" CLASE PÚBLICA OTROUVERCLASS extiende java.lang.object {public otheresclass (); Código: 0: Aload_0 1: Invokespecial #8; // método java/lang/objeto. "<Init>" :() v 4: returnpublic static void main (java.lang.string []); Código: 0: nuevo #16; // Clase OtrooUterClass $ InnClass 3: DUP 4: Nuevo #1; // Clase OtrooUterClass 7: DUP 8: Invokespecial #18; // Método "<Init>" :() V 11: DUP 12: InvokeVirtual #19; // método java/lang/object.getClass :() ljava/lang/class; 15: Pop 16: Invokespecial #23; // Método OTROUNTERCLASS $ InnerClass. "<Init>" :( LanoterouterClass;) V 19: Store_1 20: GetStatic #26; // Field java/lang/system.out: ljava/io/printstream; 23: nuevo #32; // clase java/lang/stringBuilder 26: dup 27: ldc #34; // String InnClass Arched = 29: Invokespecial #36; // método java/lang/stringBuilder. "<Init>" :( ljava/lang/string;) v 32: Aload_1 33: Invokestatic #39; // Método Otrouterclass $ InnerClass.access $ 0: (Lanotherouterclass $ innerClass;) I 36: InvokeVirtual #43; // método java/lang/stringBuilder.append: (i) ljava/lang/stringBuilder; 39: InvokeVirtual #47; // método java/lang/stringBuilder.ToString :() ljava/lang/string; 42: InvokeVirtual #51; // método java/io/printstream.println: (ljava/lang/string;) v 45: return}Esta llamada es la operación de la clase externa para obtener el atributo privado X a través de una instancia de la clase interna.
33: Invokestatic #39; // Método OTROUDERCLASS $ InnerClass.access $ 0: (Lanotherouterclass $ InnerClass;) I
Tengamos otro resumen
Hay una oración en el documento oficial de Java
Si el miembro o constructor se declara privado, entonces se permite el acceso si y solo si ocurre dentro del cuerpo de la clase de nivel superior (§7.6) que encierra la declaración del miembro o constructor.
Es decir, los miembros y constructores de IF (clase interna) se establecen como modificadores privados, que están permitidos si y solo si su clase externa accede.
Cómo evitar que los miembros privados de las clases internas se accedan por externas
Creo que después de leer las dos partes anteriores, sentirá que es difícil para los miembros privados de las clases internas evitar que las clases externas accedan. ¿Quién puede hacer que el compilador sea "Messing Qosy"? En realidad se puede hacer. Eso es usar clases internas anónimas.
Dado que el tipo de objeto Mrunnable es ejecutable, no el tipo de clase interna anónima (no podemos obtenerlo normalmente), y no hay una propiedad X en Runanble, Mrunnable.x no está permitido.
clase pública 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 archivado = "+ p.mrunnable.x); // no permitido p.mrunnable.run (); // permitido }}Resumen final
En este artículo, el privado parece ser inválido en la superficie, pero de hecho no. En cambio, las propiedades privadas se obtienen a través de métodos indirectos cuando se les llama.
La construcción de clase interna de Java contiene aplicaciones a clases externas, pero C ++ no lo hace, lo cual es diferente de C ++.
Libros que profundizan en los detalles de Java
Ideas de programación de Java
Sun Company's Core Technology Series: Versión china efectiva de Java Comprender profundamente la máquina virtual Java: características avanzadas y las mejores prácticas de JVM
Lo anterior es una compilación de la información sobre los modificadores privados de Java. Continuaremos agregando información relevante en el futuro. ¡Gracias por su apoyo para este sitio!