last modified: 2020/10/31
6.4 内部类
- 内部类(inner class)是定义在另一个类中的类。
- 使用的主要原因是:
- 内部类方法可以访问该类定义坐在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
6.4.1 使用内部类访问对象状态
- 内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域
- 内部类的对象总是有一个隐式引用,它指向了创建它的外部类对象。
- 这个引用在内部类的定义中时不可见的
- 外围类的引用在构造器中设置。
- 编译器修改所有的内部类的构造器,添加一个外围类引用的参数。
- 只有内部类可以是私有类,而常规类只可以具有包可见性,或公有可见性
6.4.2 内部类的特殊语法规则
-
外围类引用表达式
OuterClass.this
-
可以采用下列语法格式更加明确地编写内部对象的构造器:
outerObject.new InnerClass (construction parameters)- 例如,ActionListener listener = this.new TinePrinter0;
在这里,最新构造的TimePrinter对象的外围类引用被设置为创建内部类对象的方法中的this引用。这是一种最常见的情况。通常,this 限定词是多余的。
- 例如,ActionListener listener = this.new TinePrinter0;
-
不过,可以通过显式地命名将外围类引用设置为其他的对象。
-
需要注意,在外围类的作用域之外,可以这样引用内部类:
OuterClass. InnerClass -
内部类中声明的所有静态域都必须是final。 原因很简单。我们希望一个静态域只
有一个实例,不过对于每个外部对象,会分别有一个单独的内部类实例。如果这个域不是final,它可能就不是唯一的。 -
内部类不能有static方法。Java 语言规范对这个限制没有做任何解释。
6.4.3 内部类是否有用、必要和安全(!!!)
- 内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成用$ (美元符号)分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知。
6.4.4 局部内部类
- 局部类不能用public或private访问说明符进行声明。
- 它的作用域被限定在声明这个局部类的块中。
- 局部类有一个优势,即对外部世界可以完全地隐藏起来。即使TalkingClock类中的其他
代码也不能访问它。除start方法之外,没有任何方法知道TimePrinter类的存在。
6.4.5 由外部方法访问变量
- 与其他内部类相比较,局部类还有一个优点。
- 它们不仅能够访问包含它们的外部类,还可以访问局部变量。
- 不过,那些局部变量必须事实上为final。这说明,它们一旦赋值就绝不会改变。
- 在JAVA SE 8之前,必须把局部类访问的局部变量声明为fianl
6.4.6 匿名内部类
-
anonymous inner class
-
通常的语法格式为:
new Super Typel(construction parameters){
inner class methods and data
}- 其中,SuperType 可以是ActionListener这样的接口,于是内部类就要实现这个接口。
- SuperTlype也可以是一个类, 于是内部类就要扩展它。
-
由于构造器的名字必须与类名相同,而匿名类没有类名,所以,匿名类不能有构造器。
取而代之的是,将构造器参数传递给超类( superclass)构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。不仅如此,还要像下面这样提供一组括号:
new InterfaceType(){
***methods and data***
}
- 多年来,Java程序员习惯的做法是用匿名内部类实现事件监听器和其他回调。如今最好
还是使用lambda表达式。
6.4.7 静态内部类
- 有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用
外围类对象。为此,可以将内部类声明为static,以便取消产生的引用。 - 当然,只有内部类可以声明为static。静态内部类的对象除了没有对生成它的外围类对象
的引用特权外,与其他所有内部类完全一样。 - 在内部类不需要访问外围类对象的时候,应该使用静态内部类。有些程序员用嵌套类(nested class)表示静态内部类。
- 与常规内部类不同,静态内部类可以有静态域和方法。
- 声明在接口中的内部类自动成为static和public类。