Detailed explanation of Java internal classes

Detailed explanation of Java internal classes

Speaking of the word "internal class", it must be familiar to many people, but they will feel unfamiliar. The reason is that there are few scenarios that may be used when writing code. It is most used when there is event listening, and it is rarely used to summarize the usage of internal classes. Today we'll find out. The following is the table of contents for this article:

I Internal class foundation

II Deep understanding of inner classes

III Usage scenarios and benefits of internal classes

IV Common written interview questions related to internal class

If there is anything wrong, please understand and welcome criticism and correction.

Please respect the author's labor achievements. Please indicate the original link for Reprint:

   http://www.cnblogs.com/dolphin0520/p/3811445.html

I Internal class foundation

In Java, a class can be defined in another class or a method. Such a class is called an inner class. In a broad sense, internal classes generally include these four types: member internal classes, local internal classes, anonymous internal classes and static internal classes. Let's take a look at the usage of these four internal classes.

  1. Member inner class

Member inner class is the most common inner class. Its definition is located inside another class, as shown in the following form:

class Circle { double radius = 0;

public Circle(double radius) {
    this.radius = radius;
}

class Draw {     //内部类
    public void drawSahpe() {
        Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println("drawshape");
    }
}

}In this way, the class draw looks like a member of the class circle, which is called the external class. The inner class of a member can unconditionally access all member properties and member methods (including private members and static members) of the outer class.

class Circle { private double radius = 0; public static int count =1; public Circle(double radius) { this.radius = radius; }

class Draw {     //内部类
    public void drawSahpe() {
        Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(radius);  //外部类的private成员
        Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(count);   //外部类的静态成员
    }
}

}However, it should be noted that when the internal class of a member has a member variable or method with the same name as the external class, hiding will occur, that is, the members of the internal class of the member are accessed by default. If you want to access a member of an external class with the same name, you need to access it in the following form:

External class this. Member variable external class this. Although the inner class of the member method can unconditionally access the members of the outer class, the outer class doesn't want to access the members of the inner class. In the external class, if you want to access the members of the internal class of a member, you must first create an object of the internal class of the member, and then access it through a reference to this object:

class Circle { private double radius = 0;

public Circle(double radius) {
    this.radius = radius;
    getDrawInstance().drawSahpe();   //必须先创建成员内部类的对象,再进行访问
}

private Draw getDrawInstance() {
    return new Draw();
}

class Draw {     //内部类
    public void drawSahpe() {
        Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(radius);  //外部类的private成员
    }
}

}The internal class of a member is dependent on the external class, that is, if you want to create an object of the internal class of a member, the premise is that there must be an object of the external class. The general way to create an internal class object of a member is as follows:

Public class test {public static void main (string [] args) {/ / the first method: outer outer = new outer(); outer.inner inner = outer.new inner(); / / it must be created through the outer object

    //第二种方式:
    Outter.Inner inner1 = outter.getInnerInstance();
}

}

class Outter { private Inner inner = null; public Outter() {

}

public Inner getInnerInstance() {
    if(inner == null)
        inner = new Inner();
    return inner;
}

class Inner {
    public Inner() {

    }
}

}Internal classes can have private access, protected access, public access and package access. For example, in the above example, if the inner class of a member is modified with private, it can only be accessed inside the external class. If it is modified with public, it can be accessed anywhere; If it is decorated with protected, it can only be accessed in the same package or inherited from an external class; If it is the default access permission, it can only be accessed under the same package. This is a little different from external classes. External classes can only be modified by public and package access permissions. I personally understand that because the internal class of a member looks like a member of an external class, it can have a variety of permissions like a member of a class.

  2. Local inner class

A local internal class is a class defined in a method or scope. The difference between it and a member internal class is that the access of a local internal class is limited to the method or scope.

class People{ public People() {

}

}

class Man{ public Man(){

}

public People getWoman(){
    class Woman extends People{   //局部内部类
        int age =0;
    }
    return new Woman();
}

}Note that a local internal class is like a local variable in a method. It cannot have public, protected, private and static modifiers.

  3. Anonymous Inner Class

