In-depth understanding of Java internal classes
An internal class is another class defined within a class and is a subordinate relationship. Until I actually understand internal classes, I'm always confused as to why I need to define another class within a class, doesn't that add complexity to the code structure? It is only now that I can roughly see that the advantages of this design outweigh its disadvantages. For example, we can solve the problem of single inheritance of classes through internal classes, where classes that cannot be inherited anymore by external classes can be given to internal classes to inherit. We can achieve better encapsulation by defining internal classes to make a class private to a class. As we describe the details next, this article focuses on learning about internal classes from several different perspectives by introducing the definition of the four different types of internal classes, the creation of instances, the principles of internal implementation, and usage scenarios.
one、static internal class The definition of a static internal class is the same as the definition of an ordinary static variable or static method, using the static keyword, except that this time static is modified on the class, in general, only static internal classes are allowed to use the static keyword modification, the definition of ordinary classes can not be modified with the static keyword, this point needs to be noted. The following defines a static internal class.
public class Out { private static String name; private int age; public static class In{ private int age; public void sayHello(){ System.out.println("my name is : "+name); //-- compile with errors--- //System.out.println("my age is :"+ age); } } }
In the above code, the class In is a static internal class. We say that internal classes are allowed to access private fields and private methods of external classes, and for static internal classes, it follows the principle of consistency that only static members of external classes can be accessed. In the above code, the non-static private field age of the external class is made inaccessible in the static internal class, while the static field name is accessible. Here we see, how to create an instance object of a static internal class.
public static void main(String [] args){ Out.In innerClass = new Out.In(); innerClass.sayHello(); }
The creation of an instance object of a static internal class is still relatively clean, unlike a member internal class, which does not need to be associated with an external class instance (the details of which are described below), and we will look at another piece of code below.
public class Out { private static String name; public static class In{ public void sayHello(){ System.out.println(name); showName(); } } private static void showName(){ System.out.println(name); } }
The above code accesses the static members of the outer class twice in the inner class, the first time to the static field name and the second time to the static method showName. Before we decompile this class, the first thing to know is that the concept of so-called inner classes only appears at the compilation stage, there is no such concept as inner classes for the jvm layer. That is, the compiler will compile a class into a source file, and the same is true for an internal class, which will be abstracted from its outer class, add some links to the outer class, and then be compiled into a separate source file. Here's what the compiler does after we compile and run it first, using Dj to decompile the class file.
// This is ours.Out external category public class Out { // Omitted some unimportant parts private static void showName() { System.out.println(name); } private static String name; static String access$000(){return name;} static void access$100(){showName();} }
// This is our internal class public static class Out$In { public void sayHello() { System.out.println(Out.access$000()); Out.access$100(); } public Out$In() { } }
I'm sure you can already see some sort of connection between the two, and the compiler will compile the class Out into two separate class source files. For all private members in Out (that is, members that cannot be accessed after the inner class is separated out), the access$xxx method is added that can be called, thus enabling the connection between the inner class and the outer class. That's what they are.
As for the usage scenarios, generally speaking, For cases that are closely linked to an external class but do not depend on an instance of the external class, Consider defining it as a static internal class。 Here we look at the slightly more complex Member internal class。
2、 Member internal class We said it., Each of the four different types of internal classes has its own usage scenarios, Static inner classes are suitable for situations where they are closely related to the outer class but do not depend on the outer class instance。 But for cases where an instance of an external class needs to be associated with, Optionally, the internal class can be defined as Member internal class。 The following code defines a simple Member internal class:
public class Out { private String name; public void showName(){ System.out.println("my name is : "+name); } public class In{ public void sayHello(){ System.out.println(name); Out.this.showName(); } } }
The above defines a simple internal class In where our member internal class has direct access to the member fields and member methods of the external class, since it is associated with an external class instance. Here we see how an instance of that inner class is created externally.
public static void main(String [] args){ Out out = new Out(); Out.In in = out.new In(); in.sayHello(); }
owing to Member internal class is associated with a concrete instance of an external class, So its instance creation is necessarily by an external class instance。 For the creation of instances, We just need to remember., Member internal class The creation of an instance of requires the association of an external class instance object, Static internal class instances are relatively simple to create。 Here's a look at how the compiler keeps internal classes accessible to external class member information during the compilation phase。
// decompiledOut external category source code (computing) public class Out { // Omitting some non-core code public void showName() { System.out.println((new StringBuilder()).append("my name is : ").append(name).toString()); } private String name; static String access$000(Out o){return o.name;} }
// Decompiled internal classesIn source code (computing) public class Out$In { public void sayHello() { System.out.println(Out.access$000(Out.this)); showName(); } final Out this$0; public Out$In() { this.this$0 = Out.this; super(); } }
From the above code we can actually know that, When we create an instance of an internal class using an instance of an external class, will pass the external class instance as an initial resource into the internal class constructor。 This way we can access all the member information of the external class through that instance, Including private members。( Explicitly adds exposure methods)
As for the usage scenarios, For the kind of cases where there is a high dependency on external class instances, Define a Member internal class It would have been wiser.。
three、 method-internal class method-internal class, as the name implies, Classes defined inside a method。 method-internal class Relatively more complex, The following defines a method-internal class:
public class Out { private String name; public void sayHello(){ class In{ public void showName(){ System.out.println("my name is : "+name); } } In in = new In(); in.showName(); } }
We define a class, Another method is defined in this classsayHello, However, in that method we define an internal class, kindIn Just a method-internal class。 Our method-internal class does not exceed the life cycle of the method containing it, that is say, method-internal class Can only be used in methods。 So in the declaration, Any access modifier is meaningless, thusJava Simply do not allow any access modifiers to modify the method-internal class。 One of the other things to note is that, Definition and use are two different things, Don't look at that big string of code defining the class, You actually want to use the class, would have tonew targets, And for method-internal class with regard (preceding phrase), Only within the methodnew targets。 Here it is. method-internal class A brief introduction to the, Let's see how it is implemented。
The implementation principle of the method inner class is actually not too different from that of the member inner class, which also passes an instance of the outer class to the inner class when it is initialized. It's in the fact that the method inner class is defined inside the concrete method, so in addition to the class being able to access the fields and methods in the outer class through the external instance passed in, the parameters being passed in for the method containing it are also initialized to the inner class along with the outer class instance.
There's no doubt about it., method-internal class The encapsulation is more complete than either of the previously described。 So generally only when a high degree of encapsulation is required will the class be defined as method-internal class。
four、 anonymous internal class Probably the largest of all the classifications of internal classes, the anonymous internal class is the most commonly known and the one we use most often, mostly in functional programming, lambda expressions, etc. Let's focus on this anonymous internal class.
anonymous internal class It's an internal class without a name, While the definition is being completed, Instance is also created, Often andnew The key words are closely aligned。 definitely, It is also not limited to the class, It can also be an interface , Can appear in any position。 Below we define a anonymous internal class:
// First define a general class public class Out { private String name; private void sayHello(){ System.out.println("my name is :" + name); } }
// Define and use an anonymous internal class public static void main(String [] args){ Out out = new Out(){ @Override public void sayHello(){ System.out.println("my name is cyy"); } public void showName(){ System.out.println("hello single"); } }; out.sayHello(); }
From the above code it is obvious for us to see that our anonymous internal class must depend on a parent class, because it is nameless and cannot be represented by a concrete type. So anonymous internal classes are often defined by inheriting from a parent class and overriding or redeclaring some members to implement an anonymous internal class. It still actually utilizes the principle of the Richter transformation.
We can also see from this that the completion of an anonymous internal class definition implies the completion of the creation of an instance of that internal class. Let's see how it is implemented.
// decompiled anonymous internal classes static class Test$1 extends Out { Out out; public void sayHello() { System.out.println("my name is cyy"); } Test$1(Out o) { this.out = o; } }
In fact, after looking at the rationale for the three internal classes mentioned above, instead, I think the implementation of anonymous internal classes is simpler. The main idea is still to abstract out the inner class and pass in an instance of the outer class by initializing it in order to achieve access to all members of the outer class. It's just that in an anonymous inner class, the parent class being relied on is not his outer class. The main features of anonymous internal classes are that there is no name, the object can only be used once, and it can appear in any location. So it's use case is also crying out for anonymous internal classes to be preferred for some cases where code simplicity is required.
The above completes a brief introduction to the four internal classes, and the principles for each of their implementations have been described. It's actually roughly the same, as jvm requires a separate source file for each class, so the compilation phase completes the separation, but in the process of separation and maintaining this connection between internal and external classes, so the compiler adds some interfaces to maintain this structure of information sharing. The use of internal classes can greatly increase the encapsulation of the program, making the code overall more concise.
This article is mainly from the study of multiple articles, Summary of shortcomings, Hope notes!