Java programmer's prerequisite: inner class parsing

Preface

I have sorted out the related knowledge of internal class, which is quite comprehensive and basic. I hope you can learn and progress together.

1, What are inner classes?

In Java, you can put the definition of one class inside the definition of another, which is internal class. The inner class itself is a property of the class, which is consistent with other property definitions.

An example of an inner class:

public class Outer {

    private int radius = 1;
    public static int count = 2;

    public Outer() {
    }

    class inner{
        public void visitOuter() {
            System.out.println("visit outer private member variable:" + radius);
            System.out.println("visit outer static variable:" + count);
        }
    }
}

2, Types of inner classes

Inner classes can be divided into four types: member inner class, local inner class, anonymous inner class and static inner class.

Static inner class

A static class defined within a class is a static internal class.

public class Outer {

    private static int radius = 1;

    static class StaticInner {
        public void visit() {
            System.out.println("visit outer static  variable:" + radius);
        }
    }
}

Static internal class can access all static variables of external class, but not non static variables of external class. The creation method of static internal class is new external class. Static internal class (), as follows:

Outer.StaticInner inner = new Outer.StaticInner();
inner.visit();

Member inner class

A non static class defined within a class and in the member position is a member internal class.

public class Outer {

    private static  int radius = 1;
    private int count =2;
    
     class Inner {
        public void visit() {
            System.out.println("visit outer static  variable:" + radius);
            System.out.println("visit outer   variable:" + count);
        }
    }
}

Member internal classes can access all variables and methods of external classes, including static and non-static, private and public. Member internal class depends on the instance of external class. Its creation method is external class instance. new internal class (), as follows:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit();

Local inner class

The inner class defined in the method is the local inner class.

public class Outer {

    private  int out_a = 1;
    private static int STATIC_b = 2;

    public void testFunctionClass(){
        int inner_c =3;
        class Inner {
            private void fun(){
                System.out.println(out_a);
                System.out.println(STATIC_b);
                System.out.println(inner_c);
            }
        }
        Inner  inner = new Inner();
        inner.fun();
    }
    public static void testStaticFunctionClass(){
        int d =3;
        class Inner {
            private void fun(){
                // System.out.println (out a); compilation error, local class defined in static method cannot access instance variable of external class
                System.out.println(STATIC_b);
                System.out.println(d);
            }
        }
        Inner  inner = new Inner();
        inner.fun();
    }
}

The local class defined in the instance method can access all variables and methods of the external class, and the local class defined in the static method can only access static variables and methods of the external class. The creation method of local internal class: in the corresponding method, new internal class () is as follows:

 public static void testStaticFunctionClass(){
    class Inner {
    }
    Inner  inner = new Inner();
 }

Anonymous Inner Class

Anonymous inner class is an inner class without a name, which is often used in daily development.

public class Outer {

    private void test(final int i) {
        new Service() {
            public void method() {
                for (int j = 0; j < i; j++) {
                    System.out.println("Anonymous Inner Class " );
                }
            }
        }.method();
    }
 }
 //Anonymous inner class must inherit or implement an existing interface 
 interface Service{
    void method();
}

In addition to having no name, anonymous inner classes have the following characteristics:

  • Anonymous inner classes must inherit an abstract class or implement an interface.
  • Anonymous inner classes cannot define any static members and static methods.
  • When the parameter of the method in question needs to be used by an anonymous inner class, it must be declared final.
  • Anonymous inner class cannot be abstract. It must implement all abstract methods of inherited class or implemented interface.

Anonymous inner class creation method:

new class / interface{ 
  //Anonymous inner class implementation part
}

3, Advantages of inner classes

Why do we use inner classes? Because it has the following advantages:

  • An internal class object can access the content of the external class object that created it, including private data!
  • Internal classes are not seen by other classes of the same package, and have good encapsulation;
  • Internal classes effectively implement "multiple inheritance" and optimize the defects of java single inheritance.
  • Anonymous inner classes can easily define callbacks.

An internal class object can access the content of the external class object that created it, including private data!

public class Outer {

    private  int radius = 1;
    
    protected void test(){
        System.out.println("I am an external class method");
    }

    class Inner {
        public void visit() {
            System.out.println("Accessing external class variables" + radius);
            test();
        }
    }
}

We can see that the Inner class can access the private variable radius or method test of the Outer class.

Internal classes are not seen by other classes in the same package, so they have good encapsulation

