创建内部类,包括静态内部类、局部类、嵌套类和匿名内部类。

在 Java 中我们有类
类有两种类型的成员,属性(或字段)和方法(或函数)
我们的程序就是一个类的集合

内部类

Java 给了我们灵活性
一个类可以拥有第三种类型的成员,即内部类

class Computer {
     String serialNumber;
     void executeCommand() { }
     class Processor {
         // Here goes the definition of the class
     }
}

内部类也被称为嵌套类。理论上,可以有很多层次的类。

class LevelOne {
     class LevelTwo {
         class LevelThree {
             class LevelFour {
                 /** Finally do something */
             }
         }
     }
}

但实际上,我们可以有内部抽象类、内部接口和内部 枚举类。

class Computer {
     abstract class Processor { }
     interface Pluggable { }
     enum PORTS {
         USB2, USB3, ESATA, HDMI
     }
}

简单划分

关注简单的内部类,其有4种类型

  • STATIC inner classes 静态内部类
  • NON-STATIC inner classes 非静态内部类
  • LOCAL classes 局部内部类
  • ANONYMOUS classes 匿名内部类

静态内部类

  • 定义静态内部类
class Computer {
     static class Mouse {
     }
}
  • 使用静态内部类
Computer.Mouse m = new Computer.Mouse();

静态内部类是通过它们的封闭类来访问的
静态类与它们的封闭类是独立的。它们就像普通的类一样,只不过它们恰好位于另一个类中。

  • 实际上,您可以将封闭类看作是一种包。您可以导入封闭类的名称,并像普通类一样使用静态内部类。请记住,内部静态类必须是一个公共成员,以便可以从另一个包访问它。
import com.example.Computer.*;
public class Test {
      Mouse m = new Mouse();
     /** Rest of the definition */
}
  • 它们也可以被标记为 private、 protected 或不使用修饰符,因此它们只能在包中访问(默认的可访问性)。
public class Computer {
     private static class Component { }
     protected static class MotherBoard { }
     static class Slot { }
}
  • 静态内部类作为一个类的成员,可以访问封闭类的其他成员,但是只有当它们是 STATIC 时才可以。
public class Computer {
     private static String serialNumber = "1234X";
     public static class Mouse {
         void printSN() {
             System.out.println("MOUSE-" + serialNumber);
         }
     }
}

由于是静态的,可以在任何方法、块或构造函数中使用它,不管它是静态的还是非静态的,因为静态内部类并不绑定到特定的实例
出于这个原因,静态内部类经常被用作一个实用程序类,其中包含一个类的所有对象共享的公共方法

非静态内部类

  • 定义非静态内部类
class Computer {
     class HardDrive {
     }
}
  • 使用非静态内部类
    非静态内部类是通过其封闭类的实例访问的
Computer c = new Computer();
Computer.HardDrive hd = c.new HardDrive();
  • 内部类的实例只存在于封闭类的实例中。同样,当你想使用一个类的方法时,你首先需要一个该类的实例。
  • 一旦有了封闭类的实例,就可以以与通常不同的方式使用new关键字。
