前言
正文
内部类的分类
内部类可以分为:静态内部类(嵌套类)和非静态内部类。非静态内部类又可以分为:成员内部类、方法内部类、匿名内部类。
静态内部类和非静态内部类的区别
- 静态内部类可以有静态成员,而非静态内部类则不能有静态成员。
- 静态内部类可以访问外部类的静态变量,而不可访问外部类的非静态变量。
- 非静态内部类的非静态成员可以访问外部类的非静态变量。
- 静态内部类的创建不依赖于外部类,而非静态内部类必须依赖于外部类的创建而创建。
我们通过一个例子就可以很好的理解这几点区别:
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) {
// 非静态内部类 创建方式1
ClassOuter.InnerClass innerClass = new ClassOuter().new InnerClass();
// 非静态内部类 创建方式2
ClassOuter 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 {
@Override
public void funA() {
System.out.println("ClassA - funA");
}
}
private class SubClassB extends ClassB {
@Override
public 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 - funA
ClassB - funB
通过上面的例子可以看出,虽然 MultiInheritanceDemo 类不能同时继承 ClassA 和 ClassB,但是它可以通过内部类拥有 ClassA 和 ClassB 两个类的继承关系,且无需关注 ClassA 中 funC 方法的实现。
转载
更多内容参考如下两篇文章,在 Java 核心技术 卷1 中描述了内部类编译之后的表现形式,更好的解释了内部类是如何实现访问外部类的。
另外,在搞懂 JAVA 内部类的文章中,提到了内部类会造成程序内存泄漏的问题,可以看看。
- 搞懂 JAVA 内部类
- Java 核心技术 卷1 基础知识 第10版
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/pvyq0q 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。