When the inner class is decorated with private, the class is hidden from the outside. When an internal class implements an interface and makes an upward transformation, for the external, the implementation of the interface has been hidden, which well embodies the encapsulation.

//Interface provided
interface IContent{
    String getContents();
}

public class Outer {
     //Implementation details of private inner class shielding
     private class PContents implements IContent{
         @Override
         public String getContents() {
             System.out.println("Get inner class content");
             return "Internal class content";
         }
     }

    //External supply method
    public IContent getIContent() {
        return new PContents();
    }

    public static void main(String[] args) {
        Outer outer=new Outer();
        IContent a1=outer.getIContent();
        a1.getContents();
    }
}

We can find that the Outer external class provides the getIContent method, uses the internal class to implement the details, uses private to decorate the internal class, shields it, and makes the encapsulation performance of Java incisively and vividly.

Internal classes effectively implement "multiple inheritance" and optimize the defects of java single inheritance.

We know that in the Java world, a class can only have one direct parent, that is, it exists in a single inheritance way. But inner classes make multiple inheritance possible:

  • Generally speaking, the inner class inherits a class or implements an interface, and the code operation of the inner class creates the object of its outer class. Inner classes provide some kind of window into their outer classes.
  • Each inner class can inherit from an implementation in the team, so whether the outer class has inherited an implementation or not has no effect on the inner class
  • Interface solves some problems. A class can implement multiple interfaces, and internal classes can inherit multiple non interface types (classes or abstract classes).

A copy comes from the Java programming idea. The warmth of "multi inheritance" implemented by internal classes is as follows:

class D {}
abstract class E{}
class Z extends D {
E makeE(){ return new E() {}; }
}

public class MultiImplementation {
static void takesD(D d) {}
static void takesE(E e) {}
public static void main(String[] args){
Z z = new Z();
takesD(z);
takesE(z.makeE());
}
} 

There is a class D and an abstract class E in the code. Then, class Z inherits D, and inner class construction returns E. Therefore, when you want D or E, Z can cope with the characteristics of "multi inheritance".

Anonymous inner classes can easily define callbacks.

What is callback? Suppose that there are two classes A and B, calling B in B in A, and B in executing A c, then c is called callback function.


Of course, the callback function can also be a function, which is the simplest way to synchronize the callback.
There are many callback application scenarios, such as the event listener in android. Anonymous inner classes can easily define callbacks. Let's see an example

//Define a CallBack interface
public interface CallBack {
    void execute();
}

public class TimeTools {

    /**
     * Test the function call duration by defining the execute method of the CallBack interface
     * @param callBack
     */
    public   void  testTime(CallBack callBack) {
        long  beginTime = System.currentTimeMillis(); //Record start time
        callBack.execute(); ///Perform callback operation
        long  endTime = System.currentTimeMillis(); //Record end time
        System.out.println("[use time]:"  + (endTime - beginTime)); //Print usage time
    }

    public   static   void  main(String[] args) {
        TimeTools tool = new  TimeTools();
        tool.testTime(new  CallBack(){
            //Anonymous inner class, defining execute method
            public   void  execute(){
                TestTimeObject testTimeObject = new TestTimeObject();
                testTimeObject.testMethod();
            }
        });
    }
}

When we call testTime() to measure time, we use anonymous inner class to implement a method, execute(), do things in this method (execute target function), and then return to testTime method after execution, which is a good way to realize the function of test function call time. Obviously, anonymous inner classes make callback implementation easy.

4, Bottom layer of inner class

Internal class identifier

Each inner class produces a. Class file that contains all the information about how to create objects of that type. Inner classes must also generate a. Class file to contain their class object information. There are strict rules for naming inner class files: the name of outer class + $+ the name of inner class.

A simple example:

public class Outer {
    class Inner{
    }
}

After javac Outer.java is compiled, the generated class file is as follows:

If the inner class is anonymous, the compiler simply generates a number as its identifier. If the inner classes are nested in other inner classes (static inner classes), just add their names directly after their outer class flags and "$".

Why can internal classes access members of external classes, including private data?

From the previous section, we know that internal classes can access members of external classes, including private data. So how does it do it? Next, the answer.

Let's start with this simple example:

public class Outer {

    private int i = 0;
    
    class Inner{
        void method(){
            System.out.println(i);
        }
    }
}

An external class Outer, an external class private property i, an internal class Inner, and an internal class method. The Inner class method accesses the Outer class property i.

First compile, javac Outer.java, and generate. class file, as follows:

Javap - classpath. - V outer $inner, decompile the outer $inner.class file to get the following information:

