Inner class is commonly used or quite a lot, so it is very important to study and summarize inner class. Inner class, as the name suggests, is the definition of one class inside another class, which is called inner class. Inner classes are difficult to understand, but powerful, and understanding and using them can be a huge help in improving your coding.

First encounter – Why inner classes exist

Here’s an oft-seen example:

/** Outer class **/ public class Outer {/** Inner class **/ Inner class {//doSomething
    }
}
Copy the code

Now the question is, why define a class inside a class? Doesn’t that violate the single responsibility principle of programming? Wouldn’t a class definition smell good in a Java source file? Therefore, before using inner class, it is particularly important to understand the reasons for using inner class. I think learning knowledge with a purpose may have a deeper impression, because there are actual examples to assist memory.

I read a book called Think in Java and remember a quote about inner classes: “The most interesting reason to use inner classes is: Each inner class can inherit an implementation independently, so it doesn’t matter whether the enclosing class already inherits an implementation. Now, of course, this is almost a theorem to persuade people to learn inner classes. An inner class can also inherit a class or implement an interface. 2. You can break the single-inheritance “limit” of Java classes.

Look at an example to deepen your understanding of the above argument. Defining two classes ClassA and ClassB. It’s obvious that ClassC can only implement one of them. But when you define an inner class InnerClassC within ClassC that inherits ClassB, InnerClassC is a method that can access both the ClassA and ClassB classes:

public class ClassA {
    public void classAmethod(){
        System.out.println("classAmethod");
    }
}

public class ClassB {
    public void classBmethod(){
        System.out.println("classBmethod");
    }
}

public class ClassC extends ClassA {
    class InnerClassC extends ClassB{
        public InnerClassC(){ classAmethod(); classBmethod(); }}}Copy the code

So the biggest advantage of using inner classes is that it can solve the problem of multiple inheritance. If developers don’t have to deal with multiple inheritance, there are other ways to do it.

The inner class can have multiple instances, each of which has its own state information and is independent of information about other peripheral objects. 2. Within a single enclosing class, you can have multiple inner classes inherit from the same class or implement the same interface in different ways. 3. The creation of the inner class object does not necessarily depend on the outer class object. 4. An inner class does not have a confusing is-A relationship (inheritance), it is a separate entity. 5. The inner class provides better encapsulation and is not accessible to any other class except the enclosing class.

Acquaintance — Inner class foundation

Next, I’ll cover the basics of inner classes and dive deeper into the previous examples. Take a look at this code:

//ClassA.class public class ClassA { private String Aname; Getter and setter methods public voidclassAmethod(){
        System.out.println("classAmethod"); } } //ClassB.class public class ClassB { private String Bname; Getter and setter methods public voidclassBmethod(){
        System.out.println("classBmethod"); } } //ClassC.class public class ClassC extends ClassA { private String Cname; Getter and setter methods class InnerClassC extends ClassB{publicInnerClassC(){
            Cname = "I am innerClassC";
            System.out.println(getAname());
            System.out.println(getBname());
            System.out.println(getCname());
        }

        public void show(){
             System.out.println("Cname:"+getCname()); } } public static void main(String[] args){ ClassC c = new ClassC(); ClassC.InnerClassC ic = c.new InnerClassC(); ic.show(); }}Copy the code

The running results are as follows:

Null NULL I am innerClassC Cname: I am innerClassCCopy the code

This code defines two classes ClassA and ClassB, with an internal Xname attribute and a corresponding method and a classXmethod method. Next, we define a ClassC class that inherits ClassA as an external class, with the Xname attribute and corresponding methods set inside. InnerClassC inherits the ClassB class and defines a constructor with no arguments. In this no-argument constructor, you can access the attributes of the external ClassC class, even if they are private.

Why can an inner class access all of the attributes of an outer class (including private)? The reason is that when an inner class object of an outer class is created, the inner class object must capture a reference to the outer class object and use this reference to select the member of the outer class whenever the inner class accesses the member of the outer class. In plain English, the creation of the inner class depends on the external class object (note that this is not all explained later).

Returning to the code, we define a main method in the ClassC external class. The main method first instantiates an external class object and then instantiates the inner class object with classc.innerclassic = c.new InnerClassC(). InnerClassC = InnerClassC; InnerClassC = InnerClassC; InnerClassC = InnerClassC; InnerClassC = InnerClassC;

