Na programação Java, um membro é modificado usando a palavra -chave privada. Somente a classe em que esse membro está localizado e o método desta classe pode ser usado, e outras classes não podem acessar este membro privado.
O acima descreve as funções básicas do modificador privado. Hoje, vamos estudar a situação da falha da função privada.
Java Classes Internas
Em Java, acredito que muitas pessoas usaram classes internas. Java permite definir outra classe em uma classe. A classe da classe é uma classe interna, também chamada de classe aninhada. Uma simples implementação de classe interna pode ser a seguinte
classe OuterClass {classe InnerClass {}}O problema de hoje está relacionado às classes internas da Java e envolve apenas alguns conhecimentos de classe interna relacionados à pesquisa deste artigo. Introduziremos os seguintes artigos sobre as classes internas da Java.
A primeira vez que falhou?
Um cenário que costumamos usar na programação é acessar variáveis ou métodos privados de classes externas em uma classe interna, o que é ok. Conforme implementado no código a seguir.
public class OuterClass {Private String Language = "EN"; private string região = "nós"; classe pública InnerClass {public void priDouterclassprivatefields () {string fields = "idioma =" + idioma + "; região =" + região; System.out.println (campos); }} public static void main (string [] args) {OuterClass Outer = new OuterClass (); ExterClass.innerClass interno = Outer.New InnerClass (); interno.printouterclassprivatefields (); }}Por que isso? Um membro privado modificado não é acessível apenas pela classe descrita pelo membro? Private é realmente inválido?
O compilador está brincando?
Usamos o comando javap para visualizar os dois arquivos de classe gerados
Resultados de descompilação da Exterterclass
15:30 $ javap -C OuterclassCompiled do "OuterClass.java" classe pública Exterterclass estende Java.lang.Object {public OuterClass (); Código: 0: Aload_0 1: Invokespecial #11; // método java/lang/objeto. // String EN 7: Putfield #15; // idioma de campo: ljava/lang/string; 10: Aload_0 11: LDC #17; // String US 13: Putfield #19; // região de campo: ljava/lang/string; 16: ReturnPublic Static Void Main (java.lang.string []); Código: 0: novo #1; // Classe Outerclass 3: DUP 4: Invokespial #27; // Método "<Inir>" :() v 7: store_1 8: novo #28; // class Outerclass $ Innerclass 11: dup 12: aload_1 13: dup 14: InvokeVirtual #30; // método java/lang/object.getclass :() ljava/lang/classe; 17: POP 18: Invokespecial #34; // Método ExterClass $ innerClass. // Método ExterClass $ innerClass.printouterClassPrivateFields :() v 26: returnstatic java.lang.String Access $ 0 (Outerclass); Código: 0: Aload_0 1: Getfield #15; // idioma de campo: ljava/lang/string; 4: Java.lang.String Acesso de areturnstatic. Código: 0: Aload_0 1: Getfield #19; // região de campo: ljava/lang/string; 4: areturn}Huh? Não, não definimos esses dois métodos na Exterterclass
estático java.lang.String Access $ 0 (OuterClass); Código: 0: Aload_0 1: Getfield #15; // idioma de campo: ljava/lang/string; 4: Java.lang.String Acesso de areturnstatic. Código: 0: Aload_0 1: Getfield #19; // região de campo: ljava/lang/string; 4: areturn}
A julgar pelos comentários fornecidos, acesse $ 0 retorna o atributo de idioma da Outerclass; Acesso $ 1 Retorna o atributo da região da OuterClass. E ambos os métodos aceitam uma instância da Exterterclass como um parâmetro. Por que esses dois métodos são gerados e quais são suas funções? Vejamos os resultados de descompilação da classe interna.
Resultado da decompilação do OutterClass $ Innerclass
15:37 $ javap -C Outerclass/$ InnerClassCompiled do "OuterClass.java" classe pública OuterClass $ inerclass estende java.lang.object {final OuterClass this $ 0; public OuterClass $ innerClass (OutterClass); Código: 0: aload_0 1: aload_1 2: putfield #10; // Campo this $ 0: Louterclass; 5: Aload_0 6: Invokespecial #12; // método java/lang/objeto. Código: 0: novo #20; // Classe Java/Lang/StringBuilder 3: DUP 4: LDC #22; // String Language = 6: InvoKespecial #24; // Método java/lang/stringbuilder. "<ingit>" :( ljava/lang/string;) v 9: aload_0 10: getfield #10; // Campo 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ão = 21: InvokeVirtual #33; // método java/lang/stringbuilder.append: (ljava/lang/string;) ljava/lang/stringbuilder; 24: Aload_0 25: Getfield #10; // Campo 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; // campo 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}O código a seguir chama o código de acesso $ 0, com o objetivo de obter a propriedade privada do idioma da OuterClass.
13: Invokestatic #27; // Método OuterClass.Access $ 0: (Louterclass;) ljava/lang/string;
O código a seguir chama o código de Acesso $ 1, com o objetivo de obter a propriedade privada da região da OuterClass.
28: Invokestatic #39; // Método OuterClass.Access $ 1: (Louterclass;) ljava/lang/string;
Nota: Ao construir uma classe interna, a referência à classe externa será passada e usada como propriedade da classe interna, para que a classe interna retenha uma referência à sua classe externa.
Este $ 0 é a referência de classe externa mantida pela classe interna, que passa a referência e atribui o valor através do construtor.
Exterterclass finais deste US $ 0; Public OuterClass $ Innerclass (OuterClass); Código: 0: aload_0 1: aload_1 2: putfield #10; // Campo this $ 0: Louterclass; 5: Aload_0 6: Invokespecial #12; // método java/lang/objeto.
resumo
Essa parte do privado parece ser inválida, mas não é inválida, porque quando a classe interna chama as propriedades privadas da classe externa, sua execução real é chamar os métodos estáticos dos atributos gerados pelo compilador (ou seja, ACESS $ 0, acessar $ 1, etc.) para obter valores de atributo. Tudo isso é um manuseio especial do compilador.
Desta vez é inválido?
Se o método de escrita acima for muito usado, o método deste é raramente exposto, mas pode ser executado.
public class outraouterclass {public static void main (string [] args) {Innerclass interno = new OtherouterClass (). new InnerClass (); System.out.println ("Innerclass filed =" + Inner.x); } classe InnerClass {private int x = 10; }}Como acima, use o Javap para descompilar e dar uma olhada. Mas desta vez analisamos os resultados da integridade
16:03 $ javap -C OUTHEROUTERCLASS/$ INNERCLASSCOLLSCOLHO DO "OUTHEROUTERCLASS.JAVA" CLASS OUTROUTERCLASS $ INNERCLASS Estende Java.lang.Object {Final OtherouterClass this $ 0; outroouterclass $ innerClass (outroUterClass); Código: 0: aload_0 1: aload_1 2: putfield #12; // Campo this $ 0: lanotherouterclass; 5: Aload_0 6: Invokespecial #14; // método java/lang/objeto. // Campo X: I 15: ReturnStatic Int Access $ 0 (outroUterclass $ innerClass); Código: 0: Aload_0 1: Getfield #17; // Campo X: I 4: IreTurn}Ele aparece novamente, e o compilador gera automaticamente um método backdoor para obter atributos privados Acesso $ 0 uma vez para obter o valor de x.
Outros resultados de decompilação da classe
16:08 $ javap -c Outroouterclasscompilado de "Otherouterclass.java" Classe pública de outra forma estende Java.lang.Object {public OtherouterClass (); Código: 0: Aload_0 1: Invokespecial #8; // método java/lang/objeto. Código: 0: novo #16; // Classe Otherouterclass $ Innerclass 3: DUP 4: NOVO #1; // Classe Otherouterclass 7: DUP 8: Invokespecial #18; // Método "<Inir>" :() v 11: dup 12: InvokeVirtual #19; // método java/lang/object.getclass :() ljava/lang/classe; 15: Pop 16: Invokespecial #23; // método outroUTERTLASS $ INnerClass. // campo java/lang/system.out: ljava/io/printStream; 23: novo #32; // Classe Java/Lang/StringBuilder 26: DUP 27: LDC #34; // String InnerClass arquivado = 29: InvokesPpecial #36; // Método java/lang/stringbuilder. "<ingit>" :( ljava/lang/string;) v 32: aload_1 33: Invokestatic #39; // Método Otherouterclass $ 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 chamada é a operação da classe externa para obter atributo privado X através de uma instância da classe interna.
33: Invokestatic #39; // Método Otherouterclass $ innerclass.access $ 0: (LanoTherouterClass $ Innerclass;) i
Vamos ter outro resumo
Há uma frase no documento Java oficial
Se o membro ou construtor for declarado privado, o acesso será permitido se e somente se ocorrer dentro do corpo da classe de nível superior (§7.6), que envolve a declaração do membro ou construtor.
Significado se os membros e construtores (da classe interna) forem definidos como modificadores privados, que são permitidos se e somente se seus acessos de classe externa.
Como impedir que membros privados de classes internas sejam acessados por externos
Acredito que, depois de ler as duas partes acima, você sentirá que é difícil para membros particulares de classes internas evitar serem acessados por classes externas. Quem pode fazer o compilador "bagunçar intrometidos"? Pode realmente ser feito. Isso é usar classes internas anônimas.
Como o tipo de objeto Mrunnable é executado, não o tipo de classe interna anônima (não podemos obtê -lo normalmente), e não há propriedade X no RunAnble, Mrunnable.x não é permitido.
classe 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 Filed = "+ P.Mrunnable.x); // não é permitido p.mrunnable.run (); // permitido }}Resumo final
Neste artigo, Private parece ser inválido na superfície, mas na verdade não. Em vez disso, as propriedades privadas são obtidas através de métodos indiretos quando chamados.
A construção de classe interna da Java mantém aplicações para classes externas, mas o C ++ não, o que é diferente do C ++.
Livros que se aprofundam nos detalhes do Java
Idéias de programação Java
Série de tecnologia principal da Sun Company: versão chinesa eficaz da Java Entendendo profundamente Máquina Virtual Java: Recursos Avançados e Melhores Práticas da JVM
O exposto acima é uma compilação das informações sobre modificadores privados de Java. Continuaremos a adicionar informações relevantes no futuro. Obrigado pelo seu apoio a este site!