Various internal classes and Lambda expressions of Java

Inner class

An inner class means to define a class inside an outer class. The emergence of inner classes once again broke the limitations of Java single inheritance.

The inner class can be static, or can be modified with public, default, protected and private. (The external top-level class, that is, those with the same class name and file name can only use public and default).

Note: The inner class is a concept at compile time. Once compiled successfully, it will become two completely different classes.

For an outer class named outer and its internally defined inner class named inner. After the compilation is complete, two categories, outer.class and outer$inner.class, appear. Therefore, the member variable/method name of the inner class can be the same as that of the outer class.

1 member inner class

That is, the ordinary inner class, which is one layer inside the outer class.

  • The member inner class can access all the members and methods of the outer class;
  • To access the members of the external class, the members and methods of the internal class need to be accessed through the instance object;
  • The member inner class cannot contain static variables and methods . Think about it, a non-static internal class will not be loaded when the external class is loaded; but your internal class has static variables and methods, which must be loaded first, and will be combined with this internal class. Loading timing conflicts. In fact, this way of writing the compiler can prompt an error:

Various internal classes and Lambda expressions of Java

 

For specific usage, refer to the following code and comments:

public class Outer{
    private int outi = 0;//外部类的成员
    //外部类的方法
    public void outPrint(){
        System.out.println("out");
    }
    //成员内部类
    class Inner{
        int ini = outi + 1;//内部类可以访问外部类的成员变量
        public void inPrint(){
            outPrint();//内部类可以访问外部类的成员方法
            System.out.println("in");
        }
    }
    public static void main(String[] args) {
        Outer outer = new Outer();
        //这里因为main是静态方法,访问非静态的 Inner对象需要一个Outer对象
        Inner inner = outer.new Inner();
        inner.inPrint();//外部类访问内部类的成员方法
        System.out.println(inner.ini);//外部类访问内部类的成员变量
    }
}

2. Static inner class

To solve the problem that ordinary member inner classes cannot contain static members, it is to declare member inner classes as static  .

It can be said that a static inner class can only access the static members of its outer class, except that it is no different from a non-static inner class.

The static inner class is equivalent to a logically independent class, but it is placed inside.

  • Static inner classes do not depend on the loading of outer classes. (Depending on whether to use to load, it is equivalent to the parallel relationship between inside and outside.)
  • The static inner class cannot directly access the non-static members of the outer class. (Because non-static members are not loaded when the external class is loaded, unless after instantiation)

The code for comparing the internal classes of ordinary members is as follows:

public class Outer{
    private int outi = 0;//外部类的成员
    //外部类的方法
    public void outPrint(){
        System.out.println("out");
    }
    //静态成员内部类
    static class Inner{
        Outer outer = new Outer();
        int ini = outer.outi+1;//只能通过实例
        //int ini = outi + 1;//不能访问外部类的普通成员变量
        public void inPrint(){
            outer.outPrint();//只能通过实例
            //outPrint();//不能访问外部类的普通成员方法
            System.out.println("in");
        }
    }
    public static void main(String[] args) {
        Inner inner = new Inner();//可以直接new出来,因为相当于是两个独立的类
        inner.inPrint();
        System.out.println(inner.ini);
    }
}

For static inner classes, our feelings will be more obvious. Since the two classes are irrelevant, why should they be placed inside as an inner class?

In fact, it is for certain hierarchical relationships. When the scope of use of Inner is very small, it is suitable for being included in the Outer class, but does not depend on the external class. Just write it, and at the same time, it can make the hierarchical relationship of the code more clear , Without having to re-place another file.

3. Local internal class

A local inner class refers to a class defined inside a method or in a certain scope .

  • Local inner classes can only be instantiated in methods, not outside;
  • The local inner class accesses the variable of the outer method. This variable must be finalized.

For example, the following code:

class Outter{ 
    public void outMethod(){ 
        final int i=0; 
        class Inner{ 
            //使用i 
        } 
 
        Inner in=new Inner(); 
    } 

Obviously, the definition is the same as the ordinary member inner class, but the special point emphasized here: Why must i be final?

The reason is:

