JAVA内部类_1

时间:2021-03-23 22:48:23

使用内部类的原因:

(1)可以访问该类定义所在作用域中的数据,包括私有数据。

(2)可以对同一个包中的其它类隐藏起来。

(3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。

下面主要分几种情况进行讨论:

(a)使用内部类访问对象状态

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();
	}

	/** 内部类 **/
	/**将TimePrinter声明为私有,只有TalkingClock能够生成**/
	private class TimePrinter implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (beep) {
				Toolkit.getDefaultToolkit().beep();
			}
		}
	}
}

为了能够让程序正常运行,内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。

这个引用在内部类的定义中是不可见的。为了进一步说明,将外围类对象的引用称为outer。actionPerformed方法等价于下列形式:

public void actionPerformed(ActionEvent e) {
	if (outer.beep) {
	      Toolkit.getDefaultToolkit().beep();
	}
}

外围类的引用在构造器中设置。,编译器修改了所有内部类的构造器,并添加一个外围类引用的参数。

由于TimePrinter没有构造器,编译器会为其生成一个默认构造器(outer在此仅用来说明内部类的机制):

public TimePrinter(TalkingClock clock){
 	  outer = clock
}

当在start方法中创建了TimePrinter对象后,编译器会将this引用传递给当前的构造器:

ActionListener listener = new TimePrinter(this);

(b)局部内部类

在上面的代码中,TimePrinter类只在start方法中创建这个类型的对象时使用了一次。

当遇到类似情形时,可以在方法中定义局部类:

	public void start() {
		class TimePrinter implements ActionListener {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (beep) {
					Toolkit.getDefaultToolkit().beep();
				}
			}
		}
	}

(c)由外部方法访问final变量

局部类内部类不仅能访问包含它们的外部类,还可以访问局部变量。不过,那些局部变量必须被声明为final。

将TalkingClock构造器中的参数interval和beep移到start方法中。

public void start(int interval, final boolean beep) {
		class TimePrinter implements ActionListener {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (beep) {
					Toolkit.getDefaultToolkit().beep();
				}
			}
		}
		ActionListener listener = new TimePrinter();
		Timer t = new Timer(interval, listener);
		t.start();
	}

需要注意的是:TalkingClock类不再需要存储实例变量beep了,它只是引用start方法中的beep参数变量。

程序if(beep)... 在start方法内部,为什么不能访问beep变量的值呢?

内部的控制流程如下:

(1)调用start方法。

(2)调用内部的TimePrinter构造器,以初始化对象变量listener。

(3)将listener引用传递给Timer构造器,定时器打开,start方法结束。

此时start方法的beep参数变量不复存在。

(4)然后,actionPerformed方法执行if(beep)....。

为了能够让actionPerformed方法能够正常工作,TimePrinter类在beep域释放之前将beep域用start方法的局部变量进行了备份。

编译器为局部内部类构造了名为TalkingClock$TimePrinter。

	class TalkingClock$1TimePrinter{
		TalkingClock$1TimePrinter(TalkingClock, boolean);

		public void actionPerformed(java.awt.event.ActionEvent);

		final boolean val$beep;
		final TalkingClock this$0;

	}

注意构造器的boolean参数和val$beep实例变量。当创建一个对象时,beep就会被传递给构造器,并存储在val$beep域中。

编译器必须检测对局部变量的访问,为每一个变量建立相应的数据,并将局部变量拷贝到构造器中。