We can see this line, which is a pointer to an external class object:

final innerclass.Outer this$0;

Although the compiler adds a reference to the external class when it creates the internal class, how is the reference assigned? The compiler will add a parameter to the internal class construction method for initialization. The parameter type is the external class type, as follows:

innerclass.Outer$Inner(innerclass.Outer);

The outer this & 0 pointer in the member inner class points to the outer class object, so you can freely access the members of the outer class in the member inner class.

When local inner classes and anonymous inner classes access local variables, why must variables be added with final?

When local inner classes and anonymous inner classes access local variables, why do variables need to be added with final? What is its internal principle?

First look at this Code:

public class Outer {

    void outMethod(){
        final int a =10;
        class Inner {
            void innerMethod(){
                System.out.println(a);
            }

        }
    }
}

Decompile (Outer(Inner) to get the following information

In the inner class innerMethod method method, we can see the following instruction:

3: bipush   10
  • It means that the constant 10 is pushed into the stack, indicating that a local local variable is used.
  • In fact, if the value of a variable can be determined during compilation (determined as 10 in demo), the compiler will add a literal equivalent content in the constant pool of anonymous inner class (local inner class) by default or directly embed the corresponding bytecode into the execution bytecode.
  • Soy sauce purple can ensure that the variables used by the local inner class are distinguished from the outer local variables, which are only equal in value.

In the above example, why add final? Because the life cycle is inconsistent, local variables are directly stored in the stack. When the method execution is finished, non final local variables are destroyed. The reference of local inner class to local variable still exists. If local inner class wants to call local variable, it will make an error. With the addition of final, it can ensure that the variables used by the local internal classes are separated from the external local variables, which solves this problem.

Let's take another look at the code. In fact, we need to move variable a to the parameter passing mode

public class Outer {

    void outMethod(final int a){
        class Inner {
            void innerMethod(){
                System.out.println(a);
            }
        }
    }
}

Decompilation available

We can see that the constructor of the anonymous inner class outer $1iner contains two parameters, one is a reference to an external class object, the other is an int variable. Obviously, here is to pass parameter a in the variable innerMethod method method in the form of parameter to initialize the value of the copy (copy of variable a) in the anonymous inner class.

So, the new problem comes again. Since the variable a accessed in the innerMethod method and the variable a in the outMethod method are not the same variable, what happens when you modify a in the innerMethod method? That will cause data inconsistency.

How to solve it? Using the final modifier, the reference type variable decorated with final is not allowed to point to new objects, which solves the problem of data inconsistency. Note: in Java 8, for local variables referenced by local inner classes, final is added by default, so the final keyword does not need to be added.

5, Application scenario of inner class.

In which scenarios do we usually use inner classes?

Scenario 1: some multi algorithm situations

In some cases where there are many algorithms, internal classes can also be used, such as:

Arrays.sort(emps,new Comparator(){
  Public int compare(Object o1,Object o2)
  {
   return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();
  }
});

Scenario 2: solve some non object oriented statement blocks.

If some statement blocks, including if There are many else statements, case statements and so on, which are not easy to maintain and extend, so we can use the internal class + design pattern to solve them.

Scenario 3: using inner classes properly makes the code more flexible and extensible.

Using inner classes properly can make your code more flexible and extensible. For example, there are many inner classes and elegant codes in the lamda expression of JDK. as follows

// JDK8 Lambda expression writing
new Thread(() -> System.out.println("Thread run()")).start();

Scenario 4: when a class is no longer used by other classes except its external classes.

If a class cannot be used for other classes, or for some reason cannot be referenced by other classes. Then we can consider implementing it as an inner class. Database connection pool is such a typical example.

6, Internal interview questions

Finally, let's look at a classic internal interview question.

public class Outer {
    private int age = 12;

    class Inner {
        private int age = 13;
        public void print() {
            int age = 14;
            System.out.println("Local variable:" + age);
            System.out.println("Internal class variable:" + this.age);
            System.out.println("External class variable:" + Outer.this.age);
        }
    }

    public static void main(String[] args) {
        Outer.Inner in = new Outer().new Inner();
        in.print();
    }

}

Operation result:

Reference and thanks

Personal public address

  • If you are a good child who likes learning, you can pay attention to my public number and study and discuss together.
  • If you think what's wrong with this article, you can comment on it. You can also pay attention to my public number and chat with me.

Tags: Java Programming Android JDK

Posted on Fri, 10 Jan 2020 20:54:01 -0800 by dopey