A closer look at the InnerClassC no-argument constructor reveals that you can use the getAname() and getCname methods directly, both of which belong to the outer class. It is customary for developers to use this to refer to this class and super to refer to its parent class. Try changing the code in the InnerClassC inner class no-argument constructor to the following:

    public InnerClassC(){
            Cname = "I am innerClassC";
            System.out.println(this.getAname());
            System.out.println(this.getBname());
            System.out.println(this.getCname());
        }
Copy the code

IDEA error:

The getAname() and getCname methods are external classes, and they can be used directly without explicit calls. An implicit reference to an external class object exists in the inner class, which is classc. this:

So when a developer needs to generate a reference to an external class in an inner class, use classc.this.

Inner class beginners have a question: Does the inner class still exist after the Java source file containing the inner class is translated into the class file? This question needs to be tested today. Go to the folder where the ClassC class is located and compile it using the javac *.java command.

The ClassC class compiles two files. Take a look at the ClassC$InnerClassC bytecode file:

Let’s look at the ClassC bytecode file again:

You can see that the two class files are no longer one class, but two objects that are related.

An inner class is a compile-time concept that, once compiled, belongs to two completely different but related classes from the outer class.

Know each other — Inner class classification

Inner class is divided into four types, namely: member inner class, static inner class, method inner class and anonymous inner class.

Member inner class

Member inner class, as the name implies, an inner class exists as a member in an outer class. It is the most common inner class, and as you saw earlier, it has access to all member attributes and methods of the outer class, even if they are private. But if an external class wants to access the inner class’s member properties and methods, it needs to access them through an instance of the inner class.

Can we use the static keyword in a member inner class? The static keyword is so magical that there will be a follow-up article about it.

The answer is no, IDEA suggests that the ** member inner class cannot contain any static declarations. ** is easy to understand because a member property or method depends on the object, while a static property or method does not. It depends on the class.

To further understand this, take a typical example of a member inner class:

