一、内部类
内部类是定义在另一个类中的类。
使用内部类的主要原因有以下几点:
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
1.1 使用内部类访问对象状态
内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。
内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。外围类的引用在构造器中设置。编译器修改了所有的内部类的构造器,添加一个外围类引用的参数。当在外围类中创建内部类对象时,编译器就会将this引用传递给当前内部类构造器。
一个常规类A想要访问另一个类B的数据域,需要通过类B的公有方法。而将A设为内部类可以给予改进,即类B不必提供仅用于访问其他类的访问器。
内部类的这种访问特权使其与常规类比较起来功能更加强大。
注意:常规类的访问控制修饰符只有public(公有可见性)与default(即缺省,包可见性)。但内部类可以是private的,这时只有其外部类中的方法才能构造内部类对象。
1.2 内部类特殊语法规则
使用外围类引用的语法:
OuterClass.this
表示外围类引用。如:
public void actionPerformed(ActionEvent event) {
…
if (TalkingClick.this.beep) Toolkit.getDefaultToolkit().beep();
}
outerObject.new InnerClass(construction parameters)
用于更加明确地编写内部对象的构造器。如:
ActionListener listener = this.new TimePrinter();
当然,也可以通过显示地命名将外围类引用设置为其他的对象。如如果TimePrinter
是TalkingClock
的公有内部类,可利用下述语句构造一个TimePrinter
:
TalkingClock jabberer = new Talking(10,true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
另外,在外围类的作用域之外,可以这样引用内部类:
OuterClass.InnerClass
注意:
内部类中声明的所有静态域都必须是final。
内部类不能有static方法。Java语言规范对这个限制没有做任何解释。也可以允许有静态方法,但只能访问外围类的静态域和方法。
1.3 内部类是否有用、必要与安全性
内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成$(美元符号)分隔外部类名与内部类名的常规类文件,而虚拟机对此一无所知。
如在TalkingClock
类内部的TimePrinter
类将被翻译成类文件TalkingClock.TimePrinter.class
。
通过反射可以查看,编译器为了引用外围类,在内部类中生成了附加的实例域this$0(名字由编译器合成,在自己编写的代码中不能引用它)。另外,在内部类的构造器中也自动添加了外围类参数。【详见"Java 核心技术卷I” P248】
内部类的访问特权的管理:
利用反射查看外部类,发现编译器在外围类中添加了静态方法,它将返回作为参数传递给它的对象域。【详见"Java 核心技术卷I” P249】
1.4 局部内部类
当一个内部类名字只在一个方法中创建该内部类对象时使用,可将该内部类定义在方法中,成为局部内部类。
public void start() {
class TimePrinter implements ActionListener {
public void actionPerformed (ActionEvent event) {
System.out.println("At the tone, the time is " + new Date());
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval,listener);
t.start();
}
局部类不能用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。
局部类优势:
1、局部类对外部世界可以完全隐藏起来,除该内部类声明所在的方法外,没有任何方法知道其存在。
2、局部类不仅能够访问包含它们的外部类,还可以访问局部变量。不过那些局部变量必须事实上为final。
1.5 匿名内部类
1、假如只创建这个类的一个对象,就不必对类进行命名了,这种类被称为匿名内部类。
2、创建匿名内部类的通用语法格式为:
new SuperType (construction parameters) {
inner class methods and data
}
其中superType
可以是接口,于是内部类就要实现这个接口;superType
也可以是一个类,于是内部类就要扩展它。
例如:
public void start(int interval, boolean beep) {
ActionListener listener = new ActionListenerr() {
public void actionPerformed(ActionEvent event) {
System.out.println("At the tone, the time is " + new Date());
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval,listener);
t.start();
}
【如果构造参数的闭小括号后面跟一个开大括号,正在定义的就是匿名内部类。】
3、由于匿名类没有类名,所以匿名类不能有构造器。取而代之的是,将构造器参数传递给超类(superclass)构造器。尤其是在内部类实现接口时,不能有任何构造参数。
4、Java程序员习惯性做法是用匿名内部类实现事件监听器和其他回调。如今最好还是使用Lambda表达式。
1.6 静态内部类
1、当使用内部类的目的只是为了把一个类隐藏在另外一个类的内部,而并不需要内部类引用外部类对象,此时可将内部类声明为static,以便取消产生的引用。
2、如果内部类对象是在外部类的静态方法中构造的,必须使用静态内部类。
3、注意:
与常规内部类不同,静态内部类可以有静态域和方法。
声明在接口中的内部类自动成为static和public类。