A função e uso de classes internas em java

Endereço original: https://blog.csdn.net/u013728021/article/details/87358517

Artigo Directory
O papel da classe interna
1. Acesso incondicional a todos os elementos da classe externa
2. Realização de ocultação
3. Herança múltipla pode ser realizada
4. Realização de interface simples por meio de classes internas anônimas para otimizar
o relacionamento entre a
classe interna e a classe externa Classificação da classe interna
A diferença entre classes internas estáticas e classes internas não estáticas Classes internas
locais Classes
internas anônimas
Problemas que podem ser causados ​​por classes internas no desenvolvimento real
Classes internas podem causar vazamentos de memória do programa

O que é uma classe interna: uma classe definida em uma
classe O papel de
uma classe interna Por que precisamos de uma classe interna? Ou por que existem classes internas? Os principais motivos são os seguintes:

Os métodos da classe interna podem acessar os dados no escopo da definição da classe, incluindo dados privados modificados por privado. As
classes internas podem ser ocultadas de outras classes no mesmo pacote. As
classes internas podem resolver os defeitos da herança única de java.
Quando quisermos definir um retorno de chamada Quando a função não deseja escrever muito código, podemos escolher usar uma classe interna anônima para implementá-la. Os
exemplos são os seguintes:
1. Todos os elementos da classe externa podem ser acessados ​​incondicionalmente.
Por que podemos referência:

Embora a classe interna e a classe externa sejam gravadas no mesmo arquivo, após a conclusão da compilação, seus respectivos arquivos de classe são gerados e a classe interna acessa os membros da classe externa por meio dele.

1 O compilador adiciona automaticamente uma variável de membro à classe interna. O tipo desta variável de membro é o mesmo que o tipo da classe externa. Esta variável de membro é uma referência ao objeto de classe externa (this);

2 O compilador adiciona automaticamente um parâmetro ao método de construção da classe interna. O tipo do parâmetro é o tipo da classe externa. Este parâmetro é usado no método de construção para atribuir valores às variáveis ​​de membro adicionadas na classe interna. classe;

3 Ao chamar o construtor da classe interna para inicializar o objeto da classe interna, a referência da classe externa será passada por padrão.

Classpath da instrução de compilação javac (caminho para o arquivo .java).
Instrução de descompilação javap -v (informações detalhadas) classpath (caminho para o arquivo .class)

/ **
 * A classe interna acessa incondicionalmente o elemento da classe externa
 * /
public class DataOuterClass {

    private String data = "Dados de classe externa";

    classe privada InnerClass {

        public InnerClass () {

            System.out.println (dados);

        }

    }

    public void getInner () {

        new InnerClass ();

    }

    public static void main (String [] args) {

        DataOuterClass outerClass = new DataOuterClass ();

        outerClass.getInner ();

    }

}
Imprimir

Dados de classe externa
1
dados Esta é uma variável privada definida em DataOuterClass. Essa variável pode ser acessada incondicionalmente na classe interna.

2. Realização da ocultação
O segundo benefício das classes internas é na verdade muito óbvio.Todos sabemos que as classes externas, isto é, as classes comuns, não podem ser decoradas com símbolos de autoridade de acesso protegido privado, enquanto as classes internas podem ser decoradas com privado e protegido. Quando usamos private para decorar a classe interna, essa classe fica oculta do lado de fora. Isso não parece ter muito efeito, mas quando a classe interna implementa uma interface, ela está passando por uma transformação para cima, que oculta completamente a implementação da interface de fora.

interface

interface pública InnerInterface {

    void innerMethod ();

}
Classe concreta

/ **
 * Realiza o ocultamento de informações
 * /
public class OuterClass {

    / **
     * private modifica classes internas para realizar o     ocultamento de informações
     * /
private class InnerClass implements InnerInterface {

        @Override
        public void innerMethod () {             System.out.println ("Realize o ocultamento da classe interna");         }

    }

    public InnerInterface getInner () {

        retornar novo InnerClass ();

    }

}

Invoque o programa

public class Test {

    public static void main (String [] args) {

        OuterClass outerClass = new OuterClass ();

        InnerInterface inner = outerClass.getInner ();

        inner.innerMethod ();

    }

}

Imprimir

Implementar ocultação de classe interna
1
A partir desse código, só sei que o método getInner () de OuterClass pode retornar uma instância da interface InnerInterface, mas não sei como essa instância é implementada.

