前言

简单介绍 Java 内部类的分类,以及常见的使用案例。

正文

内部类的分类

内部类可以分为:静态内部类(嵌套类)和非静态内部类。非静态内部类又可以分为:成员内部类、方法内部类、匿名内部类。

静态内部类和非静态内部类的区别

  1. 静态内部类可以有静态成员,而非静态内部类则不能有静态成员。
  2. 静态内部类可以访问外部类的静态变量,而不可访问外部类的非静态变量。
  3. 非静态内部类的非静态成员可以访问外部类的非静态变量。
  4. 静态内部类的创建不依赖于外部类,而非静态内部类必须依赖于外部类的创建而创建。

我们通过一个例子就可以很好的理解这几点区别:

  1. public class ClassOuter {
  2. private int noStaticInt = 1;
  3. private static int STATIC_INT = 2;
  4. public void fun() {
  5. System.out.println("外部类方法");
  6. }
  7. public class InnerClass {
  8. //static int num = 1; 此时编辑器会报错,非静态内部类则不能有静态成员
  9. public void fun() {
  10. // 非静态内部类的非静态成员可以访问外部类的非静态变量。
  11. System.out.println(STATIC_INT);
  12. System.out.println(noStaticInt);
  13. }
  14. }
  15. public static class StaticInnerClass {
  16. static int NUM = 1;//静态内部类可以有静态成员
  17. public void fun() {
  18. System.out.println(STATIC_INT);
  19. //System.out.println(noStaticInt); 此时编辑器会报错,不可访问外部类的非静态变量
  20. }
  21. }
  22. }
  23. public class TestInnerClass {
  24. public static void main(String[] args) {
  25. // 非静态内部类 创建方式1
  26. ClassOuter.InnerClass innerClass = new ClassOuter().new InnerClass();
  27. // 非静态内部类 创建方式2
  28. ClassOuter outer = new ClassOuter();
  29. ClassOuter.InnerClass inner = outer.new InnerClass();
  30. // 静态内部类的创建方式
  31. ClassOuter.StaticInnerClass staticInnerClass = new ClassOuter.StaticInnerClass();
  32. }
  33. }

局部内部类

如果一个内部类只在一个方法中使用到了,那么我们可以将这个类定义在方法内部,这种内部类被称为局部内部类,其作用域仅限于该方法。

局部内部类有几点值得我们注意的地方:

  1. 局部内类不能用 public,protected,private 访问修饰符进行声明。
  2. 局部内部类对外完全隐藏,除了创建这个类的方法可以访问它,其他的地方是不允许访问的。
  3. 对比成员内部类,局部内部类还可以访问局部变量,但该变量必须声明为 final,且在内部类中不允许对该变量重新赋值。

    1. public class ClassOuter {
    2. private int noStaticInt = 1;
    3. private static int STATIC_INT = 2;
    4. public void fun() {
    5. System.out.println("外部类方法");
    6. }
    7. public void testFunctionClass(String params) {
    8. class FunctionClass {
    9. private void fun() {
    10. System.out.println("局部内部类的输出");
    11. System.out.println(STATIC_INT);
    12. System.out.println(noStaticInt);
    13. System.out.println(params);
    14. //params ++ ; // params 不可变所以这句话编译错误
    15. }
    16. }
    17. FunctionClass functionClass = new FunctionClass();
    18. functionClass.fun();
    19. }
    20. }

    匿名内部类

  4. 匿名内部类没有访问修饰符。

  5. 匿名内部类必须继承一个抽象类或者实现一个接口。
  6. 匿名内部类中不能存在任何静态成员或静态方法。
  7. 匿名内部类是没有构造方法的,因为它没有类名。
  8. 与局部内部类一样,匿名内部类也可以引用局部变量,该变量也必须声明为 final。

    1. public class Button {
    2. public void click(final int params) {
    3. // 匿名内部类,实现的是ActionListener接口
    4. new ActionListener() {
    5. public void onAction() {
    6. System.out.println("click action..." + params);
    7. }
    8. }.onAction();
    9. }
    10. // 匿名内部类必须继承或实现一个已有的接口
    11. public interface ActionListener {
    12. public void onAction();
    13. }
    14. public static void main(String[] args) {
    15. Button button = new Button();
    16. button.click(1);
    17. }
    18. }

    使用案例

    使用内部类实现多继承

在 Java 中,一个类只能继承一个父类,不支持多继承。所以,一般多继承是通过接口来实现的。但是使用接口也有不方便的地方。比如实现一个接口就必须实现它里面的所有方法(Java 8 之前)。而有了内部类就不一样了,我们可以通过内部类来实现多继承。比如下面的例子:

  1. public abstract class ClassA {
  2. public abstract void funA();
  3. public void funC() {
  4. System.out.println("ClassA - funC");
  5. }
  6. }
  7. public abstract class ClassB {
  8. public abstract void funB();
  9. }
  10. public class MultiInheritanceDemo {
  11. private class SubClassA extends ClassA {
  12. @Override
  13. public void funA() {
  14. System.out.println("ClassA - funA");
  15. }
  16. }
  17. private class SubClassB extends ClassB {
  18. @Override
  19. public void funB() {
  20. System.out.println("ClassB - funB");
  21. }
  22. }
  23. public void funA() {
  24. new SubClassA().funA();
  25. }
  26. public void funB() {
  27. new SubClassB().funB();
  28. }
  29. public static void main(String[] args) {
  30. MultiInheritanceDemo multiInheritanceDemo = new MultiInheritanceDemo();
  31. multiInheritanceDemo.funA();
  32. multiInheritanceDemo.funB();
  33. }
  34. }

运行程序,输出:

  1. ClassA - funA
  2. ClassB - funB

通过上面的例子可以看出,虽然 MultiInheritanceDemo 类不能同时继承 ClassA 和 ClassB,但是它可以通过内部类拥有 ClassA 和 ClassB 两个类的继承关系,且无需关注 ClassA 中 funC 方法的实现。

转载

更多内容参考如下两篇文章,在 Java 核心技术 卷1 中描述了内部类编译之后的表现形式,更好的解释了内部类是如何实现访问外部类的。

另外,在搞懂 JAVA 内部类的文章中,提到了内部类会造成程序内存泄漏的问题,可以看看。

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/pvyq0q 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。