内部类
内部类(inner class)是定义在另一个类中的类。
内部类对象有一个隐式引用。他引用了实例化该内部类对象的外围类对象。通过这个指针可以访问外围内部类的全部状态。
内部类使用的主要原因
使用内部类的主要原因有以下三种:
1、内部类方法可以访问该内部类定义所在类的作用域中的数据,包括私有数据。
2、内部类可以对同一包中的其他类隐藏起来。
3、当想要定义一个回调函数(GUI设计时)且不想编写大量代码时,使用匿名内部类比较便捷。
内部类的种类
java中内部类分为四种:成员内部类、静态内部类、局部内部类和匿名内部类。
创建内部类的格式及引用格式
要创建内部类对象,可以用outer.inner obj = outerobj.new inner();
使用外围类的引用正规语法的表达式为:OuterClass.this
如当外部类为TalkingClock内部类为TimePrinter
TalkingClock jabberer = new TalkingClock();
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
1、成员内部类
成员内部类就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。同时外部类要访问内部类的所有成员变量/方法则需 要通过内部类的对象来获取。注意:成员内部类中不能出现static的变量和方法,因为成员内部类要先创建外部类,才能创建内部类。在成员内部类要引用外围类对象时,使用outer.this来表示外部类对象。
如下例所示:
如下示例演示一个每隔十秒发一次通告的小测试程序。
package innerClass;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class InnerClassTest {
public static void main(String[] args) {
// TODO 自动生成的方法存根
TalkingClock clock = new TalkingClock(1000,true);
clock.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
TalkingClock类的定义
class TalkingClock // 定义类
{
private int interval; // 类中有两个实例域
private boolean beep;
public TalkingClock(int interval,boolean beep) // 定义了构造方法
{
this.interval = interval;
this.beep = beep;
}
public void start() // 类中方法定义
{
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval,listener); // 实例定时器对象
t.start(); // 调用Timer对象的start方法,启动定时器
}
public class TimePrinter implements ActionListener // 定义一个内部类,实现ActionListener接口用于通知定时器知道调用哪个方法
{
/*
外围类的引用在构造器中设置。编译器修改了所有的内部类的构造器,添加一个外围类引用的参数。因为TimePrinter类没有定义构造器,所 以编译器为这个类生成了一个默认的构造器:
public TimePrinter(TalkingClock clock) 自动生成的代码
{
outer = clock;
}
*/
public void actionPerformed(ActionEvent event)
{
Date now = new Date(); // 实例化日期对象
System.out.println("At the tone,the time is"+now);
if(beep) // if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
Toolkit.getDefaultToolkit().beep();
}
}
}
2、局部内部类
局部内部类不能用public和private访问说明符进行声明。他的作用域被限定在声明这个局部内部类的块中。
局部内部类有一个优势:即对外部世界可以完全地隐藏起来。只对定义他的方法可见。且局部内部类不仅能够访问包含他们的外部类,还可以访问局部变量,但注意那些局部变量一定要被声明为final。
局部内部类可以定义在方法中:
public class Parcel4{
public Destination destination(String s){
class PDestination implements Destionation{
private String label1;
private Destination (String whereTo){
label1 = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args){
Parcel4 p = new Parcel4();
Destination d = p.destination("Tasmanis");
}
}
也可以定义在作用域里:
public class Parcel4{
private void internalTracking(boolean){
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s){
id=s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts=new TrackingSlip("slip");
String s=ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args){
Parcel5 p = new Parcel5();
p.track();
}
}
3、匿名内部类
匿名内部类是不能加访问修饰符的。将局部内部类的使用在深入,加入只创建这个类的一个对象,就不必命名了。 如下实例:
public void start() // 含义为:创建一个实现ActionListener接口的类的新对象,需要实现的方法actionPerformed定义在括号{}内
{
ActionListener listener = new TimePrinter();
{
Date now = new Date(); // 实例化日期对象System.out.println("At the tone,the time is"+now);
if(beep) // if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
Toolkit.getDefaultToolkit().beep();
}
Timer t = new Timer(interval,listener); // 实例定时器对象
t.start(); // 调用Timer对象的start方法,启动定时器
}
匿名内部类的通用格式:newSuperType(construction parameters){inner class methods an data}
SuperType可以是ActionListener这样的接口,于是内部类就要实现这个接口。SuperType也可以是一个类,于是内部类就要扩展它。
由于构造器的名字必须与类名相同,而匿名内部类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给超类构造器,尤其是在内部类实现接口的时候,不能有任何构造参数。不仅如此还要像下面这样提供一组括号:
new InterfaceType()
{
methods an data
}
匿名内部类和局部内部类一样也像别的类一样进行编译,但只是作用域不同而已,只在该方法或条件的作用域内才能使用,退出这些作用域后无法引用的。但有时final限制显得不太方便,假设想更新在一个封闭作用域内的计数器。如下例所示:
int counter = 0;
Date[] dates = new Date[100];
for(int i = 0li<dates.length;i++)
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter++; // 错误,只能访问final修饰的局部变量。由于counter需要变化却不能声明为final。补救措施为让他引用一个数组。
return super.compareTo(other);
}
};
补救改良:(数组变量被final修饰,仅仅代表不可以让他在引用另外一个数组,数组中的数据可以自由地更改)
final int[] counter = new int[1];
for(int i =0;i<dates.length;i++)
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter++; // 错误,只能访问final修饰的局部变量。由于counter需要变化却不能声明为final。补救措施为让他引用一个数组。
return super.compareTo(other);
}
};
4、静态内部类(嵌套类)
有时候,使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外围类对象。为此可以将内部类声明为static。
声明在接口中的内部类自动称为static和public类。