Computer computer = new Computer();
Computer.HardDrive hardDrive = computer.new HardDrive();
  • 您也可以使用 import 技巧来减少写入,但是您仍然需要像以往一样创建内部类。(仍然需要实例话封闭类的实例才可以创建其非静态内部类
import com.example.Computer.*;
public class Test {
         Computer computer = new Computer();
         HardDrive hd = computer.new HardDrive();
         /** Rest of the definition */
}
  • 获取内部类实例的另一种方法是使用封闭类的方法来创建它,避免使用那种new关键字的奇怪的语法。
public class Computer {
     class HardDrive { }
     public HardDrive getHardDrive() {
         return new HardDrive();
     }
}
  • 作为类的成员,非静态内部类可以访问封闭类的其他成员,无论其成员是否为静态。
public class Computer {
     private String brand = "XXX";
     private static String serialNumber = "1234X";
     public class HardDrive {
         void printSN() {
            System.out.println(
                brand + "-MOUSE-" + serialNumber
             );
         }
     }
}

内部类也可以标记为 private、 protected 或不带修饰符,因此它们只能在包中访问。但是大多数时候,因为它们依赖于封闭类,所以它们被标记为 private。

public class Computer {
     private class Component { }
     protected class MotherBoard { }
     class Slot { }
}
  • 非静态内部类不能包含静态成员,因为非静态内部类依赖于其封闭类的实例,而静态成员在类加载的时候就执行。拥有一个静态成员意味着它可以跨实例共享,因为该成员属于该类,但是由于我们讨论的是一个内部类,该内部类不能被封闭类的许多实例共享,所以这是不可能的。
public class Computer {
     class HardDrive {
         // Compile-time error here
         static int capacity;
          // Compile-time error here
          static void printInfo() {
              // Definition goes here
         }
     }
}
  • 唯一的例外是使用final static关键字定义非静态内部类的成员属性,其只能作用在属性且不为null值
public class Computer {
     class HardDrive {
         final static int capacity = 120; // It does compile!
         // Compile-time error here
         final static String brand = null;
         // Compile-time error here
         final static void printInfo() {
             // Definition goes here
         }
     }
}

image.png

局部内部类

  • 定义局部内部类
class Computer {
     void process() {
         class Processor {
         }
     }
}
  • 使用局部内部类
    局部类只能在定义它们的方法或块中使用
void process() {
         class Core { }
         Core core = new Core();
}

本地类是局部的,只能在声明它们的方法或块中使用。块是介于花括号之间的任何东西。

void method() {
    class MethodLocalClass { }
    MethodLocalClass mlc = new MethodLocalClass();
    if ( 1 == 1 ) {
       class IfLocalClass { }
       IfLocalClass ilc = new IfLocalClass();
    }
    while ( true ) {
       class WhileLocalClass { }
       WhileLocalClass wlc = new WhileLocalClass();
    }
}

-因为局部内部类不是类的成员,所以不能使用访问级别声明它,只能在声明它们的地方进行访问。但是,可以将局部类声明为abstract类或final类

  • 可以访问封闭类的成员,但是它们不能声明静态成员(只有静态最终属性) ,就像内部类一样。
  • 局部内部类不能声明静态static成员
class Computer {
    private static int a = 1;
    private String serialNumber = "1234XX";

    void process() {
        class Processor {
            private int b=1;
            static int c=4; //ERROR
            Processor() {
                System.out.println(
                        "Processor #1 of computer " +
                                serialNumber + a
                );
            }
        }
    }

匿名内部类

  • 定义匿名类
    新操作符后面跟着接口或类的名称和构造函数的参数(如果是接口,则为空括号)
Computer comp = new Computer() {
     void process() {
         // Here goes the definition
     }
};

类的主体实现接口或继承类
因为匿名类没有名称,可以被这样调用。但是,匿名类表达式不会声明新类。它要么实现一个现有的接口,要么继承一个现有的类。

new Computer() { }
//等价于
class [NO_NAME_CLASS] extends Computer { }

new Runnable() { }
//等价于
class [NO_NAME_CLASS] implements Runnable { }
  • 因为匿名内部类没有名称(实际上,编译器在创建类文件的时候给其随机命名) ,匿名类不能有构造方法。如果要运行某些初始化代码,必须使用初始化块,在生成实例的时候执行一次。
Computer t = new Computer() {
     {
         // Initializing code
     }
     void process() { /** Redefinition goes here */ }
};
  • 变量规则
    匿名类是局部内部类的一种类型,它们有相同的规则
    1.可以访问其封闭类的成员
    2.不能申明static变量成员
    3.只能访问局部变量(方法中定义的变量或参数)
    4.用匿名类(子类对象)时,您正在使用超类引用。通过此引用,您可以使用该类型中声明的属性和方法。
  • 5.但是如果在匿名内部类申明一个新的方法,在封闭类中无法引用,因为匿名内部类没有名字,即使想强转成匿名内部类也因为没有名字无法转换
class Task {
     void doIt() {
         /** Here goes the definition */
     }
}
class Launcher {
     public static void main(String args[]) {
         Task task = new Task() {
             void redoIt() {
                 /** Here goes the definition */
             }
         };
         task.doIt(); // It's OK
         task.redoIt(); // Compile-time error!
     }
}

image.png
使用匿名类主要是关于代码风格。如果类有一个较短的主体,它只实现一个接口(如果我们使用接口) ,它不声明新成员,语法使代码更加清晰,您应该考虑使用它而不是局部类或内部类。

影子变量

内部类的一个成员具有与封闭类的一个成员相同的名称。

class Computer {
     private String serialNumber = "1234XXX";
     class HardDrive {
         private String serialNumber = "1234DDD";
         void printSN(String serialNumber) {
             System.out.println("SN: " + serialNumber);
         }
     }
}
  • 此处的打印的serialNumber指代的是方法的入参变量
  • 如果要打印HardDrive的成员变量必须要用this.serialNumber
  • 如果要打印Computer的成员变量必须要使用Computer.this.serialNumber 即有影子变量时内部类要使用封闭类的相同名字变量必须要使用封闭类名.this.变量名
    image.png

这个家伙很懒,啥也没有留下😋