Anonymous inner classes should be used most often when we write code. Using anonymous inner classes when writing event listening code is not only convenient, but also makes the code easier to maintain. The following code is an Android event listening Code:

scan_ bt.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

        }
    });

    history_bt.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

        }
    });

This code sets the listener for the two buttons, which uses the anonymous inner class. In this Code:

new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

        }
    }

Is the use of anonymous inner classes. The code needs to set a listener object for the button. Using an anonymous inner class can generate a corresponding object when implementing the methods in the parent class or interface, but only if the parent class or interface exists first. Of course, it is also possible to write in the following way, which is the same as using anonymous inner classes above.

private void setListener() { scan_bt.setOnClickListener(new Listener1()); history_bt.setOnClickListener(new Listener2()); }

class Listener1 implements View. OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub

}

}

class Listener2 implements View. OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub

}

}Although this writing method can achieve the same effect, it is lengthy and difficult to maintain. Therefore, the method of anonymous inner class is generally used to write event monitoring code. Similarly, anonymous inner classes cannot have access modifiers and static modifiers.

Anonymous inner classes are the only classes without constructors. Because it has no constructor, the scope of use of anonymous inner classes is very limited. Most anonymous inner classes are used for interface callbacks. The anonymous inner class is automatically named outer $1 by the system when compiling class。 Generally speaking, anonymous inner classes are used to inherit other classes or implement interfaces. There is no need to add additional methods, but only the implementation or rewriting of inherited methods.

  4. Static inner class

Static internal classes are also classes defined in another class, but there is a keyword static in front of the class. Static internal classes do not need to depend on external classes. This is similar to the static member properties of classes, and it cannot use non static member variables or methods of external classes. This is well understood because objects of static internal classes can be created without objects of external classes. If non static members of external classes are allowed to access, there will be a contradiction, Because non static members of external classes must be attached to specific objects.

public class Test { public static void main(String[] args) { Outter.Inner inner = new Outter.Inner(); } }

class Outter { public Outter() {

}

static class Inner {
    public Inner() {

    }
}

}   

II Deep understanding of inner classes

  1. Why can an inner class unconditionally access members of an outer class?

Before that, we have discussed that the internal class of a member can unconditionally access the members of an external class. How exactly is it implemented? Let's decompile the bytecode file to see what happened. In fact, when compiling, the compiler will compile the internal classes of members into a bytecode file separately. The following is outer Java code:

public class Outter { private Inner inner = null; public Outter() {

}

public Inner getInnerInstance() {
    if(inner == null)
        inner = new Inner();
    return inner;
}

protected class Inner {
    public Inner() {

    }
}

}After compilation, two bytecode files appear:

Decompile outer $inner The class file gets the following information:

E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$Inner Compiled from "Outter.java" public class com. cxh. test2. Outter$Inner extends java. lang.Object SourceFile: "Outter.java" InnerClass:

24= #1 of #22; // Inner=class com/cxh/test2/Outter$Inner of class com/cxh/tes

t2/Outter minor version: 0 major version: 50 Constant pool: const #1 = class #2; // com/cxh/test2/Outter$Inner const #2 = Asciz com/cxh/test2/Outter$Inner; const #3 = class #4; // java/lang/Object const #4 = Asciz java/lang/Object; const #5 = Asciz this$0; const #6 = Asciz Lcom/cxh/test2/Outter;; const #7 = Asciz ; const #8 = Asciz (Lcom/cxh/test2/Outter;) V; const #9 = Asciz Code; const #10 = Field #1.# 11; // com/cxh/test2/Outter$Inner. this$0:Lcom/cxh/t est2/Outter; const #11 = NameAndType #5:#6;// this$0:Lcom/cxh/test2/Outter; const #12 = Method #3.# 13; // java/lang/Object. " ":()V const #13 = NameAndType #7:#14;// " ":()V const #14 = Asciz ()V; const #15 = Asciz LineNumberTable; const #16 = Asciz LocalVariableTable; const #17 = Asciz this; const #18 = Asciz Lcom/cxh/test2/Outter$Inner;; const #19 = Asciz SourceFile; const #20 = Asciz Outter. java; const #21 = Asciz InnerClasses; const #22 = class #23; // com/cxh/test2/Outter const #23 = Asciz com/cxh/test2/Outter; const #24 = Asciz Inner;

