Le rôle et l'utilisation des classes internes en java

Adresse d'origine: https://blog.csdn.net/u013728021/article/details/87358517

Répertoire d'article
Le rôle de la classe interne
1. Accès inconditionnel à tous les éléments de la classe externe
2. Réalisation de la dissimulation
3. L'héritage multiple peut être réalisé
4. Réalisation d'interface simple à travers des classes internes anonymes pour optimiser
la relation entre la
classe interne et le classe externe Classification de la classe interne
La différence entre les classes internes statiques et les classes internes non statiques Classes internes
locales Classes
internes anonymes
Problèmes pouvant être causés par des classes internes dans le développement réel Les classes internes
peuvent provoquer des fuites de mémoire du programme

Qu'est-ce qu'une classe interne: une classe définie dans une
classe Le rôle d'
une classe interne Pourquoi avons-nous besoin d'une classe interne? Ou pourquoi des classes internes existent-elles? Les principales raisons sont les suivantes:

Les méthodes de classe internes peuvent accéder aux données dans la portée de la définition de classe, y compris les données privées modifiées par private.
Les classes internes peuvent être masquées aux autres classes du même package.
Les classes internes peuvent résoudre les défauts de l'héritage unique Java.
Lorsque nous le souhaitons définir un rappel Lorsque la fonction ne souhaite pas écrire beaucoup de code, nous pouvons choisir d'utiliser une classe interne anonyme pour l'implémenter. Les
exemples sont les suivants:
1. Tous les éléments de la classe externe sont accessibles de manière inconditionnelle.
Pourquoi pouvons-nous référence:

Bien que la classe interne et la classe externe soient écrites dans le même fichier, une fois la compilation terminée, leurs fichiers de classe respectifs sont générés et la classe interne accède aux membres de la classe externe par ce biais.

1 Le compilateur ajoute automatiquement une variable membre à la classe interne. Le type de cette variable membre est le même que le type de la classe externe. Cette variable membre est une référence à l'objet de classe externe (this);

2 Le compilateur ajoute automatiquement un paramètre à la méthode de construction de la classe interne. Le type du paramètre est le type de la classe externe. Ce paramètre est utilisé dans la méthode de construction pour affecter des valeurs aux variables membres ajoutées dans la classe interne. classer;

3 Lors de l'appel du constructeur de la classe interne pour initialiser l'objet de classe interne, la référence de la classe externe sera transmise par défaut.

Instruction de compilation javac classpath (chemin vers le fichier .java)
Instruction de décompilation javap -v (informations détaillées) classpath (chemin vers le fichier .class)

/ **
 * La classe interne accède sans condition à l'élément de classe externe
 * /
public class DataOuterClass {

    private String data = "Données de classe externes";

    classe privée InnerClass {

        public InnerClass () {

            System.out.println (données);

        }

    }

    public void getInner () {

        new InnerClass ();

    }

    public static void main (String [] args) {

        DataOuterClass externalClass = nouveau DataOuterClass ();

        externalClass.getInner ();

    }

}
Imprimer

Données de classe externe
1
données Il s'agit d'une variable privée définie dans DataOuterClass. Cette variable est accessible sans condition dans la classe interne.

2. Réalisation de la dissimulation
Le deuxième avantage des classes internes est en fait très évident: nous savons tous que les classes externes, c'est-à-dire les classes ordinaires, ne peuvent pas être décorées avec des symboles d'autorité d'accès privés protégés, tandis que les classes internes peuvent être décorées avec privé et protégé. Lorsque nous utilisons private pour décorer la classe interne, cette classe est masquée de l'extérieur. Cela ne semble pas avoir beaucoup d'effet, mais lorsque la classe interne implémente une interface, elle subit une transformation ascendante, qui masque complètement l'implémentation de l'interface de l'extérieur.

interface

interface publique InnerInterface {

    void innerMethod ();

}
Classe de béton

/ **
 * Réalise le masquage d'informations
 * /
public class OuterClass {

    / **
     * private modifie les classes internes pour réaliser le masquage des informations
     * /
    private class InnerClass implémente InnerInterface {

        @Override
        public void innerMethod () {             System.out.println ("Réaliser le masquage de classe interne");         }

    }

    public InnerInterface getInner () {

        return new InnerClass ();

    }

}

Appelez le programme

Test de classe publique {

    public static void main (String [] args) {

        OuterClass externalClass = nouvelle OuterClass ();

        InnerInterface inner = externalClass.getInner ();

        inner.innerMethod ();

    }

}

Imprimer

