Speaking of the word internal class, presumably a lot of people are not unfamiliar, but will feel unfamiliar. The reason is that there are not many scenarios that can be used when writing code, most of it is in the case of event monitoring, and it is seldom used to summarize the usage of internal classes.
I. Inner-class basis
In Java, a class can be defined inside another class or inside a method, and such a class is called an inner class. The broad range of internal classes generally includes these four types: member inner class, local inner class, anonymous inner class, and static inner class. Let's take a look at the usage of these four inner classes.
1. member Inner class
The member inner class is the most common inner class, defined as being inside another class, as in the following form:
Class Circle { double radius = 0; Public Circle (double radius) { This.radius = radius; } Class Draw { //internal class public void Drawsahpe () { System.out.println ("Drawshape"); }
This looks like the class draw is a member of the class circle, called the outer class. Member inner classes have unconditional access to all member properties and member methods of the external class, including private and static members.
Class Circle { private double radius = 0; public static int count =1; Public Circle (double radius) { This.radius = radius; } Class Draw { //internal class public void Drawsahpe () { System.out.println (RADIUS); The private member of the external class System.out.println (count); static member of external class }
It is important to note, however, that when a member inner class has a member variable or method with the same name as an external class, there is a hidden phenomenon, that is, by default, members of the members inner class are accessed. If you want to access a member of the same name as an external class, you need to access it in the following form:
Although members of the inner class can access the members of an external class unconditionally, the external class would like to access members of the member's inner class but not so much. In an external class, if you want to access members of a member's inner class, you must first create an object of the member's inner class, and then use a reference to that object to visit:
Class Circle { private double radius = 0; Public Circle (double radius) { This.radius = radius; Getdrawinstance (). DRAWSAHPE (); You must first create an object of the member's inner class, and then access the private Draw Getdrawinstance () { return new Draw (); } Class Draw { //internal class public void Drawsahpe () { System.out.println (RADIUS); Private member of external class }
A member inner class exists that is dependent on an external class, that is, if you want to create an object of the member's inner class, as long as an object of an outer class must exist. The general way to create a member's inner class object is as follows:
public class Test {public static void main (string[] args) {//First way: outter outter = new Outter (); Outter.inner Inner = Outter.new Inner (); Must be created through the Outter object//Second way: 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, if the member inner class inner is modified with private, it can only be accessed inside the external class, if it is modified with public, it will be accessible anywhere, if it is decorated with protected, it can only be accessed under the same package or inheriting the external class. ; If it is a default access, it can only be accessed under the same package. This is a bit different from the external class, and the outer class can only be decorated with both public and package access permissions. I personally understand that because the member inner class looks like a member of an external class, you can have multiple permissions adornments like members of a class.
2. Local inner class
A local inner class is a class that is defined within a method or scope, and differs from a member's inner class in that the access of a local inner class is limited to within a method or within that scope.
Class people{public people () { }} class man{public Mans () { } public People Getwoman () { Class Woman extends people{ //local inner class int age =0; } return new Woman ();
Note that the local inner class, like a local variable inside a method, cannot have public, protected, private, and static modifiers.
3. Anonymous inner class
Anonymous inner class should be the most used when we write code, it is convenient to use anonymous inner class when writing the code of Event listener, and make the code easier to maintain. The following code is an Android event listener 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 two buttons, where the anonymous inner class is used. In this piece of code:
New Onclicklistener () { @Override public void OnClick (View v) { //TODO auto-generated method stub }
Is the use of anonymous internal classes. The code needs to set the Listener object to the button, using an anonymous inner class to produce a corresponding object at the same time as the method in the parent class or interface, but only if the parent class or interface must exist before it can be used. Of course, such as the following is also possible, with the above using anonymous inner class to achieve the same effect.
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 kind of writing can achieve the same effect, but it is both lengthy and difficult to maintain, it is common to use anonymous inner class methods to write event monitoring code. Similarly, anonymous inner classes cannot have access modifiers and static modifiers.
Anonymous inner classes are the only classes that do not have constructors. Because it does not have a constructor, the use of anonymous internal classes is very limited, and most anonymous inner classes are used for interface callbacks. Anonymous inner classes are automatically named Outter$1.class by the system at compile time. In general, anonymous inner classes are used to inherit other classes or implement interfaces, and there is no need to add additional methods, just implementations of inherited methods or overrides.
4. Static Inner class
A static inner class is also a class that is defined in another class, except that there is a keyword static in front of the class. A static inner class is not dependent on an external class, which is somewhat similar to a static member property of a class, and it cannot use a non-static member variable or method of an external class, because in the absence of an object of an outer class, you can create an object of the static inner class. If you allow access to non-static members of an external class, there is a contradiction because the non-static members of the outer class must be attached to the specific object.
public class Test {public static void Main (string[] args) { Outter.inner Inner = new Outter.inner ()} } class Outter {public outter () { } static class Inner {public Inner () { }
Two. In-depth understanding of internal classes
1. Why does the member inner class have unconditional access to members of the external class?
Before that, we have discussed how the members ' inner classes can access the members of the external class unconditionally, and how exactly is it implemented? Here's a look at what to do by deserializing the bytecode file. In fact, when compiling, the compiler compiles the member internals into a single bytecode file, the following is the code for Outter.java:
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 compiling, two bytecode files appear:
The decompile outter$inner.class file gets the following information:
E:\workspace\test\bin\com\cxh\test2>javap-v Outter$inner Compiled from the "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 PO Ol: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 <init>; Const #8 = Asciz (lcom/cxh/test2/outter;) V; Const #9 = Asciz Code; Const #10 = Field #1. #11;//COM/C XH/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. " <init> ":() V const #13 = NameandtypE #7: #14;//"<init>":() 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. " <init> ":() V 9:return linenumbertable:line 16:0 line 18:9 localvariabletable:start Length Slo T Name Signature 0 0 this lcom/cxh/test2/outter$inner; }
Rows 11th through 35 are the contents of a constant pool, with the contents of line 38th below:
Final Com.cxh.test2.Outter this$0;
This line is a pointer to the outer class object, see here presumably everyone enlightened. This means that the compiler defaults to adding a reference to the Outer class object to the member's inner class, so how does this reference assign an initial value? The following looks at the constructor of the inner class:
Public Com.cxh.test2.outter$inner (Com.cxh.test2.Outter);
As can be seen here, although the constructor of the inner class that we define is the parameterless constructor, the compiler will add a parameter by default, which is a reference to an external class object, so the outter this&0 pointer in the member's inner class points to the Outer class object. Therefore, you can freely access members of external classes within a member's inner class. It is also implied from this that the member inner class is dependent on the outer class, and if no object of the outer class is created, the Outter this&0 reference cannot be initialized and the object of the member's inner class cannot be created.
2. Why are local inner classes and anonymous inner classes accessible only to local final variables?
Presumably this problem has plagued many people, before discussing this issue, look at the following code:
public class Test {public static void Main (string[] args) { } public void Test (final int b) { final I NT A = ten; New Thread () {public void run () { System.out.println (a); System.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 and local inner classes as Outter$x.class (x is a positive integer).
As you know, the name of the anonymous inner class in the Test method is test$1.
In the previous code, if either final of the variables A and B is removed, the code compiles. Let's consider this question first:
When the test method is finished, the life cycle of the variable A is over, and the life cycle of the thread object is probably not over yet, then it becomes impossible to continue accessing the variable A in the thread's Run method, but what about the effect? Java uses the means of replication to solve this problem. The bytecode of this code can be deserialized to get the following content:
We see that there is an instruction in the Run method:
Bipush 10
This instruction indicates that the operand 10 is stacked, indicating that a local local variable is being used. This process is performed by the compiler by default during compilation, and if the value of the variable is determined during compilation, the compiler defaults to adding an equal literal to the constant pool in the anonymous inner class (local inner Class) or directly embedding the corresponding bytecode into the execution bytecode. Thus, the variable used by the anonymous inner class is another local variable, except that the value is equal to the value of the local variable in the method, so it is completely independent of the local variable in the method.
Let's look at an example below:
public class Test {public static void Main (string[] args) { } public void Test (final int a) { new Thr EAD () {public void run () { System.out.println (a);} ; }. Start ();
The anti-compilation gets:
We see that the constructor for anonymous inner class test$1 contains two parameters, one is a reference to an external class object, and the other is an int variable, and it is clear that the parameter a in the variable test method is passed in as a argument to the copy of the anonymous inner class (copy of variable a) for assignment initialization.
It is also said that if the value of a local variable can be determined during compilation, a copy is created directly inside of the anonymous interior. If the value of the local variable cannot be determined during compilation, the copy is initialized by the way the constructor passes the parameter.
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. This solves the previous problem of inconsistent life cycle. But the new problem comes again, since the variable A 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 can result in inconsistent data, which will not achieve the original intent and requirements. To solve this problem, the Java compiler restricts the problem of data inconsistency by restricting the variable A to the final variable, not allowing changes to the variable a (which is not allowed to point to the new object for a variable of the reference type).
Here, you should be aware of why local variables and formal parameters in a method must be qualified with final.
3. Is there a special place for static internal classes?
As you can tell, static inner classes do not depend on external classes, and you can create an object of an inner class without creating an external class object. In addition, the static inner class does not hold a reference to the external class object, this reader can try to decompile the class file himself to see it, there is no outter this&0 reference.
Three. Usage scenarios and benefits of internal classes
Why do I need an internal class in Java? Summarize the following four main points:
1. Each inner class can inherit an implementation of an interface independently, so no matter whether an external class has inherited an implementation of (an interface), there is no effect on the inner class. The inner class makes the multiple-inheritance solution complete,
2. To facilitate the existence of a certain logical relationship of the classes organized together, but also can be hidden from the outside world.
3. Easy to write event drivers
4. Easy to write thread code
Personally feel that 1th is one of the most important reasons, the existence of internal classes makes the Java multi-inheritance mechanism become more perfect.
Four. Common written test questions related to internal classes
1. Complete the code in (1), (2), and (3) according to the comments
public class test{public static void Main (string[] args) { //Initialize Bean1 (1) bean1. i++; Initializes the 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;
As you can know, for a member inner class, you must first produce an instantiated object of the outer class in order to produce an instantiated object of the inner class. The static inner class produces an instantiated object of the inner class without generating an instantiated object of the outer class.
The general form of creating a static inner class object is the external class class name. Inner class class name XXX = new External class class name. Inner class class name ()
The general form of creating a member inner class object is the external class class name. Inner class class Name XXX = External class object name. New Inner class class name ()
Thus, (1), (2), the Code at (3) is:
Test test = new test (); Test.bean1 bean1 = Test.new Bean1 ();
Bean bean = new Bean (); Bean.bean3 bean3 =
2. What is the output of the following code?
public class Test {public static void Main (string[] args) { outter outter = new Outter (); Outter.new Inner (). print (); } class Outter { 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 ("Inner class variable:" + this.a); SYSTEM.OUT.PRINTLN ("External class variable:" + OUTTER.THIS.A); }
321
Finally, add a little bit of knowledge about the inheritance of the members ' inner classes. In general, inner classes are rarely used as inheritance. But when used to inherit, pay attention to two points:
1) The member inner class must be referenced in Outter.inner.
2) The constructor must have a reference to the Outer class object and call Super () through this reference. This piece of code is from the Java programming idea
Class Withinner { class inner{ }} class Inheritinner extends Withinner.inner { //Inheritinner () is not compiled, a certain To add the formal parameter inheritinner (Withinner wi) { wi.super ();//must have this call } public static void Main (string[] args) { Withinner WI = new Withinner (); Inheritinner obj = new Inheritinner (WI); }
Java Internal class