前言
正文
内部类的分类
内部类可以分为:静态内部类(嵌套类)和非静态内部类。非静态内部类又可以分为:成员内部类、方法内部类、匿名内部类。
静态内部类和非静态内部类的区别
- 静态内部类可以有静态成员,而非静态内部类则不能有静态成员。
 - 静态内部类可以访问外部类的静态变量,而不可访问外部类的非静态变量。
 - 非静态内部类的非静态成员可以访问外部类的非静态变量。
 - 静态内部类的创建不依赖于外部类,而非静态内部类必须依赖于外部类的创建而创建。
 
我们通过一个例子就可以很好的理解这几点区别:
public class ClassOuter {private int noStaticInt = 1;private static int STATIC_INT = 2;public void fun() {System.out.println("外部类方法");}public class InnerClass {//static int num = 1; 此时编辑器会报错,非静态内部类则不能有静态成员public void fun() {// 非静态内部类的非静态成员可以访问外部类的非静态变量。System.out.println(STATIC_INT);System.out.println(noStaticInt);}}public static class StaticInnerClass {static int NUM = 1;//静态内部类可以有静态成员public void fun() {System.out.println(STATIC_INT);//System.out.println(noStaticInt); 此时编辑器会报错,不可访问外部类的非静态变量}}}public class TestInnerClass {public static void main(String[] args) {// 非静态内部类 创建方式1ClassOuter.InnerClass innerClass = new ClassOuter().new InnerClass();// 非静态内部类 创建方式2ClassOuter outer = new ClassOuter();ClassOuter.InnerClass inner = outer.new InnerClass();// 静态内部类的创建方式ClassOuter.StaticInnerClass staticInnerClass = new ClassOuter.StaticInnerClass();}}
局部内部类
如果一个内部类只在一个方法中使用到了,那么我们可以将这个类定义在方法内部,这种内部类被称为局部内部类,其作用域仅限于该方法。
局部内部类有几点值得我们注意的地方:
- 局部内类不能用 public,protected,private 访问修饰符进行声明。
 - 局部内部类对外完全隐藏,除了创建这个类的方法可以访问它,其他的地方是不允许访问的。
 对比成员内部类,局部内部类还可以访问局部变量,但该变量必须声明为 final,且在内部类中不允许对该变量重新赋值。
public class ClassOuter {private int noStaticInt = 1;private static int STATIC_INT = 2;public void fun() {System.out.println("外部类方法");}public void testFunctionClass(String params) {class FunctionClass {private void fun() {System.out.println("局部内部类的输出");System.out.println(STATIC_INT);System.out.println(noStaticInt);System.out.println(params);//params ++ ; // params 不可变所以这句话编译错误}}FunctionClass functionClass = new FunctionClass();functionClass.fun();}}
匿名内部类
匿名内部类没有访问修饰符。
- 匿名内部类必须继承一个抽象类或者实现一个接口。
 - 匿名内部类中不能存在任何静态成员或静态方法。
 - 匿名内部类是没有构造方法的,因为它没有类名。
 与局部内部类一样,匿名内部类也可以引用局部变量,该变量也必须声明为 final。
public class Button {public void click(final int params) {// 匿名内部类,实现的是ActionListener接口new ActionListener() {public void onAction() {System.out.println("click action..." + params);}}.onAction();}// 匿名内部类必须继承或实现一个已有的接口public interface ActionListener {public void onAction();}public static void main(String[] args) {Button button = new Button();button.click(1);}}
使用案例
使用内部类实现多继承
在 Java 中,一个类只能继承一个父类,不支持多继承。所以,一般多继承是通过接口来实现的。但是使用接口也有不方便的地方。比如实现一个接口就必须实现它里面的所有方法(Java 8 之前)。而有了内部类就不一样了,我们可以通过内部类来实现多继承。比如下面的例子:
public abstract class ClassA {public abstract void funA();public void funC() {System.out.println("ClassA - funC");}}public abstract class ClassB {public abstract void funB();}public class MultiInheritanceDemo {private class SubClassA extends ClassA {@Overridepublic void funA() {System.out.println("ClassA - funA");}}private class SubClassB extends ClassB {@Overridepublic void funB() {System.out.println("ClassB - funB");}}public void funA() {new SubClassA().funA();}public void funB() {new SubClassB().funB();}public static void main(String[] args) {MultiInheritanceDemo multiInheritanceDemo = new MultiInheritanceDemo();multiInheritanceDemo.funA();multiInheritanceDemo.funB();}}
运行程序,输出:
ClassA - funAClassB - funB
通过上面的例子可以看出,虽然 MultiInheritanceDemo 类不能同时继承 ClassA 和 ClassB,但是它可以通过内部类拥有 ClassA 和 ClassB 两个类的继承关系,且无需关注 ClassA 中 funC 方法的实现。
转载
更多内容参考如下两篇文章,在 Java 核心技术 卷1 中描述了内部类编译之后的表现形式,更好的解释了内部类是如何实现访问外部类的。
另外,在搞懂 JAVA 内部类的文章中,提到了内部类会造成程序内存泄漏的问题,可以看看。
- 搞懂 JAVA 内部类
 - Java 核心技术 卷1 基础知识 第10版
 
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/pvyq0q 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