Implémenter le masquage de classe interne
1
À partir de ce code, je sais seulement que la méthode getInner () de OuterClass peut renvoyer une instance de l'interface InnerInterface, mais je ne sais pas comment cette instance est implémentée.

Et comme InnerClass est privé, nous ne pouvons pas voir le nom de cette classe spécifique si nous ne regardons pas le code, donc il peut être bien caché.

3. L'héritage multiple peut être obtenu
Nous savons que java ne permet pas l'utilisation de extend pour hériter de plusieurs classes. L'introduction de classes internes peut bien résoudre ce problème.
Je comprends que Java ne peut hériter que d'une seule classe. Quiconque a appris la syntaxe de base sait qu'avant qu'il y ait des classes internes, sa méthode d'héritage multiple est implémentée par des interfaces. Mais parfois, l'utilisation de l'interface présente de nombreux inconvénients. Par exemple, si nous implémentons une interface, nous devons implémenter toutes les méthodes qu'elle contient.
Avec les classes internes, c'est différent. Cela peut faire en sorte que notre classe hérite de plusieurs classes concrètes ou abstraites. Comme l'exemple suivant:

Classe un

classe publique ExampleOne {

    public String name () {

        retourne "intérieur";

    }

}
Classe deux

classe publique ExampleTwo {

    public int age () {

        retour 25;

    }

}
Classe trois

classe publique MainExample {

   / **
    * La classe interne 1 hérite de ExampleOne
    * /
   la classe privée InnerOne étend ExampleOne {

       public String name () {

           retourne super.name ();

       }

   }

   / **
    * La classe interne 2 hérite de ExampleTwo
    * /
   la classe privée InnerTwo étend ExampleTwo {

       public int age () {

           retourne super.age ();

       }

   }

   public String name () {

       retourne un nouveau InnerOne (). name ();

   }

   public int age () {

       return new InnerTwo (). age ();

   }

   public static void main (String [] args) {

       MainExample mi = nouveau MainExample ();

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

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

   }

}

Regardez la classe trois, qui implémente respectivement deux classes internes InnerOne et InnerTwo. La classe InnerOne hérite de ExampleOne et InnerTwo hérite de ExampleTwo, donc notre classe trois MainExample a les méthodes et les propriétés de ExampleOne et ExampleTwo. Réalise indirectement l'héritage multiple.

4. Optimisez l'implémentation d'interface simple grâce à des classes internes anonymes.
Je pense que tout le monde est familier avec les classes internes anonymes. Notre façon courante d'écrire des événements de clic est comme ceci:
pas besoin d'implémenter des objets OnClickListener.