public class Outer{ private int outerVar = 1; private int commonVar = 2; private static int outStaticVar = 3; // Getter and setter methods /** member methods **/ public voidouterMethod(){
       System.out.println("outerMethod"); } /** static method **/ public static voidouterStaticMethod(){
        System.out.println("outerStaticMethod"); } public class Inner{/** private int commonVar = 102; /** No argument constructor **/ publicInner() {}; /** member method to access external class attributes and methods **/ public voidshow(){system.out.println (){system.out.println (){system.out.println (){system.out.println ();"Inner class commonVar property value :"+commonVar); // If the inner class and the outer class attribute have the same name, get the outer class attribute with the same name (outer class name.this). System.out.println("External class commonVar property value :"+Outer.this.commonVar); // The inner class accesses the member property of the outer class system.out.println ("OuterVar property value for the external class :"+outerVar); // The inner class accesses the static property system.out.println ("OutStaticVar property value for the external class :"+outStaticVar); OuterMethod (); // The inner class accesses the outer member method. OuterStaticMethod (); // The inner class accesses the outer class's outerStaticMethod. } } public static void main(String[] args){ Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.show(); }}Copy the code

Running results:

CommonVar of the internal class :102 commonVar of the external class :2 outerVar of the external class :1 outStaticVar of the external class :3 outerMethod outerStaticMethodCopy the code

Since it exists as a member of an external class, can other classes also access it? Define an Other class with the following code:

public class Other { public static void main(String[] args){ Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.show(); }}Copy the code

No problem, but if code duplication is too obvious, it would be appropriate to define a get method in class Outer to get its member inner classes, especially if the inner class constructor takes no arguments:

// Use to get an Inner class object public InnergetInner() {return new Inner();
    }
Copy the code

Of course, there is no trouble with the parameter constructor, just pass it in as a parameter.

** Member inner class summary. ** From the previous introduction, you can see that member inner classes have the following five characteristics:

  • A member inner class exists as a member of an external class and can be modified by any modifier;
  • Member inner classes have direct access to all properties and methods of the external class, even if they are private/static.
  • Member inner classes depend on external classes, so the creation of inner class objects must depend on external class objects.
  • Member inner classes cannot contain any static declarations;
  • Member inner classes can have the same name as external class attributes and methods, but inner class attributes/methods are called by default. If you want to access external class properties/methods, useExternal class name. this. property with the same nameTo access.

Static inner class

As the name implies, a static inner class is an inner class that has been modified static, which means it is special. Static, by the way, can modify member variables, methods, code blocks, and inner classes, which will be covered in a future article. As we said before, static does not depend on the object, but on the class.

When introducing member inner classes, we said that the creation of member inner class objects depends on external class objects. However, the creation of a static inner class object does not depend on the outer class object, which means that the static inner class cannot use any variables/methods that are not static in the outer class.

Let’s also use a typical static inner class example to deepen our understanding:

public class Outer { private int outerVar = 1; private static int commonVar = 2; private static int outStaticVar = 3; // Getter and setter methods /** member methods **/ public voidouterMethod(){
        System.out.println("outerMethod"); } /** static method **/ public static voidouterStaticMethod(){
        System.out.println("outerStaticMethod"); } /** static code block **/ static {system.out.println ("out static block"); } public static class StaticInner{/** private int innerVar = 101; /** / private static int commonVar = 102; private static int innerStaticVar = 103; /** static code block **/ static {system.out.println ("inner static block"); } /** member method **/ public voidshow(){// When a static inner class has the same name as an outer class attribute (only a static attribute), the inner class's static attribute system.out.println () is called by default."Inner class commonVar property value :"+commonVar); // If the static inner class has the same name as the outer class attribute (static only), get the outer class attribute (the outer class name) with the same name. System.out.println("External class commonVar property value :"+OuterA.commonVar); // Static inner class access its own member property system.out.println ("The innerStaticVar property value of the inner class :"+innerStaticVar); // The static inner class accesses the static property system.out.println ("OutStaticVar property value for the external class :"+outStaticVar); OuterStaticMethod (); // Static inner class access outer class outerStaticMethod(); } /** static method **/ public static voidinnerStaticMethod(){
            System.out.println("innerStaticMethod"); } } public static void main(String[] args){ System.out.println(StaticInner.innerStaticVar); StaticInner.innerStaticMethod(); new StaticInner().show(); }}Copy the code

Take a guess at the result :(think about the order of execution of each component)

Out static block inner static block 103 innerStaticMethod commonVar value of the inner static block 102 commonVar value of the outer static block 2 The innerStaticVar attribute of the inner class is 103. The outStaticVar attribute of the outer class is 3 outerStaticMethodCopy the code

It is important to note that static inner classes, specifically Java components (properties, methods, code blocks, inner classes) that are modified static cannot use this and super internally because they lack an implicit this and super. Static modified components are independent of the object. Class only.

To see if Other classes can access a static inner class from an external class, define an Other class with code like this:

Public class Other {public static void main(String[] args){public static void main(String[] args){ Static inner class is loaded, please pay attention to the external class not be loaded. The Outer StaticInner. InnerStaticMethod (); New outer.staticinner ().show(); }}Copy the code

The running results are as follows:

Inner static block >>>>>>> See that a static block with no external class is not initialized. InnerStaticMethod commonVar value of the inner class :102 Out static block commonVar value of the external class :2 The innerStaticVar attribute of the inner class is 103. The outStaticVar attribute of the outer class is 3 outerStaticMethodCopy the code

Note that when you directly access a static method in a static inner class, the outer class will not be called because it does not depend on the outer class, as evidenced by the fact that the static block of the outer class is not initialized.

* static inner class summary. ** From the previous introduction, you can see that static inner classes have the following five characteristics:

  • Static inner classes can contain arbitrary information inside them;
  • Static inner classes can only access static properties/methods of external classes;
  • A static inner class can exist independently of its outer class;
  • throughExternal class name Internal class name Static property/methodTo access the static properties/methods of the inner class directly;
  • Static inner classes can have the same name as static attributes or methods of the outer class, but the default is to call the attributes/methods of the inner class. If you want to access properties/methods of an external class that are modified static, use theExternal class name. Property of the same nameTo access.

Local inner class

As the name implies, local inner classes are defined in methods and scopes and are used to solve complex problems. Since it is a local inner class, it cannot be decorated by any access modifiers and can only be used in that defined method or scope. Note that the static keyword cannot be used in local inner classes.

Also give a typical example of a local inner class to deepen understanding:

public class OuterB { private int outerVar = 1; private int commonVar = 2; private static int outStaticVar = 3; // Getter and setter methods /** member methods **/ public voidouterMethod(){
        System.out.println("outerMethod"); } /** static method **/ public static voidouterStaticMethod(){
        System.out.println("outerStaticMethod"); } public static void main(String[] args){OuterB OuterB = new OuterB();} public static void main(String[] args){OuterB OuterB = new OuterB(); outerB.outerCreateMethod("1234"); } public void outerCreateMethod(String password){public void outerCreateMethod(String password){public void outerCreateMethod(String password){public void outerCreateMethod(String password)true; /** private int innerVar = 101; /** private int innerVar = 101; private int commonVar = 102; /** Local inner class member method **/ public voidshow(){system.out.println (){system.out.println (){system.out.println (){system.out.println ();"Local inner class commonVar property value :"+commonVar); // Get the external class attribute of the same name (the external class name.this). System.out.println("External class commonVar property value :"+OuterB.this.commonVar); // The local inner class accesses its own member property system.out.println ("Local Inner class innerVar attribute value :"+innerVar); // The local inner class accesses the member property of the external class system.out.println ("OuterVar property value for the external class :"+outerVar); // Static system.out.println (system.out.println)"OutStaticVar property value for the external class :"+outStaticVar); // The local inner class accesses the arguments in the method that defines it: system.out.println ("OuterCreateMethod method passed in the password argument :"+password); // The local inner class accesses the local variable system.out.println () in the method that defines it."Flag defined in the outerCreateMethod method:+flag); OuterStaticMethod (); // Local inner class access outer class outerStaticMethod(); // The local inner class accesses the outer class member method outerMethod(); Inner Inner = new Inner(); Inner Inner = new Inner(); System.out.println("Local inner class commonVar property value :"+inner.commonVar);
        System.out.println("Local Inner class innerVar attribute value :"+inner.innerVar); inner.show(); }}Copy the code

Guess the result:

Local inner class commonVar value :102 innerVar value :101 Local inner class commonVar value :102 External class commonVar value :2 Local inner class innerVar value :101 OuterVar value of the external class :1 outStaticVar value of the external class :3 Parameter passed in the outerCreateMethod method Password :1234 Flag defined in the outerCreateMethod method:true
outerStaticMethod
outerMethod
Copy the code

As you can see from the above example, a local inner class can only be used in the method or scope in which it is defined, and cannot be preceded by any access modifiers. Otherwise, the rest of the usage is very similar to that of a member inner class, so it’s safe to think of a local inner class as just a “special” member inner class that only works in the method or scope that defines it.

If you haven’t noticed the lines in the local inner class, it is used to print the parameters and local variables of the method in which the local inner class is defined. Yes, but there are conditions.

// The local inner class accesses the arguments in the method that defines it: system.out.println ("OuterCreateMethod method passed in the password argument :"+password); // The local inner class accesses the local variable system.out.println () in the method that defines it."Flag defined in the outerCreateMethod method:+flag);
Copy the code

What are the conditions? Set the flag to true, but later you change it to false:

/** The variable defined in the method **/ Boolean flag =true;
        flag = false;
Copy the code

The local inner class throws an exception:

Flag variables are accessed from inner classes and require final or valid final. Implication: If a local inner class wants to access a local variable in the method that defines it, the variable will either be preceded by a final modifier or will not be modified after the first assignment (the reference type is pointing that cannot be changed). Note that prior to JDK1.8 (excluding JDK1.8) only final modified variables can be accessed, i.e. only the former method was added later. This special variable restriction also applies when a local inner class accesses the parameters of the methods that define it.

** Local inner class summary. ** From the previous introduction, you can see that local inner classes have the following six characteristics:

  • Local inner classes can only be defined in a method or scope;
  • Local inner classes cannot be preceded by any access modifiers;
  • Local inner classes cannot contain any static declarations;
  • When the parameters and variables of a defined method/scope have final modifications or the values of the parameters and variables are not modified after assignment, the local inner class can directly access the parameters and variables of the defined method/scope but cannot modify them.
  • A local inner class can directly access all properties and methods of an external class, even if they are private/static;
  • A local inner class can have the same name as the properties and methods of the outer class, but the default is to call the properties/methods of the local inner class. If you want to access properties/methods of an external class, useExternal class name. this. property with the same nameTo access.

Anonymous inner class

As the name implies, an anonymous inner class is an inner class without a name, and since there is no name, there is no access modifier and no constructor. Anonymous inner class is widely used, so its research is particularly important. In fact, you can think of an anonymous inner class as a local inner class without a name.

The anonymous inner class is created in the following format:

New to the parent class constructor (parameter list) | | implementing an interface () {/ / class body part of the anonymous inner class}Copy the code

A novice may create an anonymous inner class without first understanding what is going on. From this creation format, you can see that the anonymous inner class must inherit a class or implement an interface. Anonymous inner classes cannot be declared using the class keyword, but instead use new directly to generate an implicit reference to an object.

The use of anonymous inner class is special, take a typical example to deepen understanding:

package com.envy.inner;

public class OuterC {

    public interface Fruit{
        void eat();
    };

    public static Fruit getFruit(String description){
        Boolean flag = true;
        return new Fruit() {
            @Override
            public void eat() {
                System.out.println("Get parameter information for the defined class :"+description);
                System.out.println("Get the local variable of the defined class :"+flag); } // Make sure the semicolon on the next line is not missing! }; } public static void main(String[] args){/** OuterC."Delicious").eat(); }}Copy the code

Running results:

Get local variables of the defined class:true
Copy the code

From the above code, you can learn a few things: 1. Anonymous inner classes must either inherit a class or implement an interface. Anonymous inner classes have no class name, so they have no constructor. 3. Anonymous inner classes have no access modifiers. 4. To use an anonymous inner class, the class/interface after new must exist, and some of the methods after new may be overridden. 5. Anonymous inner classes have the same access restrictions as local inner classes when accessing method parameters/variables. Anonymous inner classes cannot be abstract classes, so they must implement all the abstract methods of the inherited class or implement the interface.

Acacia – access restriction thinking

Now consider why there is an access restriction when a local inner class (including an anonymous inner class) accesses the parameters or local variables of the method that defines it. That is, the local variable is required to be either preceded by a final modifier or not modified after the first assignment (the reference type is the pointer that cannot be changed). For the sake of explanation, the final keyword must be used.

If the developer does not follow the required Settings, then forcibly accessing IDEA will prompt the exception shown in the picture:

For research purposes, here is a test with the code:

public class OuterD {
    public interface Fruit{
        void eat();
    }

    public Fruit getFruit(String name){
        boolean flag = true;
        return new Fruit() {
            @Override
            public void eat() {
                System.out.println("parameter:"+name);

                System.out.println("local variable:"+flag); }}; }}Copy the code

Try compiling the test class using the javac *.java command, and since it contains an interface, you can guess that it will end up compiling three class bytecode files:

The outerd.class file is a compiled file from the external class. Check it out:

package com.envy.test;

public class OuterD {
    public OuterD() {
    }

    public OuterD.Fruit getFruit(final String var1) {
        final boolean var2 = true;
        return new OuterD.Fruit() {
            public void eat() {
                System.out.println("parameter:" + var1);
                System.out.println("local variable:"+ var2); }}; } public interface Fruit { void eat(); }}Copy the code

It can be found that the external class automatically adds its parameterless constructor after compilation, and the final keyword is automatically added before the parameters and local variables of the method defining the inner class. The author uses Java1.8 here, so it can be known. It is also possible that 1.8 and later will allow local variables/method parameters to remain unchanged after their first assignment (reference types are pointed to and cannot be changed). Final keywords will still be added after compilation, but developers are no longer required to do so. And you can see it by using external classes. The interface name provides external access to the inner class.

The OuterD$Fruit. Class file is actually a compiled file from the Fruit interface:

package com.envy.test;

public interface OuterD$Fruit {
    void eat();
}
Copy the code

You can see that after compilation, access to the inner class information in the external class file is through. $is used outside the outer class file. The implication is that $exists only when a class contains an inner class.

OuterD$1.class is the compiled file of the anonymous inner class because it has no name and can only be identified by 1:

package com.envy.test;

import com.envy.test.OuterD.Fruit;

class OuterDThe $1 implements Fruit {
    OuterDThe $1(OuterD var1, String var2, boolean var3) {
        this.this$0 = var1;
        this.val$name = var2;
        this.val$flag = var3;
    }

    public void eat() {
        System.out.println("parameter:" + this.val$name);
        System.out.println("local variable:" + this.val$flag); }}Copy the code

The first parameter is var1 (external class object), the second parameter is passed in by the defined method, and the third parameter is the value of a local variable defined within the defined method.

So instead of directly calling the parameters or local variables passed in the defined method, the anonymous inner class uses its own constructor to make a backup of the parameters passed in. Methods in the anonymous inner class actually call their own properties, not the parameters or local variables passed in from the outside. (The eat method illustrates this.)

Methods in inner classes call their own properties, so you can modify the properties of the inner class without affecting the method parameters or local variables in the outer defined method. In general, this is true, but when I tried to modify the method parameter or local variable in the anonymous inner class, IDEA reported an error:

Var $name = var2; var $name = var2; var $name = var2; var $name = var2;

OuterDThe $1(OuterD var1, String var2, boolean var3) {
        this.this$0 = var1;
        this.val$name = var2;
        this.val$flag = var3;
    }
Copy the code

If the val$name attribute ina method changes, the value of the corresponding parameter var2 should also change, but often the value of the parameter var2 does not change. This creates a serious security problem, so the final keyword is specified to avoid this problem.

See here also knew that the problem is actually a copy, Java does not exist a pointer, so to avoid copying the value of the change (such as a local variable is modified, and the value of the inner class copy didn’t change, thus causing the internal and external value is not the same), so the final keywords are introduced to ensure that the value will never change.

A local variable that is modified in a defined method is passed as a parameter to an anonymous inner class. If the parameter changes, it is assigned to a property in the inner class. Why doesn’t the property change?

To solve this problem, Debug is used step by step:

package com.envy.inner;

public class OuterD {
    public interface Fruit{
        void eat();
    }

    public Fruit getFruit(String name){
        boolean flag = true;
        return new Fruit() {
            @Override
            public void eat() {
                System.out.println("parameter:"+name);
                System.out.println("local variable:"+flag); }}; } public static void main(String[] args){ OuterD outerD = new OuterD(); Fruit fruit= outerD.getFruit("envy"); fruit.eat(); }}Copy the code

The execution sequence is as follows:

The getFruit method is executed before the eat method, which directly stores the value of the getFruit object. Note that the eat method in this anonymous inner class can be called by the Fruit object after the getFruit method is executed, and the lifetime of the local variable stored on the stack is over.

Note that final must be added to a method argument or local variable that is accessed by an anonymous inner class, or that is not modified after the first assignment.

Love – can be initialized without constructor

As mentioned several times before, since the anonymous inner class has no name and therefore no constructor, can it also be initialized? The answer is yes, you can use construction blocks! Here’s a typical example:

package com.envy.inner;

public class OuterE {

    public interface Fruit{
        double getFruitPrice();
        String getFruitName();
    };

    public Fruit getFruit(final String name, final double price){
        return new Fruit() {
            String fruitName;
            double fruitPrice;
            {
                fruitName = name;
                fruitPrice = price;
            }
            public double getFruitPrice() {
                return fruitPrice;
            }
            public String getFruitName() {returnfruitName; }}; } public static void main(String[] args){ OuterE outerE = new OuterE(); Fruit fruit = outerE.getFruit(The word "apple",2.8);
        System.out.println(fruit.getFruitPrice());
        System.out.println(fruit.getFruitName());
    }
}
Copy the code

Running results:

2.8 the appleCopy the code

Fruit.getfruitprice (), you can’t directly call the getters for properties in anonymous inner classes because anonymous inner classes don’t have names. So can not call its method, can only use it to implement the class of the same name method implementation to complete the method call.

So about the internal class is introduced here, which involves a lot of knowledge points, need to review and digest.

Reference article: a detailed explanation of internal class, a brief introduction to Java internal class, thank you guys for their guidance.

For more content, please pay attention to your personal wechat public account: Yu Si Blog, or scan the following QR code on wechat to directly follow: