Fork me on GitHub

Java中的内部类

Java中的内部类含义就是在一个Java类A的作用域下定义了一个类B,类B就称作为类A的内部类。内部类的机制很强大,在类B中不仅可以访问自身的作用域的数据,还可以访问外围类的实例域。在Java中,内部类分为四种:普通内部类(成员内部类)、局部内部类、匿名内部类、静态内部类(或嵌套类)

内部类原理

JVM中是不存在内部类的,在编译器中,就将内部类编译成了一个【外部类$内部类】作为类名的类,在JVM中外部类与内部类是两个单独的类:

# 编译下述代码会产生两个文件:Outer.class和Outer$Inner.class
class Outer {
class Inner{}
}

在内部类代码中,编译器为这个类默认生成了一个构造器,将外部类作为参数传递进来,如下图,用javap -private查看所有类和成员,可看到内部类的结构,OuterClass为外部类,FormatHobby为内部类:

在外部类中会生成静态方法,用于内部类进行调用,以下access$000就是提供给内部类调用的静态方法。内部类使用了外部类中的String[] hobby,于是通过access$000返回一个String[],提高访问权限:

普通内部类(或称为成员内部类)

定义一个普通内部类

点击查看代码
/**
* Company: XXXXXX
*
* @description:
* @author: banmao
* @date: 2022/1/10 9:49
*/
public class OuterClass {

  private String name;
  private String[] hobby;

  public OuterClass(String name, String[] hobby) {
    this.name = name;
    this.hobby = hobby;
  }

  private void toIntroduceMyself() {
    // 自我介绍
    FormatHobby formatHobby = new FormatHobby();
    System.out.println("My name is " + name + ",My hobbies are " + formatHobby.format());
  }

  private class FormatHobby {
    // 内部类
    private String format() {
      String hobbyStr = "";
      for (String str : hobby) {
        hobbyStr += str + ",";
      }
      return hobbyStr.substring(0, hobbyStr.length() - 1);
    }
  }

  public static void main(String[] args) {
    OuterClass outerClass = new OuterClass("outerClass", new String[]{"篮球", "台球"});
    outerClass.toIntroduceMyself();
  }
}

几个关键的点

1、如上,定义了一个普通内部类FormatHobby,且访问修饰符为private,这也是内部类独有的,外部类只能是public或package的访问权限,内部类的访问修饰符决定了内部类对外部其它类可不可见。private代表仅仅对当前所在的OuterClass可见。
2、普通内部类中不能拥有静态属性,静态方法,例:AB代表外部类,CD代表内部类,A.C与B.D,C与D如果有共享的静态属性,但是这个属性对A,B来说应该是不共享的,因为A与B是单独的两个对象,对象的数据应该隔离,所以C与D不应该有共享的数据。
3、在外部类的main方法中无法实例化内部类,因为内部类实例中默认传的是this关键字进行构造,比如FormatHobby formatHobby = new FormatHobby(this);,this关键字是属于对象的,不能在static方法中使用。

局部内部类

点击查看代码
/**
* Company: XXXXXX
*
* @description:
* @author: banmao
* @date: 2022/1/10 9:49
*/
public class OuterClass2 {

public String name;
private String[] hobby;

public OuterClass2(String name, String[] hobby) {
  this.name = name;
  this.hobby = hobby;
}

private void toIntroduceMyself(String appendHobby) {
  // 自我介绍
  class FormatHobby {
    private String format() {
      String hobbyStr = "";
      for (String str : hobby) {
        hobbyStr += str + ",";
      }
    return hobbyStr + appendHobby;
    }
  }
  FormatHobby formatHobby = new FormatHobby();
  System.out.println("My name is " + name + ",My hobbies are " + formatHobby.format());
}

public static void main(String[] args) {
  OuterClass2 outerClass = new OuterClass2("outerClass", new String[]{"篮球", "台球"});
  outerClass.toIntroduceMyself("乒乓球");
}
}

几个关键点

1、局部类不能用private或private进行声明,局部内部类的访问权限控制在了声明这个局部类的块中
2、局部类可以访问局部变量,但是那些局部变量必须事实上为final,事实上为final的含义是表明这个变量一旦声明则不改变
3、不能定义静态成员与静态方法,不能是public,protected,private,static

匿名内部类

点击查看代码
/**
 * Company: XXXXXX
 *
 * @description:
 * @author: banmao
 * @date: 2022/1/10 9:49
 */
public class OuterClass3 {

    public String name;
    private String[] hobby;

    public OuterClass3(String name, String[] hobby) {
        this.name = name;
        this.hobby = hobby;
    }