{ final com.cxh.test2.Outter this$0;

public com. cxh. test2. Outter$Inner(com.cxh.test2.Outter); Code: Stack=2,Locals=2,Args_ size=2 0: aload_ 0 1: aload_ 1 2: putfield #10; // Field this$0:Lcom/cxh/test2/Outter; 5: aload_ 0 6: invokespecial #12; // Method java/lang/Object. " ":()V 9: return LineNumberTable: line 16: 0 line 18: 9

LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/cxh/test2/Outter$Inner;

}Lines 11 to 35 are the contents of the constant pool. Here are the contents of line 38 one by one:

final com. cxh. test2. Outter this$0; This line is a pointer to an external class object. You must be enlightened when you see here. In other words, the compiler will add a reference to the external class object for the internal class of the member by default. How is this reference initialized? Next, look at the constructor of the inner class:

public com. cxh. test2. Outter$Inner(com.cxh.test2.Outter); It can be seen from here that although the constructor of the internal class we are defining is a parameterless constructor, the compiler will add a parameter by default. The type of the parameter is a reference to the external class object, so the outer this&0 pointer in the internal class of the member points to the external class object. Therefore, the members of the external class can be accessed freely in the internal class of the member. It also indirectly shows that the inner class of the member depends on the outer class. If the object of the outer class is not created, the outer this&0 reference cannot be initialized and assigned, and the object of the inner class of the member cannot be created.

  2. Why can local inner classes and anonymous inner classes only access local final variables?

This problem must have bothered many people. Before discussing this problem, look at the following code:

public class Test { public static void main(String[] args) {

}

public void test(final int b) {
    final int a = 10;
    new Thread(){
        public void run() {
            Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(a);
            Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(b);
        };
    }.start();
}

}This code will be compiled into two class files: test Class and test $1 class。 By default, the compiler names anonymous inner classes and local inner classes outer $X.class (x is a positive integer).

According to the above figure, the name of the anonymous inner class in the test method is called test $1.

In the previous code, if you remove any final in front of variables A and B, this code will not compile. Let's consider this question first:

After the test method is executed, the life cycle of variable a ends. At this time, the life cycle of thread object is likely not to end, so it is impossible to continue to access variable a in the run method of thread, but what should we do to achieve this effect? Java uses replication to solve this problem. Decompile the bytecode of this code to obtain the following contents:

We see an instruction in the run method:

The bipush 10 instruction indicates that the operand 10 is pushed on the stack, indicating that a local variable is used. This process is performed by the compiler by default during compilation. If the value of this variable can be determined during compilation, By default, the compiler will add a literal with equal content to the constant pool of the anonymous inner class (local inner class) or directly embed the corresponding bytecode into the execution bytecode. In this way, the variable used by the anonymous inner class is another local variable, but the value is equal to the value of the local variable in the method, so it is completely independent from the local variable in the method.

Here is another example:

public class Test { public static void main(String[] args) {

}

public void test(final int a) {
    new Thread(){
        public void run() {
            Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(a);
        };
    }.start();
}

}Decompile to get:

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

That is, if the value of the local variable can be determined during compilation, a copy will be created directly in the anonymous internal. If the value of the local variable cannot be determined during compilation, the copy is initialized and assigned by passing parameters to the constructor.

As can be seen from the above, the variable a accessed in the run method is not the local variable a in the test method at all. In this way, the inconsistent life cycle problem mentioned above is solved. But the new problem comes again. Since the variable a accessed in the run method and the variable a in the test method are not the same variable, what happens when the value of variable a is changed in the run method?

Yes, it will cause data inconsistency, which will not meet the original intention and requirements. In order to solve this problem, the java compiler restricts the variable a to the final variable and does not allow changes to the variable a (for variables of reference type, it is not allowed to point to new objects). In this way, the problem of data inconsistency can be solved.

Here, you must know why the local variables and formal parameters in the method must be qualified with final.

  3. Is there anything special about static inner classes?

As you can see from the above, static internal classes do not depend on external classes, that is, you can create objects of internal classes without creating external class objects. In addition, static internal classes do not hold references to external class objects. The reader can try to decompile the class file by himself. There is no outer this&0 reference.

III Usage scenarios and benefits of internal classes

Why do you need inner classes in Java? To sum up, there are mainly the following four points:

  1. Each internal class can independently inherit the implementation of an interface, so whether the external class has inherited the implementation of an interface has no impact on the internal class. Internal classes make the multi inheritance solution complete,

  2. It is convenient to organize classes with certain logical relationships together and hide them from the outside world.

  3. Easy to write event drivers

  4. Easy to write threaded code

Personally, the first point is one of the most important reasons. The existence of internal classes makes the multi inheritance mechanism of Java more perfect.

IV Common written interview questions related to internal class

1. Fill in the code at (1), (2) and (3) according to the notes

Public class test {public static void main (string [] args) {/ / initialize bean1 (1) bean1. I + +; / / initialize bean2 (2) bean2. J + +; / / initialize bean3 (3) bean3. K + +;} class Bean1{ public int I = 0; }

static class Bean2{
       public int J = 0;
}

}

class Bean{ class Bean3{ public int k = 0; } } It can be seen from the above that for the internal class of a member, the instantiated object of the external class must be generated before the instantiated object of the internal class can be generated. The static inner class can generate the instantiated object of the inner class without generating the instantiated object of the outer class.

The general form of creating static internal class objects is: external class name Internal class name XXX = new external class name Internal class name ()

The general form of creating internal class objects of members is: external class name Internal class name XXX = external class object name New inner class name ()

Therefore, the codes at (1), (2) and (3) are:

Test test = new test();

  Test. Bean1 bean1 = test. new Bean1();

Test. Bean2 b2 = new Test. Bean2();

Bean bean = new Bean();

Bean. Bean3 bean3 = bean. new Bean3(); 2. What is the output result of the following code?

public class Test { public static void main(String[] args) { Outter outter = new Outter(); outter.new Inner().print(); } }

Class outer {private int a = 1; class inner {private int a = 2; public void print() {int a = 3; system.out.println ("local variable:" + a); system.out.println ("internal class variable:" + this. A); system.out.println ("external class variable:" + outer. This. A);}}}

3 2 1

Finally, add a little knowledge: about the inheritance of classes within members. In general, inner classes are rarely used as inheritance. However, when it is used for inheritance, two points should be noted:

1) the reference method of the inner class of the member must be outer Inner.

2) the constructor must have a reference to the external class object, and call super () through this reference. This code is extracted from Java programming ideas

class WithInner { class Inner{

}

} class InheritInner extends WithInner. Inner {

// InheritInner() 是不能通过编译的,一定要<a href="https://www.jb51.cc/tag/jiashang/" target="_blank" class="keywords">加上</a>形参 
InheritInner(WithInner wi) { 
    wi.<a href="https://www.jb51.cc/tag/super/" target="_blank" class="keywords">super()</a>; //必须有这句<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>
} 

public static void main(String[] args) { 
    WithInner wi = new WithInner(); 
    InheritInner obj = new InheritInner(wi); 
} 

}

reference material:

Java programming ideas

   http://www.cnblogs.com/chenssy/p/3388487.html

   http://blog.csdn.net/zhangjg_blog/article/details/20000769

   http://blog.csdn.net/zhangjg_blog/article/details/19996629

   http://blog.csdn.net/zhaoqianjava/article/details/6849812

   http://www.cnblogs.com/nerxIoUs/archive/2013/01/24/2875649.html

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>