4 内部类
4.1 内部类概述
定义在其他类内部的类被称为内部类,包含内部类的类也被称为外部类。
内部类有如下作用:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
- 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,而同一个类的成员可以互相访问。但外部类不能访问内部类的实现细节。
- 匿名内部类适用于创建那些仅需要一次使用的类。
内部类与外部类的区别:
- 内部类比外部类可以多使用三个修饰符:private、protected、static。
- 非静态内部类不能拥有静态成员。
成员内部类是一种与成员变量、方法、构造器、初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。
成员内部类分为两种:非静态内部类和静态内部类。成员内部类的class文件总是这种格式:OuterClass$InnerClass.class。
4.2 非静态内部类
非静态内部类的成员可以直接访问外部类的private成员。非静态内部类对象里,保存了一个它所寄生的外部类对象的引用。
如果外部类需要访问非静态内部类的成员,则必须通过非静态内部类对象来访问其实例成员。
如果外部类成员变量、内部类成员变量和内部类方法的局部变量同名,则可通过使用外部类类名.this、this作为限定进行区分。
根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。
非静态内部类里不能有静态方法、静态成员变量、静态初始化块。
4.3 静态内部类
如果使用static来修饰一个内部类,则此内部类属于外部类本身,而不属于外部类的某个对象。
静态内部类可以包含静态成员,也可以包含非静态成员。静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。因为静态内部类没有外部类对象的this引用。
外部类的所有方法、所有初始化块可以使用静态内部类定义变量、创建对象。但外部类依然不能直接访问静态内部类的成员。
接口中可以定义静态内部类。
4.4 使用内部类
在外部类内部使用内部类:
在外部类内部使用内部类时,与使用普通类的方式没有区别。但外部类的静态成员不能使用非静态内部类。
在外部类以外使用非静态内部类:
此时非静态内部类不能用private修饰,否则无法再外部类以外使用。使用其他三种访问控制修饰符修饰的内部类,与普通类的使用规则相同。
非静态内部类的对象和非静态内部类子类的对象都必须持有指向外部类对象的引用,区别是创建两种对象时传入外部类对象的方式不同:当创建非静态内部类的对象时,必须通过外部类对象来调用new关键字;当创建非静态内部类子类的对象时,必须使用外部类对象作为调用者来调用非静态内部类子类的构造器。
在外部类以外定义内部类引用变量的语法格式如下:
OuterClass.InnerClass variable
在外部类以外的地方创建非静态内部类实例的语法格式如下:
OuterInstance.new InnerConstructor()
在外部类以外使用静态内部类:
因为静态内部类是外部类类相关的,因此创建静态内部类对象时无须创建外部类对象。
静态内部类和非静态内部类声明变量得方式相同,静态内部类创建对象的方式与普通类穿件对象的方式相同,但静态内部类前要加外部类前缀。
无法重写内部类:
内部类的类名默认加了外部类的类名作为一个包命名空间,即使子类中写一个和父类内部类具有相同名字的内部类,那么命名空间也不相同,所以无法重写。
4.5 局部内部类
如果把一个内部类放在方法里定义(注意,不是像创建匿名内部类那样,而是在方法内部。),则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。
局部内部类的class文件总是遵循如下命名方式:OuterClass$NInnerClass。数字N是为了区分同名的局部内部类。
4.6 匿名内部类
创建匿名内部类是会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。匿名内部类必须且只能实现一个父类,或只实现一个接口。
匿名内部类使用规则:
- 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。
- 匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义初始化块。
当通过实现接口来创建匿名内部类时,匿名内部类不能显示创建构造器,因此匿名内部类只有一个隐式的无参构造器,所以new接口名后的括号里不能传入参数值;但如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器。若父类构造器有参数,则new类名后的括号里传入的参数将直接赋值给父类的参数。
Java7之前,要求局部内部类、匿名内部类访问的局部变量必须使用final修饰;Java8以后,如果局部内部类被匿名内部类访问,那么该局部变量会自动变成被final修饰的状态。