    private void toIntroduceMyself(String appendHobby) {
        FormatHobbyInterface formatHobby = new FormatHobbyInterface() {
            public String format() {
                String hobbyStr = "";
                for (String str : hobby) {
                    hobbyStr += str + ",";
                }
                return hobbyStr + appendHobby;
            }
        };
        System.out.println("My name is " + name + ",My hobbies are " + formatHobby.format());
    }

    public static void main(String[] args) {
        OuterClass3 outerClass = new OuterClass3("outerClass", new String[]{"篮球", "台球"});
        outerClass.toIntroduceMyself("乒乓球");
    }
}

几个关键点

1、匿名内部类含义是创建一个类或接口的没有类名的,内容包含在{}中的子类或实现类
2、可以用Java8 Lambda表达式来改写,表现的更加简洁
3、常用做法是使用匿名内部类来实现事件监听器和其他回调
4、匿名内部类不能有构造方法(因为没有类名)
5、匿名内部类属于局部内部类,局部内部类的所有限制对它都有效

静态内部类(或称为嵌套类)

点击查看代码
/**
 * Company: XXXXXX
 *
 * @description:
 * @author: banmao
 * @date: 2022/1/10 9:49
 */
public class OuterClass4 {

    public String name;

    public OuterClass4(String name) {
        this.name = name;
    }

    private void toIntroduceMyself(String appendHobby) {
        System.out.println("My name is " + name + ",My hobbies are " + appendHobby);
    }

    public static void main(String[] args) {
        OuterClass4 outerClass = new OuterClass4("outerClass");
        outerClass.toIntroduceMyself("乒乓球");
    }

    public static class StaticInnerClass {
        // 静态内部类
        private static String name = "静态内部类";

        private String hobby;

        public StaticInnerClass(String hobby) {
            this.hobby = hobby;
        }

        public static void toIntroduceMyself() {
            System.out.println("我是:" + name);
        }

        public void printHobby() {
            System.out.println("我是:" + name + ",我的爱好是:" + hobby);
        }

    }
}

几个关键点

1、静态内部类与其他内部类的不同在于,静态内部类其实算做一个顶级类,它对外部类并没有引用关系
2、静态内部类可以有静态属性与静态方法,静态内部类只能访问外部类的静态属性与方法
3、静态内部类的使用场景一般是通过类嵌套的结构来命名控制(比如避免命名冲突)

总结

1、四种内部类,成员内部类、局部内部类、匿名内部类、静态内部类
2、比较常用的为成员内部类与匿名内部类
3、在JVM中是不存在内部类概念,编译器将内部类解析成了顶级类

扩展

1、内部类都可以无限套娃(嵌套定义)吗?都可以
2、在接口、抽象类、枚举、注解中都可以定义内部类吗?

接口中不能有成员内部类,可以有静态内部类,使用java1.8的default关键字的话,可以定义局部内部类与匿名内部类

点击查看代码
/**
 * Company: XXXXXX
 * Description:
 *
 * @author banmao
 * @date 2022/1/12 8:55
 */
public interface InnerClassInterface {

    class InnerClass {
        // 静态内部类,public与static为缺省
    }

    default void print() {

        class LocalInnerClass {
            // 局部内部类
        }

        TestInterface testInterface = new TestInterface() {

            @Override
            public void printTest() {
                System.out.println("匿名内部类");
            }
        };
        testInterface.printTest();
    }
}

自定义注解中只能允许有静态内部类,default关键字不能使用在注解类中,java中的自定义注解相当于一个接口

点击查看代码
public @interface TestAnnotation {

    class InnerClass {
        // 静态内部类,public与static为缺省
    }
}

枚举类相当于一个类,其中定义的枚举类型就是继承此类的多个实例,所以在枚举类中,四种内部类都可以定义

点击查看代码
public enum TestEnum {

    SUCCESS,
    FAILURE;

    class InnerClass {
        // 成员内部类
    }

    public void print() {

        class LocalInnerClass {
            // 局部内部类
        }

        TestInterface testInterface = new TestInterface() {

            @Override
            public void printTest() {
                System.out.println("匿名内部类");
            }
        };
        testInterface.printTest();
    }

    static class StaticInnerClass {
        // 静态内部类
    }
}

抽象类中四种内部类都可以定义

代码类似于上述的枚举类,故代码省略

3、内部类还可以继承其它类吗?可以,每个内部类都可以独立的继承其它类,这也是Java中“多重继承”的一种补充

posted @ 2022-01-12 09:45  三脚半猫  阅读(161)  评论(0)    收藏  举报