...
    view.setOnClickListener (new View.OnClickListener () {         @Override         public void onClick () {             // ... do XXX ...         }     }) ... La relation entre la classe interne et la classe externe est pour non- classe interne statique, la création de la classe interne dépend de l'objet d'instance de la classe externe, et la classe interne ne peut pas être créée sans une instance de la classe externe







La classe interne est une entité relativement indépendante et n'est pas une relation is-a avec la classe externe

Le moment de la création de la classe interne ne dépend pas de la création de la classe externe

Il existe deux façons de créer des classes internes communes:

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



    

        

public class TestInnerClass {     public static void main (String [] args) {         // Créer la méthode 1         ClassOuter.InnerClass innerClass = new ClassOuter (). new InnerClass ();         // Créer la méthode 2         ClassOuter external = new ClassOuter ();         ClassOuter. InnerClass inner = external.new InnerClass ();     } } Classification des classes internes Les classes internes peuvent être divisées en: classes internes statiques (classes imbriquées) et classes internes non statiques. Les classes internes non statiques peuvent être divisées en: classes internes membres, classes internes de méthodes et classes internes anonymes.









La différence entre la classe interne statique et la classe interne non statique Classe interne statique Classe
interne non statique Les
variables membres statiques peuvent-elles être
autorisées? Oui Non Pouvez-vous accéder aux variables non statiques de la classe externe? Non Oui
Pouvez-vous accéder aux variables statiques Oui La
création dépend-elle de l'externe Que la classe soit ou non,
nous pouvons comprendre ces différences à travers un exemple:

classe publique ClassOuter {     int privé noStaticInt = 1;     int statique privé STATIC_INT = 2;

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

    public class InnerClass {         // static int num = 1; À ce stade , l'éditeur signalera une erreur pour les classes internes non statiques, donc elles ne peuvent pas avoir de membres statiques.         public void fun () {             // Membres non statiques de non -static les classes internes peuvent accéder aux membres non statiques de la variable des classes externes.             System.out.println (STATIC_INT);             System.out.println (noStaticInt);         }     }






    public static class StaticInnerClass {         static int NUM = 1; // Les classes internes statiques peuvent avoir des membres statiques         public void fun () {             System.out.println (STATIC_INT);             //System.out.println(noStaticInt); À ce stade, le éditeur Il signalera que les variables non statiques des classes externes ne sont pas accessibles.         }     } }






public class TestInnerClass {     public static void main (String [] args) {         // Méthode de création de classe interne non statique 1         ClassOuter.InnerClass innerClass = new ClassOuter (). new InnerClass ();         // Méthode de création de classe interne non statique 2         ClassOuter external = new ClassOuter ();         ClassOuter.InnerClass inner = external.new InnerClass ();         // Comment créer une classe interne statique         ClassOuter.StaticInnerClass staticInnerClass = new ClassOuter.StaticInnerClass ();     } } Définition de classe interne locale











Si une classe interne est utilisée dans une seule méthode, nous pouvons définir cette classe à l'intérieur de la méthode. Ce type de classe interne est appelé une classe interne locale. Son champ d'application est limité à cette méthode.

Il y a deux points qui méritent notre attention dans la classe interne locale:

Les classes internes locales ne sont pas autorisées à utiliser le modificateur d'autorisation d'accès public private protected,
les classes internes locales ne sont pas non plus complètement masquées de l'extérieur et l'accès n'est pas autorisé ailleurs, sauf que la méthode de création de cette classe peut y accéder.
La différence entre une classe interne locale et une classe interne membre est qu'elle peut faire référence à une variable membre, mais le membre doit être déclaré comme final et la valeur de la variable ne peut pas être modifiée en interne. (Cette phrase n'est pas exacte, car s'il ne s'agit pas d'un type de données de base, il n'est tout simplement pas autorisé à modifier l'objet pointé par la référence, et l'objet lui-même peut être modifié)
public class ClassOuter {     private int noStaticInt = 1;     int statique privé STATIC_INT = 2;

    public void fun () {         System.out.println ("External class method");     }     public void testFunctionClass () {         class FunctionClass {             private void fun () {                 System.out.println ("Local internal class output");                 System .out.println (STATIC_INT);                 System.out.println (noStaticInt);                 System.out.println (params);                 // params ++; // params est immuable donc cette phrase se compile de manière incorrecte             }         }         FunctionClass functionClass = new FunctionClass ( );         functionClass.fun ();     } } Classe interne anonyme Une classe interne anonyme n'a pas de modificateur d'accès. Une classe interne anonyme doit hériter d'une classe abstraite ou implémenter une interface


    

















Il ne peut y avoir aucun membre statique ni aucune méthode dans
une classe interne anonyme. Une classe interne anonyme n'a pas de méthode de construction car elle n'a pas de nom de classe.
Comme les classes internes locales, les classes internes anonymes peuvent également référencer des variables locales. Cette variable doit également être déclarée en tant que
classe publique finale Button {     public void click (final int params) {         // Classe interne anonyme, qui implémente l'interface         ActionListener new ActionListener () {             public void onAction () {                 System.out.println (" click action ... "+ params);             }         } .onAction ();     }     // La classe interne anonyme doit hériter ou implémenter une interface existante interface     publique ActionListener {         public void onAction ();     }











    public static void main (String [] args) {         Button button = new Button ();         button.click ();     } } Pourquoi les variables locales doivent-elles être finalisées?




Parce que le cycle de vie des variables locales et des classes internes anonymes est différent.

La classe interne anonyme est stockée dans le tas après sa création et les variables locales de la méthode sont stockées dans la pile Java. Lorsque la méthode est exécutée, la pile est désempilée et les variables locales disparaissent.

Ensuite, la classe interne anonyme peut encore être stockée dans le tas à ce moment, alors où la classe interne anonyme doit-elle trouver cette variable locale?

Pour résoudre ce problème, le compilateur nous aide automatiquement à créer une sauvegarde des variables locales dans la classe interne anonyme. C'est-à-dire que même si l'exécution de la méthode se termine, il y a toujours une sauvegarde dans la classe interne anonyme. Naturellement, nous n'ont pas peur de le trouver.

Mais le problème est revenu.

Si le a dans la variable locale ne cesse de changer.
Alors ne voulez pas que la sauvegarde d'une variable change tout le temps.
Afin de garder les variables locales cohérentes avec le champ de sauvegarde dans la classe interne anonyme.
Le compilateur doit stipuler que ces champs locaux doivent être des constantes, et une fois qu'ils sont affectés, ils ne peuvent plus être modifiés.

Donc, la raison pour laquelle le domaine de la méthode externe de la classe interne anonyme doit être le domaine constant.

Attention particulière
En Java8, les restrictions de modification sur final ont été supprimées, mais en fait, tant qu'elle est utilisée dans une classe interne anonyme, la variable deviendra automatiquement définitive (ne peut être utilisée, pas assignée).

Problèmes qui peuvent être causés par
des classes internes dans le développement réel. Les classes internes peuvent provoquer des fuites de mémoire dans le programme. Je
pense que les amis Android ne seront pas familiers avec cet exemple. Le gestionnaire que nous utilisons souvent nous affichera constamment de tels avertissements.

Voyons d'abord pourquoi les classes internes provoquent des fuites de mémoire.

Pour comprendre pourquoi les classes internes provoquent des fuites de mémoire, nous devons comprendre le mécanisme de recyclage de la machine virtuelle java, mais nous n'introduirons pas le mécanisme de recyclage de la mémoire java en détail ici. Nous avons seulement besoin de comprendre le mécanisme de recyclage de la mémoire java grâce à l '«analyse accessible " atteindre.

Autrement dit, la machine virtuelle Java utilise le mécanisme de recyclage de la mémoire pour déterminer si les références sont accessibles, et si elles ne le sont pas, elles seront recyclées à un moment donné.

Dans quelles circonstances les classes internes peuvent-elles provoquer des fuites de mémoire?

Si une classe interne anonyme n'est détenue par aucune référence, alors l'objet de classe interne anonyme a une chance d'être recyclé.

Si la classe interne n'est référencée que dans la classe externe, lorsque la classe externe n'est plus référencée, la classe externe et la classe interne peuvent être recyclées par le GC.

Si la référence de la classe interne est référencée par d'autres classes autres que la classe externe, cela empêchera la classe interne et la classe externe d'être recyclées par le GC, même si la classe externe n'est pas référencée, car la classe interne class contient une référence à la classe externe).

classe publique ClassOuter {

    Object object = new Object () {         public void finalize () {             System.out.println ("inner Libérez la mémoire occupée ...");         }     };



    public void finalize () {         System.out.println ("Outer Libère la mémoire occupée ...");     } }


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






    private static void Test () lance InterruptedException {         System.out.println ("Début du programme.");

        ClassOuter externe = nouveau ClassOuter ();
        Object object = external.object;
        externe = nul;

        System.out.println ("Exécuter GC");
        System.gc ();

        Thread.sleep (3000);
        System.out.println ("Fin du programme.");
    }
} Le
programme en cours d'exécution constate que la récupération de mémoire ne récupère pas l'objet objet.
En effet, même si la classe externe n'est référencée par aucun variable, tant que sa classe interne La classe est détenue par des variables autres que la classe externe, et la classe externe ne sera pas collectée par le GC.

Nous devons prêter une attention particulière à la situation où la classe interne est référencée par d'autres classes extérieures, ce qui rend la classe externe impossible à libérer, ce qui peut facilement conduire à des fuites de mémoire.

Lorsque Hanlder est utilisé comme classe interne dans Android, ses objets sont détenus par la file d'attente de messages envoyée par Hanlder dans la file d'attente de messages MessageQueue contrôlée par le Looper du thread principal du système (bien sûr, c'est aussi le Looper créé manuellement par le thread enfant). Lorsqu'il y a un grand nombre de files d'attente de messages Lorsque le traitement du message doit être traité ou que le message retardé doit être exécuté, l'activité qui a créé le gestionnaire s'est fermée et l'objet Activity ne peut pas être libéré, ce qui provoque une fuite de mémoire.
Quand Hanlder sera-t-il libéré? Lorsque la file d'attente de messages aura fini de traiter le message transporté par Hanlder, elle appellera msg.recycleUnchecked () pour libérer la référence Handler détenue par le Message.

Pour gérer les fuites de mémoire Hanlder dans Android, vous pouvez partir de deux aspects:

Après avoir fermé le onDestry d'Activité / Fragment, annulez le Message qui est toujours en file d'attente:

mHandler.removeCallbacksAndMessages (null);
1
Créer Hanlder en tant que classe interne statique et utiliser une référence souple

La
   classe statique privée mHandler MyHandler étend Handler {

        privé final WeakReference <MainActivity> mActivity;

        public MyHandler (activité MainActivity) {             mActivity = new WeakReference <MainActivity> (activité);         }

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






Je suppose que tu aimes

Origine blog.csdn.net/u013804636/article/details/107066187
conseillé
Classement