E porque InnerClass é privado, não podemos ver o nome desta classe específica se não olharmos para o código, então ele pode estar bem escondido.

3. A herança múltipla pode ser alcançada.Nós
sabemos que o java não permite o uso de extensões para herdar várias classes. A introdução de classes internas pode resolver bem este problema.
Eu entendo que Java só pode herdar uma classe.Qualquer pessoa que aprendeu a sintaxe básica sabe que, antes de haver classes internas, seu método de herança múltipla é implementado por interfaces. Mas às vezes há muitos inconvenientes em usar a interface. Por exemplo, se implementamos uma interface, devemos implementar todos os métodos nela.
Com classes internas, é diferente. Ele pode fazer nossa classe herdar várias classes concretas ou abstratas. Como o exemplo a seguir:

Classe Um

public class ExampleOne {

    public String name () {

        retornar "interno";

    }

}
Classe dois

public class ExampleTwo {

    public int age () {

        return 25;

    }

}
Classe Três

public class MainExample {

   / **
    * Classe interna 1 herda ExampleOne
    * /
   private class InnerOne extends ExampleOne {

       public String name () {

           return super.name ();

       }

   }

   / **
    * Inner class 2 herda ExampleTwo
    * /
   private class InnerTwo extends ExampleTwo {

       public int age () {

           return super.age ();

       }

   }

   public String name () {

       retornar novo InnerOne (). nome ();

   }

   public int age () {

       retornar novo InnerTwo (). age ();

   }

   public static void main (String [] args) {

       MainExample mi = novo MainExample ();

       System.out.println ("姓名:" + mi.name ());

       System.out.println ("年龄:" + mi.age ());

   }

}

Observe a classe três, que implementa duas classes internas InnerOne e InnerTwo, respectivamente. A classe InnerOne herda ExampleOne e InnerTwo herda ExampleTwo, portanto, nossa classe três MainExample tem os métodos e propriedades de ExampleOne e ExampleTwo. Realiza indiretamente a herança múltipla.

4. Otimize a implementação de interface simples por meio de classes internas anônimas.Acredito
que todos estão familiarizados com classes internas anônimas.Nossa maneira comum de escrever eventos de clique é assim:
não há necessidade de implementar objetos OnClickListener.

...
    view.setOnClickListener (new View.OnClickListener () {         @Override         public void onClick () {             // ... do XXX ...         }     }) ... A relação entre a classe interna e a classe externa é para não classe interna estática, a criação da classe interna depende do objeto de instância da classe externa, e a classe interna não pode ser criada sem uma instância da classe externa







A classe interna é uma entidade relativamente independente e não é um relacionamento é um com a classe externa

O momento de criação da classe interna não depende da criação da classe externa

Existem duas maneiras de criar classes internas comuns:

public class ClassOuter {     public void fun () {         System.out.println ("外部 类 方法");     }     public class InnerClass {     } }
    



    

        

public class TestInnerClass {     public static void main (String [] args) {         // Criar método 1         ClassOuter.InnerClass innerClass = new ClassOuter (). new InnerClass ();         // Criar método 2         ClassOuter outer = new ClassOuter ();         ClassOuter. InnerClass inner = outer.new InnerClass ();     } } Classificação das classes internas As classes internas podem ser divididas em: classes internas estáticas (classes aninhadas) e classes internas não estáticas. As classes internas não estáticas podem ser divididas em: classes internas de membro, classes internas de método e classes internas anônimas.









A diferença entre a classe interna estática e a classe interna não estática Classe interna estática Classe
interna não estática
As variáveis ​​de membro estático podem ser
permitidas? Sim Não Você pode acessar as variáveis ​​não estáticas da classe externa? Não Sim
Você pode acessar as variáveis ​​estáticas da classe externa? Sim A
criação é dependente de externa Quer a classe seja ou não,
podemos entender essas diferenças por meio de um exemplo:

classe pública ClassOuter {     private int noStaticInt = 1;     estático privado int STATIC_INT = 2;

    public void fun () {         System.out.println ("Método de classe externa");     }

    public class InnerClass {         // static int num = 1; Neste momento, o editor relatará um erro para classes internas não estáticas, portanto, elas não podem ter membros estáticos.         public void fun () {             // Membros não estáticos de não -static classes internas podem acessar membros não estáticos da variável de classes externas.             System.out.println (STATIC_INT);             System.out.println (noStaticInt);         }     }






    public static class StaticInnerClass {         static int NUM = 1; // Classes internas estáticas podem ter membros estáticos         public void fun () {             System.out.println (STATIC_INT);             //System.out.println(noStaticInt); No momento, o editor Ele relatará que variáveis ​​não estáticas de classes externas não podem ser acessadas.         }     } }






public class TestInnerClass {     public static void main (String [] args) {         // Método de criação de classe interna não estática 1         ClassOuter.InnerClass innerClass = new ClassOuter (). new InnerClass ();         // Método de criação de classe interna não estática 2         ClassOuter outer = new ClassOuter ();         ClassOuter.InnerClass inner = outer.new InnerClass ();         // Como criar uma classe interna estática         ClassOuter.StaticInnerClass staticInnerClass = new ClassOuter.StaticInnerClass ();     } } Definição de classe interna local











Se uma classe interna for usada em apenas um método, podemos definir essa classe dentro do método.Este tipo de classe interna é chamada de classe interna local. Seu escopo é limitado a este método.

Existem dois pontos dignos de nossa atenção na classe interna local:

Classes internas locais não têm permissão para usar o modificador de permissão de acesso público privado protegido, nem
as classes internas locais são completamente ocultas do lado de fora e o acesso não é permitido em outros lugares, exceto o método de criação desta classe pode acessá-lo.
A diferença entre uma classe interna local e uma classe interna de membro é que ela pode se referir a uma variável de membro, mas o membro deve ser declarado como final e o valor da variável não pode ser modificado internamente. (Esta frase não é precisa, porque se não for um tipo de dados básico, simplesmente não é permitido modificar o objeto apontado pela referência, e o próprio objeto pode ser modificado)
public class ClassOuter {     private int noStaticInt = 1;     estático privado int STATIC_INT = 2;

    public void fun () {         System.out.println ("Método de classe externa");     }     public void testFunctionClass () {         classe FunctionClass {             private void fun () {                 System.out.println ("Saída de classe interna local");                 Sistema .out.println (STATIC_INT);                 System.out.println (noStaticInt);                 System.out.println (params);                 // params ++; // params é imutável, então esta frase é compilada incorretamente             }         }         FunctionClass functionClass = new FunctionClass ( );         functionClass.fun ();     } } Classe interna anônima Uma classe interna anônima não tem modificador de acesso. Uma classe interna anônima deve herdar uma classe abstrata ou implementar uma interface


    

















Não pode haver nenhum membro ou método estático em
uma classe interna anônima.Uma classe interna anônima não tem nenhum método de construção porque não possui um nome de classe.
Da mesma forma que as classes internas locais, as classes internas anônimas também podem fazer referência a variáveis ​​locais. Esta variável também deve ser declarada como
classe pública final Button {     public void click (final int params) {         // Classe interna anônima, que implementa a interface         ActionListener new ActionListener () {             public void onAction () {                 System.out.println (" click action ... "+ params);             }         } .onAction ();     }     // A classe interna anônima deve herdar ou implementar uma interface     pública existente ActionListener {         public void onAction ();     }











    public static void main (String [] args) {         Button button = new Button ();         button.click ();     } } Por que as variáveis ​​locais precisam ser finalizadas?




Porque o ciclo de vida de variáveis ​​locais e classes internas anônimas são diferentes.

A classe interna anônima é armazenada no heap após ser criada e as variáveis ​​locais no método são armazenadas na pilha Java. Quando o método é executado, a pilha é desempilhada e as variáveis ​​locais desaparecerão.

Então, a classe interna anônima ainda pode estar armazenada no heap neste momento, portanto, onde a classe interna anônima deve encontrar essa variável local?

Para resolver este problema, o compilador automaticamente nos ajuda a criar um backup das variáveis ​​locais na classe interna anônima. Ou seja, mesmo que a execução do método termine, ainda há um backup na classe interna anônima. Naturalmente, nós não tem medo de encontrá-lo.

Mas o problema voltou.

Se o a na variável local continuar mudando.
Portanto, você não quer permitir que uma variável de backup mude o tempo todo.
Para manter as variáveis ​​locais consistentes com o campo de backup na classe interna anônima.
O compilador deve estipular que esses campos locais devem ser constantes e, uma vez atribuídos, não podem mais ser alterados.

Portanto, a razão pela qual o domínio do método externo da classe interna anônima deve ser o domínio constante.

Atenção especial
No Java8, as restrições de modificação no final foram removidas, mas na verdade, desde que seja usado em uma classe interna anônima, a variável se tornará automaticamente final (só pode ser usada, não atribuída).

Problemas que podem ser causados ​​por
classes internas no desenvolvimento real. As classes internas podem causar vazamentos de memória no programa.
Acredito que amigos do Android não desconheçam este exemplo. O Handler que usamos com frequência nos alertará com esses avisos o tempo todo.

Vejamos primeiro por que as classes internas causam vazamentos de memória.

Para entender por que as classes internas causam vazamentos de memória, devemos entender o mecanismo de reciclagem da máquina virtual java, mas não apresentaremos o mecanismo de reciclagem de memória java em detalhes aqui. Precisamos apenas entender o mecanismo de reciclagem de memória java por meio da "Análise alcançável " alcançar.

Ou seja, a máquina virtual Java usa o mecanismo de reciclagem de memória para determinar se as referências são alcançáveis ​​e, se não forem, serão recicladas em algum ponto.

Portanto, em que circunstâncias as classes internas podem causar vazamentos de memória?

Se uma classe interna anônima não for mantida por nenhuma referência, o objeto da classe interna anônima terá uma chance de ser reciclado.

Se a classe interna for referenciada apenas na classe externa, quando a classe externa não for mais referenciada, tanto a classe externa quanto a interna podem ser recicladas pelo GC.

Se a referência da classe interna for referenciada por outras classes diferentes da classe externa, isso fará com que a classe interna e a classe externa não possam ser recicladas pelo GC, mesmo se a classe externa não for referenciada, porque a classe interna classe contém uma referência à classe externa).

public class ClassOuter {

    Object object = new Object () {         public void finalize () {             System.out.println ("interior Libere a memória ocupada ...");         }     };



    public void finalize () {         System.out.println ("Liberação externa da memória ocupada ...");     } }


public class TestInnerClass {     public static void main (String [] args) {         try {             Test ();         } catch (InterruptedException e) {             e.printStackTrace ();         }     }






    private static void Test () lança InterruptedException {         System.out.println ("Início do programa.");

        ClassOuter outer = new ClassOuter ();
        Object object = outer.object;
        externo = nulo;

        System.out.println ("Execute GC");
        System.gc ();

        Thread.sleep (3000);
        System.out.println ("Fim do programa.");
    }
} O
programa em execução descobre que a recuperação de memória não recupera o objeto objeto.
Isso ocorre porque mesmo que a classe externa não seja referenciada por nenhum variável, contanto que sua A classe interna seja mantida por variáveis ​​diferentes da classe externa, e a classe externa não será coletada pelo GC.

Devemos prestar atenção especial à situação em que a classe interna é referenciada por outras classes externas, o que torna a classe externa incapaz de ser liberada, o que pode facilmente levar a vazamentos de memória.

Quando Hanlder é usado como uma classe interna no Android, seus objetos são mantidos pela fila de mensagens enviada por Hanlder na MessageQueue Message Queue controlada pelo Looper do thread principal do sistema (claro, este também é o Looper criado manualmente por o thread filho). Quando há um grande número de filas de mensagens Quando o processamento da mensagem precisa ser processado, ou a mensagem atrasada precisa ser executada, a Activity que criou o Handler saiu e o objeto Activity não pode ser liberado, o que causa um vazamento de memória.
Então, quando o Hanlder será liberado? Quando a fila de mensagens terminar de processar a mensagem transportada pelo Hanlder, ele chamará msg.recycleUnchecked () para liberar a referência Handler mantida pela Mensagem.

Para lidar com vazamentos de memória Hanlder no Android, você pode começar por dois aspectos:

Depois de fechar o onDestry de Activity / Fragment, cancele a Message que ainda está na fila:

mHandler.removeCallbacksAndMessages (null);
1
Crie Hanlder como uma classe interna estática e use a referência flexível

Classe
   estática privada mHandler MyHandler extends Handler {

        final privado WeakReference <MainActivity> mActivity;

        público MyHandler (atividade MainActivity) {             mActivity = new WeakReference <MainActivity> (atividade);         }

        @Override
        public void handleMessage (mensagem msg) {             MainActivity activity = mActivity.get ();             if (activity == null || activity.isFinishing ()) {                return;             }             // ...         }     }






Acho que você gosta

Origin blog.csdn.net/u013804636/article/details/107066187
Recomendado
Clasificación