The role and use of internal classes in java

Original address: https://blog.csdn.net/u013728021/article/details/87358517

Article Directory
The role of the internal class
1. Unconditional access to all elements of the outer class
2. Realization of hiding
3. Multiple inheritance can be realized
4. Simple interface realization through anonymous inner classes to optimize
the relationship between the
inner class and the outer class Classification of the inner class
The difference between static inner classes and non-static inner classes
Local inner classes
Anonymous inner classes
Problems that may be caused by inner classes in actual development
Inner classes can cause program memory leaks

What is an inner class: a class defined in a
class The role of
an inner class Why do we need an inner class? Or why do internal classes exist? The main reasons are as follows:

Inner class methods can access the data in the scope of the class definition, including private data modified by private.
Inner classes can be hidden from other classes in the same package.
Inner classes can solve the defects of java single inheritance.
When we want to define a callback When the function does not want to write a lot of code, we can choose to use an anonymous inner class to implement it.
Examples are as follows:
1. All elements of the outer class can be accessed unconditionally.
Why can we reference:

Although the inner class and the outer class are written in the same file, after the compilation is completed, their respective class files are generated, and the inner class accesses the members of the outer class through this.

1 The compiler automatically adds a member variable to the inner class. The type of this member variable is the same as the type of the outer class. This member variable is a reference to the outer class object (this);

2 The compiler automatically adds a parameter to the construction method of the inner class. The type of the parameter is the type of the outer class. This parameter is used in the construction method to assign values ​​to the member variables added in the inner class;

3 When calling the constructor of the inner class to initialize the inner class object, the reference of the outer class will be passed in by default.

Compilation instruction javac classpath (path to .java file)
Decompilation instruction javap -v (detailed information) classpath (path to .class file)

/**
 * The inner class unconditionally accesses the outer class element
 */
public class DataOuterClass {

    private String data = "External Class Data";

    private class InnerClass {

        public InnerClass() {

            System.out.println(data);

        }

    }

    public void getInner() {

        new InnerClass();

    }

    public static void main(String[] args) {

        DataOuterClass outerClass = new DataOuterClass();

        outerClass.getInner ();

    }

}
Print

External class data
1
data This is a private variable defined in DataOuterClass. This variable can be accessed unconditionally in the inner class.

2. Realization of hiding
The second benefit of inner classes is actually very obvious. We all know that outer classes, that is, ordinary classes, cannot be decorated with private protected access authority symbols, while inner classes can be decorated with private and protected. When we use private to decorate the inner class, this class is hidden from the outside. This does not seem to have much effect, but when the inner class implements an interface, it is undergoing an upward transformation, which completely hides the implementation of the interface from the outside.

interface

public interface InnerInterface {

    void innerMethod();

}
Concrete class

/**
 * Realize information hiding
 */
public class OuterClass {

    /**
     * private modifies internal classes to realize information hiding
     */
    private class InnerClass implements InnerInterface {

        @Override
        public void innerMethod() {             System.out.println("Realize inner class hiding");         }

    }

    public InnerInterface getInner() {

        return new InnerClass();

    }

}

Invoke the program

public class Test {

    public static void main(String[] args) {

        OuterClass outerClass = new OuterClass();

        InnerInterface inner = outerClass.getInner();

        inner.innerMethod();

    }

}

Print

Implement inner class hiding
1
From this code, I only know that the getInner() method of OuterClass can return an instance of the InnerInterface interface, but I don’t know how this instance is implemented.

And because InnerClass is private, we can't see the name of this specific class if we don't look at the code, so it can be well hidden.

3. Multiple inheritance can be achieved.
We know that java does not allow the use of extends to inherit multiple classes. The introduction of internal classes can solve this problem well.
I understand that Java can only inherit one class. Anyone who has learned the basic syntax knows that, before there are inner classes, its multiple inheritance method is realized by interfaces. But sometimes there are many inconveniences in using the interface. For example, if we implement an interface, we must implement all the methods in it.
With inner classes, it's different. It can make our class inherit multiple concrete or abstract classes. Such as the following example:

Class One

public class ExampleOne {

    public String name() {

        return "inner";

    }

}
Class two

public class ExampleTwo {

    public int age() {

        return 25;

    }

}
Class Three

public class MainExample {

   /**
    * Inner class 1 inherits ExampleOne
    */
   private class InnerOne extends ExampleOne {

