内部类,顾名思义就是一个 A 类的内部再定义一个 B 类。那么 A 类就是外部类, B 类就是内部类。

  1. public class A {
  2. public class B{
  3. }
  4. }

内部类除了定义的位置有特殊性外,本质上与普通类没有区别,包括:

  1. 也拥有属性、构造、行为、初始化块、自己的内部类
  2. 内部类虽然和外部类书写在同一篇 .java 文件,但编译后拥有自己独立的 .class 文件
  3. 内部类的类名不是书写的简单名,而是和外部类的类名共同组成的

根据内部类书写的位置,可以分为两大类:

  1. 成员内部类
  2. 局部内部类

    成员内部类

    如果一个内部类是直接定在外部类的 {} 里,它的位置与外部类的属性、方法、构造平行,称为“成员内部类”。它是最普通的内部类,作为外部类的成员,它也能拥有访问修饰符来控制这个内部类能否被外部使用。

    1. 普通成员内部类

    1. // Outside.java
    2. public class Outside{
    3. // 内部类位置与外部类的属性、方法、构造器平行,所以就作为了外部类的成员内部类
    4. class Inside{
    5. }
    6. // 外部类的构造
    7. public Outside(){
    8. }
    9. // 外部类有自己的属性、方法
    10. private String name;
    11. public void print(){
    12. }
    13. }

    若内部类 class Inside{} 添加了 private 修饰,则表示只能在其外部类 class Outside{} 中使用;若添加的是 public 则表示外部类外其他类也都可以使用。
    要使用普通成员内部类,要先产生外部类的对象,再用 外部类对象.new 内部类() 产生内部类对象:

    1. 外部类型 外部实例名称 = new 外部类();
    2. 外部类.内部类类型 实例名称 = 外部实例.new 内部类();
    1. // Outside.java
    2. public class Outside {
    3. // 外部类属性
    4. public String nameOutside = "外部类";
    5. // 内部类
    6. public class Inside {
    7. public String nameInside = "内部类 inside";
    8. // 内部类访问外部类属性值
    9. public void print() {
    10. System.out.println(nameInside + "," + nameOutside);
    11. }
    12. }
    13. }
    14. // TestMain.java
    15. public class TestMain {
    16. public static void main(String[] args) {
    17. // 外部类的实例
    18. Outside outsideObj = new Outside();
    19. /*
    20. 内部类作为了外部类的成员,所以创建内部类实例的语法
    21. */
    22. Outside.Inside insideObj = outsideObj.new Inside();
    23. insideObj.print();
    24. }
    25. }

    image-20220509175301709.png

    2. 静态内部类

    由于添加了 static 修饰,所以该静态内部类与它所属的外部类实例对象无关,就无须产生外部对象了。直接使用外部类类名访问。

    1. Outsize.Inside inner2 = new Outsize.Inside();

    局部内部类

    局部内部类也被称为方法内部类,“局部”这个词也是老熟人了,提到作用域就肯定会提到它。既然是局部 XX 也就是指它是被定义在外部类的某个函数中的,它的书写位置与该方法的局部变量平级。作为方法的内部定义,同局部变量一样,只能在函数内部使用、没有访问修饰符。

    3. 局部内部类

    局部内部类只能在定义它的函数里使用。 ```java // Outside.java public class Outside { // 外部类属性 public String nameOutside = “外部类”;

    public void foo() {

    1. class Inside {
    2. String nameInside = "内部类 inside";
    3. public void print() {
    4. System.out.println(nameInside + "," + nameOutside);
    5. }
    6. }
    7. new Inside().print();

    } }

// TestMain.java public class TestMain { public static void main(String[] args) { // 外部类的实例 Outside outsideObj = new Outside(); outsideObj.foo(); } }

  1. ![image-20220510100756097.png](https://cdn.nlark.com/yuque/0/2022/png/1454005/1652163013599-a6466568-c613-429c-afc3-1c6799ce4c39.png#clientId=ua2d833b2-332d-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u957f0b97&margin=%5Bobject%20Object%5D&name=image-20220510100756097.png&originHeight=980&originWidth=2450&originalType=binary&ratio=1&rotation=0&showTitle=false&size=218164&status=done&style=stroke&taskId=u4e7292d8-43b0-463d-bf52-2ae932e925b&title=)<br />对于参数的使用,也是需要小心操作的。内部类中,书写 this 代表的是当前内部类对象,若要表示它所关联的外部类,要写成 `外部类.this.属性`。局部内部类中的方法,可以操作外部类属性。
  2. ```java
  3. // Outside.java
  4. public class Outside {
  5. // 外部类属性
  6. public String nameOutside = "外部类";
  7. public int number = 5;
  8. public void foo() {
  9. class Inside {
  10. String nameInside = "内部类 inside";
  11. public int number = 10;
  12. public void print() {
  13. System.out.println(nameInside + "," + nameOutside);
  14. }
  15. public void printNumber(int number) {
  16. System.out.println(number);
  17. System.out.println(this.number); // 访问自身属性
  18. System.out.println(Outside.this.number); // 访问外部类属性
  19. }
  20. }
  21. Inside inside = new Inside();
  22. inside.printNumber(number);
  23. }
  24. }
  25. // TestMain.java
  26. public class TestMain {
  27. public static void main(String[] args) {
  28. // 外部类的实例
  29. Outside outsideObj = new Outside();
  30. outsideObj.foo();
  31. }
  32. }

image-20220510135211770.png

4. 匿名内部类

匿名内部类是在 new 对象同时去定义这个对象中的属性和行为的,由于没有取名字只能使用一次。

  1. new Object(){
  2. private int a;
  3. public void test(){
  4. }
  5. }.test();

编译后的文件

含有内部类的 .java 文件在编译后会一个类生成一篇 .class 文件,在 out 文件夹可以查看

  • 成员内部类编译后的 .class 文件名:外部类名字+$+内部类的名字
  • 局部内部类编译后 .class 文件名:外部类名+$序号+内部类名
    • 一个函数中可能会存在多个局部内部类,编译后序号从1开始,是同名局部内部类编译的顺序序号

根据语法特殊性,成员内部类 和 局部内部类 可被分为:

  1. 成员内部类分为:普通成员内部类、静态内部类
    • 区别就在于加不加 static 关键字
  2. 局部内部类分为:普通局部内部类、匿名内部类
    • 匿名内部类就是没有给类取类名的。由于没有名字就无法再次使用了,所以在定义时马上产生对象,只能使用一次

image-20220510102513865.png