  • When the JVM runs to the need to create the Inner object, the Outter class has all been run, then the garbage collection mechanism is likely to release the local variable i, and if according to the definition of the ordinary member inner class, i should be accessible, then This problem requires a constraint, so the compiler solves this problem, he will create a copy of i in the Inner class;
  • Then there will be a new problem. When the i of the outer class changes, the i of the inner class must be consistent;
  • Therefore, it has to be stipulated that these local domains must be constants and must be modified with final to ensure data consistency.

 

4. Anonymous inner class

4.1 Anonymous inner class

When a local inner class has no name, it is an anonymous inner class.

  • The way to achieve this is to create an anonymous object of an external class with content or a subclass of an interface .
  • Unlike ordinary local internal classes, the variables of external methods can be accessed without final modification . (After jdk1.8)

For example, two of our common

    public static void main(String[] args) {
        //第一种方式
        new Thread(){
            @Override            public void run() {                System.out.println("内部类输出……");
            }        }.start();        //第二种方式
        new Thread(new Runnable() {
            @Override            public void run() {                System.out.println("内部类输出……");
            }        }).start();    }
  • The first way is to create an external class with content and override its methods, but we did not name this class, but directly created an instance;
  • The second way is to implement the corresponding method by implementing an interface. Similarly, we did not name this implementation class, but directly created an instance.

It is precisely because of the above that an anonymous inner class can access external variables , and it does not need to be final. Similarly, it can also hold references to external classes . This situation may cause memory leaks .

The life cycle of the external class has arrived, but because the internal class holder refers to the external class, the external class cannot be recycled, causing a memory leak.

The solution is:

  • Use static inner classes and do not hold references to outer classes. If you want to call methods of outer classes or use properties of outer classes, you can use weak references (as mentioned earlier, static inner classes are parallel to outer classes, and weak references are safe) .

4.2 Anonymous inner classes and lambda expressions

Starting from Java 8, Lambda expressions have been introduced, using code blocks as parameters and using more concise code to create instances of interfaces with only one abstract method (this kind of interface is called a functional interface) .

Still use the code of the anonymous inner class above as an example:

    public static void main(String[] args) {
  new Thread(()-> System.out.println("内部类输出")).start();
    }

It can be seen that when a lambda expression replaces an anonymous inner class, the lambda code block is written to replace the method body that implements the abstract class . To sum up, the grammar of a lambda expression is mainly composed of three parts:

  • For the formal parameter list , if there is only one parameter, you can omit the parentheses. When there is no parameter type, you can use () or obj instead.
  • Arrow->
  • In the code block part, if the code is only one line, you can omit the curly braces, otherwise use curly braces to mark the code part of the lambda expression.

In terms of writing, we then define an interface ourselves, and then write about the lambda type with parameters:

//自定义接口
interface Origin{
    int sum(int a, int b);//待实现方法,有参数
}
public class LambdaTest {
    public static void main(String[] args) {
        //写法1:使用lambda表达式实现接口
        Origin o = (int a, int b)-> {
            return a+b;
        };
        
        //写法2:省略参数类型
        Origin o1 = (a, b)->{
            return a+b;
        };
        
        //写法3,省略花括号(只适用于方法实现只有一行的情况)
        Origin o2 = (a, b)-> a+b;
        
        System.out.println(o.sum(100,100));
    }
}

4.3 Java's four reference types

  1. Strong reference : The most common common object reference. As long as there is a strong reference pointing to an object, it means that the object is still alive, and the garbage collection will not reclaim this object;
  2. Weak reference : Once the garbage collector finds an object with only weak references, it will reclaim its memory regardless of whether the current memory space is sufficient . (Even if weak references are referenced by other strong references, they will still be recycled)
  3. Soft references : If an object has only soft references, if the memory space is sufficient, then the JVM will not GC it, if the memory space is insufficient, it will GC the object .
  4. Phantom reference : If an object has only a phantom reference, it will be treated as garbage by the JVM for GC at any time as if it has no reference.

Will not be recycled?

Strong references, as long as there are strong references, they will not be recycled.

Regarding the problems caused by strong and weak references, the most obvious one is the use of ThreadLocal.

Guess you like

Origin blog.csdn.net/mrchaochao/article/details/108625802