Deep understanding of why inner classes can access members of external classes in Java

Source: Internet
Author: User

Tags: Code link variable assignment output signature translation get analysis class

Introduction to Internal classes

Although Java is a relatively simple programming language, but for beginners, there are many things feel foggy, understanding is not very clear. The inner class is a feature that often confuses Beginners. Even now I think Java is good, but still not very clear. One of the doubts is why are internal class objects accessible to members of external class objects, including member variables and member methods? I have long wanted to explore the characteristics of the inner class, and today finally took the time to study it.

An inner class is a class that is defined inside a class. There are two cases of a class that is defined inside a class: one that is modified by the static keyword, called a static inner class, and one that is not modified by the static keyword, which is an ordinary inner class. The inner classes mentioned below refer to this ordinary inner class that is not decorated with the static Keyword. Although the static inner class is also defined inside the outer class, it is only formally (written) in relation to the outer class, in fact there is no direct relationship between the logic and the outer class. And the general inner class, not only in formal and external classes have a relationship (written in the outer class inside), logically and outside the class has a Connection. This logical relationship can be summed up in the following two points:

1 The creation of an inner class object depends on the outer class object;

2 The inner class object holds a reference to an external class Object.

The second article above explains why members of external classes can be accessed in an inner class. This is because an inner class object holds a reference to an external class Object. But we can't help but ask, why do we hold this quote? Then look down, the answer is Behind.

Get answers by deserializing bytecode


At the source level, we can not see the reason, because Java for the introduction of grammar, omitted a lot of things to write, that is to say, a lot of things should be written in the source code, but for the sake of introduction, do not have to write in the source, the compiler will be compiled with some Code. Now let's see what the Java compiler adds to Us.
first, build a project Testinnerclass for Testing. In this project, for simplicity, the package is not created, so the source code is directly in the default Package. In this project, there is only one simple file below.

?
123456789 public class Outer {    int outerField = 0;        class Inner{        void InnerMethod(){            int i = outerField;        }    }}


The file is simple, so you don't have to introduce it too much. The inner class inner is defined in the external class outer, and the member variable Outerfield of the outer is accessed in the inner method.
Although these two classes are written in the same file, the individual class files are generated when the compilation is Complete:


Our purpose here is to explore the behavior of the inner class, so we only decompile the class file Outer$inner.class of the inner Classes. On the command line, switch to the Project's bin directory and type the following command to decompile the class File:

?
1 javap -classpath . -v Outer$Inner


-classpath. Description in the current directory looking for the class file to Decompile-v plus this parameter output information is more Comprehensive. Includes constant pools and local variable tables within methods, line numbers, access flags, and so On.
Note that if you have a package name, write the fully qualified name of the class file, such as:

?
1 javap -classpath . -v com.baidu.Outer$Inner



The output of the anti-compilation is many, for the sake of space, here we omit the constant Pool. The output information except for the constant pool is given BELOW.

?
1234567891011121314151617181920212223242526272829303132333435363738 {  final Outer this$0;    flags: ACC_FINAL, ACC_SYNTHETIC  Outer$Inner(Outer);    flags:    Code:      stack=2, locals=2, args_size=2         0: aload_0         1: aload_1         2: putfield      #10// Field this$0:LOuter;         5: aload_0         6: invokespecial #12 // Method java/lang/Object."<init>":()V         9: return      LineNumberTable:        line 5: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature               0100thisLOuter$Inner;  void InnerMethod();    flags:    Code:      stack=1, locals=2, args_size=1         0: aload_0         1: getfield      #10// Field this$0:LOuter;         4: getfield      #20// Field Outer.outerField:I         7: istore_1         8: return      LineNumberTable:        line 7: 0        line 8: 8      LocalVariableTable:        Start  Length  Slot  Name   Signature               090thisLOuter$Inner;               811i   I}</init>


First we will see that the first line of information is as Follows:

?
1 finalOuter this$0;


In the inner class outer$inner, there is a member variable with the name this$0, type outer, and the variable is final. In fact, this is called a "reference to an external class object that exists in an inner class object." But when we define this inner class, we don't declare it, so this member variable is added by the Compiler.
Although the compiler adds a reference to an external class when it creates an inner class, how is this reference assigned? After all, he must be assigned a value before it can point to an external class Object. Let's move our attention to the Constructor. The following output is information about the Constructor.

?
123456789101112131415 Outer$Inner(Outer);  flags:  Code:    stack=2, locals=2, args_size=2       0: aload_0       1: aload_1       2: putfield      #10// Field this$0:LOuter;       5: aload_0       6: invokespecial #12// Method java/lang/Object."<init>":()V       9: return    LineNumberTable:      line 5: 0    LocalVariableTable:      Start  Length  Slot  Name   Signature             0100this LOuter$Inner;</init>


We know that if you do not declare a constructor method in a class, the compiler adds a parameterless constructor by Default. But this is not going to work here, because we clearly see that this constructor has a construction method, and the type is Outer. so, The compiler adds a parameter to the constructor of the inner class, and the type of the parameter is the type of the outer class.
Let's take a look at how to use this default added parameter in the construction Parameters. Let's analyze the bytecode of the construction method. Here is the meaning of each line of bytecode:
Aload_0: loads the first reference variable in a local variable table into the operand stack. Here are a few things to Explain. Variables in the local variable table are initialized before the method executes; variables in the local variable table include the parameters of the method; the first variable in the local variable table of the member method is always this; the operand stack is the stack that executes the current Code. So it means: load this reference from the local variable table into the operand stack.

Aload_1:
Loads the second reference variable in a local variable table into the operand stack. The variables that are loaded here are the parameters of the outer type in the construction method.
Putfield #10//Field this$0:louter;
Assigns a value to the specified member variable using the reference variable at the top of the operand stack. The meaning of this is to assign the parameters of the outer type that are passed out to the member variable this$0. This sentence, Putfield bytecode, reveals how this reference variable to an external class object is Assigned.
The following sentence bytecode is not related to the topic discussed in this article, only a brief introduction. The meaning of the following byte code is: Use this reference to invoke the constructor of the parent class (Object) and then Return.
Translated in our familiar form, this inner class and its constructors are a bit like this: (note that this does not conform to the Java syntax, just to illustrate the Problem)

?
12345678 class Outer$Inner{    final Outer this$0;        public Outer$Inner(Outer outer){        this.this$0 = outer;        super();    }}



In this case, it can be thought that when the constructor of the inner class is called to initialize the inner class object, the compiler also passes in the reference to the external class by Default. The invocation form is a bit like this: (note that this does not conform to the Java syntax, just to illustrate the Problem)
vcq9ysfp4m2stcshoydu2stasr/a4lxesw5uzxjnzxrob2s3vbeo1tcjrcc3w87kwcvn4rk/woc1xlpj1lgx5mg/b3v0zxjgawvszkosim/cw+ a1xnfwvdrc673syr7by7fdzsrkx8jnus69+ndqtcsjugo8yni+cgo8chjlignsyxnzpq== "brush:java;" > void Innermethod (); flags:code:stack=1, locals=2, args_size=1 0:aload_0 1:getfield #10//Field this$0:louter; 4:getfield #20//Field outer.outerfield:i 7:istore_1 8:return
GetField #10//Field this$0:louter;
Load member variable this$0 onto the operand stack
GetField #20//Field outer.outerfield:i
Load the member variable Outerfield of the external class into the operand stack using the this$0 reference loaded above
Istore_1
Saves the value of the int type at the top of the operand stack to the second variable in the local variable table (note that the first local variable is occupied by this and the second local variable is i). The int type variable at the top of the operand stack is the Outerfield variable loaded in the previous Step. so, the meaning of this byte code is: use Outerfield for I to assign Value.
The above three steps are how an inner class accesses an external class member through a reference to an external class Object.


Summarize


The article is written here, believing that the reader will have a clear understanding of the whole principle. Here's a summary:
By deserializing the bytecode of the inner class, This article explains how the inner class accesses the members of the outer class object, and in addition, we have some understanding of the Compiler's behavior, and the compiler automatically adds some logic at compile time, which is why we feel confused.
As for how the inner class accesses the members of the external class, it is also very simple after analysis, mainly through the following steps:
1 The compiler automatically adds a member variable to the inner class that has the same type as the outer class, which is a reference to the outer class object;
2 The compiler automatically adds a parameter to the construction method of the inner class, which is the type of the outer class, and is used within the constructor method to assign a value to the member variable added in 1;
3 the reference to the external class is passed in by default when the constructor of the inner class is called to initialize the inner class Object.

Deep understanding of why inner classes can access members of external classes in Java

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

Tags Index: