Java—内部类

2018-05-11  本文已影响0人  1iangcc
树状图

1.什么是内部类?

在一个类A中声明一个类B,那么被声明的类B就是内部类,类A称为外部类。内部类虽然定义方式和属性有点相似但实质完全不同(内部类是类,而属性是类的成员)。
此外,内部类会隐式持有外部类对象的引用,因此在内部类中可以任意调用外部类中的方法。非静态内部类对象的创建依赖外部类对象,如果没有外部类的对象,也没有内部类的对象。

2.语法

public class syntax {

    public static void main(String[] args) {

        /*
        语法:声明内部类的类型 创建内部类对象
         */
        OutterClass outterClass = new OutterClass();
        OutterClass.InnerClass innerClass = outterClass.new InnerClass();

    }
}

class OutterClass {

    class InnerClass {

        /**
         * 语法:在内部类中获得外部类的引用
         */
        public OutterClass getOutterClass() {
            return OutterClass.this;
        }
    }
}

3.特性:

4.内部类的种类:

内部类可以被全部权限修饰符修饰,因此可以分为以下几种:

  1. 非private成员内部类
  1. private成员内部类
  1. 静态内部类
  1. 局部内部类
  1. 匿名内部类

5.为什么匿名内部类和局部内部类中引用外部定义的参数必须是final的?

简单版:
因为java中支持闭包,但对闭包的支持又不够彻底,如果需要在内部类中用到外部的参数,为了防止参数和内部类中调用的参数不一致,则参数必须是final的(java 8以后不需要显式写final,编译器自动添加 )。

详细版:
因为匿名内部类的外围环境是在方法中,而在内部类可以调用内部类外的参数,实际上已经构成了闭包。

但是方法的声明周期和类生命周期不同,当一个方法结束后,生命周期就完结了,而匿名内部类的生命周期和普通类一样,且匿名内部类可能还会被它所向上转型的基类或接口引用所回调。

所以为了防止生命周期带来的问题,编译器在编译匿名内部类时会把所引用到的外部参数拷贝一份到内部类中,为了防止内外参数的一致性,所以参数必须是final的。

同样地,在局部内部类中也有类似的闭包问题。
另外,在jdk1.8后,编译器自动隐式为我们添加final。

那么,为什么匿名内部类中调用外部类中的方法或参数不需要final?
因为内部类持有一个外部类的引用,回调时候一定能访问到。

6.内部类被继承:特殊的构造方法

非private的成员内部类和静态内部类可以被继承,静态内部类的继承和普通类相同,但成员内部类的继承有点特殊:

public class OutterClass {

    public class InnerClass {
        String name;

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

class InnerClassChild extends OutterClass.InnerClass {

    //特殊的构造函数
    public InnerClassChild(OutterClass outterClass, String name) {
        outterClass.super(name);
    }
}

如果反编译的话我们可以看到构造函数实际是这样的:

public InnerClassChild(OutterClass outterClass, String name) {
        super(outterClass, name);
}

因此看上去调用super()构造方法的是外部类的对象,实际上只是把外部类对象传入而已。
所以实际上这句特殊的语法执行的是:
构造InnerClassChild之前,先调用它的父类OutterClass.InnerClass的构造方法,但创建内部类必须通过外部类的对象,所以需要传入外部类的对象。

7.内部类的编译

所有内部类被编译器编译后都会产生class文件,class文件命名规则按内部类种类分3种情况:

实例:

/**
 * 外部类
 */
public class ParameterTest {

    /**
     * 内部类
     */
    public class PP{

        /**
         * 内部类中的内部类
         */
        public class GG {

        }
    }

    public IteratorTest getIteratorTest(){
        /**
         * 匿名内部类
         */
        return new IteratorTest(){

        };
    }
}

上面的代码编译可得下面的字节码文件:

对应的class文件

8.内部类的应用

上一篇 下一篇

猜你喜欢

热点阅读