       public String name() {

           return super.name();

       }

   }

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

       public int age() {

           return super.age();

       }

   }

   public String name() {

       return new InnerOne().name();

   }

   public int age() {

       return new InnerTwo().age();

   }

   public static void main(String[] args) {

       MainExample mi = new MainExample();

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

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

   }

}

Look at class three, which implements two internal classes InnerOne and InnerTwo respectively. InnerOne class inherits ExampleOne, and InnerTwo inherits ExampleTwo, so our class three MainExample has the methods and properties of ExampleOne and ExampleTwo. Indirectly realizes multiple inheritance.

4. Optimize simple interface implementation through anonymous inner classes.
I believe everyone is familiar with anonymous inner classes. Our common way of writing click events is like this:
no need to implement OnClickListener objects.

...
    view.setOnClickListener(new View.OnClickListener(){         @Override         public void onClick(){             // ... do XXX...         }     }) ... The relationship between inner class and outer class is for non-static inner class , The creation of the inner class depends on the instance object of the outer class, and the inner class cannot be created without an instance of the outer class







The inner class is a relatively independent entity, and is not an is-a relationship with the outer class

The moment of creating the inner class does not depend on the creation of the outer class

There are two ways to create common inner classes:

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

public class TestInnerClass {     public static void main(String[] args) {         //Create method 1         ClassOuter.InnerClass innerClass = new ClassOuter().new InnerClass();         //Create method 2         ClassOuter outer = new ClassOuter();         ClassOuter. InnerClass inner = outer.new InnerClass();     } } Classification of inner classes Inner classes can be divided into: static inner classes (nested classes) and non-static inner classes. Non-static inner classes can be divided into: member inner classes, method inner classes, and anonymous inner classes.









The difference between static inner class and non-static inner class
Static inner class Non-static inner class
Can static member variables be available? Yes No
Can you access the non-static variables of the outer class? No Yes
Can you access the static variables of the outer class? Yes Is the
creation dependent on external Whether the class is or not,
we can understand these differences through an example:

public class ClassOuter {
    private int noStaticInt = 1;
    private static int STATIC_INT = 2;

    public void fun() {         System.out.println("External class method");     }

    public class InnerClass {         //static int num = 1; At this time, the editor will report an error for non-static inner classes, so they cannot have static members.         public void fun(){             //Non-static members of non-static inner classes can access non-static members of outer classes variable.             System.out.println(STATIC_INT);             System.out.println(noStaticInt);         }     }






    public static class StaticInnerClass {         static int NUM = 1;//Static inner classes can have static members         public void fun(){             System.out.println(STATIC_INT);             //System.out.println(noStaticInt); At this time the editor It will report that non-static variables of external classes cannot be accessed.         }     } }






public class TestInnerClass {     public static void main(String[] args) {         //Non-static inner class creation method 1         ClassOuter.InnerClass innerClass = new ClassOuter().new InnerClass();         //Non-static inner class creation method 2         ClassOuter outer = new ClassOuter();         ClassOuter.InnerClass inner = outer.new InnerClass();         //How to create static inner class         ClassOuter.StaticInnerClass staticInnerClass = new ClassOuter.StaticInnerClass();     } } Local inner class definition











If an inner class is used in only one method, then we can define this class inside the method. This kind of inner class is called a local inner class. Its scope is limited to this method.

There are two points worthy of our attention in the local inner class:

Local inner classes are not allowed to use the access permission modifier public private protected, neither are
local inner classes are completely hidden from the outside, and access is not allowed in other places except the method of creating this class can access it.
The difference between a local inner class and a member inner class is that it can refer to a member variable, but the member must be declared as final, and the value of the variable is not allowed to be modified internally. (This sentence is not accurate, because if it is not a basic data type, it is just not allowed to modify the object pointed to by the reference, and the object itself can be modified)
public class ClassOuter {     private int noStaticInt = 1;     private static int 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 is immutable so this sentence compiles incorrectly             }         }         FunctionClass functionClass = new FunctionClass();         functionClass.fun();     } } Anonymous inner class An anonymous inner class has no access modifier. An anonymous inner class must inherit an abstract class or implement an interface


    

















There can be no static members or methods in
an anonymous inner class. An anonymous inner class has no construction method because it has no class name.
Same as local inner classes, anonymous inner classes can also reference local variables. This variable must also be declared as final
public class Button {     public void click(final int params){         //Anonymous inner class, which implements the ActionListener interface         new ActionListener(){             public void onAction(){                 System.out.println("click action..." + params);             }         }.onAction();     }     //Anonymous inner class must inherit or implement an existing interface     public interface ActionListener{         public void onAction();     }











    public static void main(String[] args) {         Button button=new Button();         button.click();     } } Why do local variables need to be finalized?




Because the life cycle of local variables and anonymous inner classes are different.

The anonymous inner class is stored in the heap after being created, and the local variables in the method are stored in the Java stack. When the method is executed, the stack is unstacked, and the local variables will disappear.

Then the anonymous inner class may still be stored in the heap at this time, so where should the anonymous inner class find this local variable?

In order to solve this problem, the compiler automatically helps us create a backup of local variables in the anonymous inner class. That is to say, even if the method execution ends, there is still a backup in the anonymous inner class. Naturally, we are not afraid of finding it.

But the problem came again.

If the a in the local variable keeps changing.
So don't you want to let the backup a variable change all the time.
In order to keep the local variables consistent with the backup field in the anonymous inner class.
The compiler has to stipulate that these local fields must be constants, and once they are assigned, they can no longer be changed.

So the reason why the domain of the external method of the anonymous inner class must be the constant domain.

Special attention
In Java8, the modification restrictions on final have been removed, but in fact, as long as it is used in an anonymous inner class, the variable will automatically become final (can only be used, not assigned).

Problems that may be caused by
internal classes in actual development. Internal classes can cause memory leaks in the program. I
believe that Android friends will not be unfamiliar with this example. The Handler we often use will prompt us with such warnings all the time.

Let's first look at why internal classes cause memory leaks.

To understand why internal classes cause memory leaks, we must understand the recycling mechanism of the java virtual machine, but we will not introduce the java memory recycling mechanism in detail here. We only need to understand the java memory recycling mechanism through the "reachable Analysis” to achieve.

That is, the Java virtual machine uses the memory recycling mechanism to determine whether the references are reachable, and if they are not reachable, they will be recycled at some point.

So under what circumstances can internal classes cause memory leaks?

If an anonymous inner class is not held by any reference, then the anonymous inner class object has a chance to be recycled.

If the inner class is only referenced in the outer class, when the outer class is no longer referenced, both the outer class and the inner class can be recycled by the GC.

If the reference of the inner class is referenced by other classes other than the outer class, it will cause the inner class and the outer class to be unable to be recycled by the GC, even if the outer class is not referenced, because the inner class holds a reference to the outer class).

public class ClassOuter {

    Object object = new Object() {
        public void finalize() {
            System.out.println("inner Free the occupied memory...");
        }
    };

    public void finalize() {
        System.out.println("Outer Free the occupied memory...");
    }
}

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

    private static void Test() throws InterruptedException {
        System.out.println("Start of program.");

        ClassOuter outer = new ClassOuter();
        Object object = outer.object;
        outer = null;

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

        Thread.sleep(3000);
        System.out.println("End of program.");
    }
} The
running program finds that memory reclamation does not reclaim the object object.
This is because even if the external class is not referenced by any variable, as long as its internal The class is held by variables other than the outer class, and the outer class will not be collected by the GC.

We must pay special attention to the situation where the inner class is referenced by other classes outside, which makes the outer class unable to be released, which can easily lead to memory leaks.

When Hanlder is used as an internal class in Android, its objects are held by the Message queue sent by Hanlder in the MessageQueue Message Queue controlled by the Looper of the main thread of the system (Of course, this is also the Looper manually created by the child thread). When there are a large number of message queues When the message processing needs to be processed, or the delayed message needs to be executed, the Activity that created the Handler has exited, and the Activity object cannot be released, which causes a memory leak.
Then when will Hanlder be released? When the message queue finishes processing the message carried by Hanlder, it will call msg.recycleUnchecked() to release the Handler reference held by the Message.

To deal with Hanlder memory leaks in Android, you can start from two aspects:

After closing the onDestry of Activity/Fragment, cancel the Message that is still queued:

mHandler.removeCallbacksAndMessages(null);
1
Create Hanlder as a static inner class and use soft reference

mHandler
   private static class MyHandler extends Handler {

        private final WeakReference<MainActivity> mActivity;

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

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

Guess you like

Origin blog.csdn.net/u013804636/article/details/107066187