1.内部类
顾名思义:内部类就是定义在另一个类中的类。
为什么需要使用内部类?
1)内部类里面的方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
2)内部类可以对同一个包中的其它类隐藏起来。
3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
2.使用内部类访问对象状态
1)先创建一个TalkingClock对象。
2)构造一个语音时钟需要提供两个参数:发布通告的间隔和开关铃声的标志。
class TalkingClock{
/**
* 实例域
*/
private int interval; // 发布通告的间隔
private boolean beep; // 开关铃声的标志
public TalkingClock(int interval, boolean beep) {
super();
this.interval = interval;
this.beep = beep;
}
}
下面这段代码中的TimePrinter
类位于TalkingClock
类的内部。并不意味着每个TalkingClock
都有一个TimePrinter
实例域。也就是上面这段代码说的TimePrinter
对象是由TalkingClock
类的方法构造。
public class TimePrinter implements ActionListener{
public void actionPerformed(ActionEvent e) {
}
}
下面这段代码中TimePrinter
类并没有实例域或者名为beep的变量,反而beep引用了创建TimePrinter
的TalkingClock
对象的域。
内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。
if (beep) Toolkit.getDefaultToolkit().beep(); // 检查beep的标志
内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dxM6Imzl-1579613264593)(C:\Users\sywangu\AppData\Roaming\Typora\typora-user-images\image-20200121152257771.png)]
1)上图中的这个引用在内部类的定义中是不可见的。(如下代码)
public void actionPerformed(ActionEvent e) {
System.out.println("当前时间是:" + new Date());
/**
* TimePrinter类没有实例域、也没有beep的变量,而是beep引用了创建TimePrinter的TalkingClock对象的域。
* 内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。
*/
if (beep) Toolkit.getDefaultToolkit().beep(); // 检查beep的标志
}
2)外围类的引用在构造器中设置。(如下代码)
public TalkingClock(int interval, boolean beep) {
super();
this.interval = interval;
this.beep = beep;
}
3)当在start方法中创建了TimePrinter
对象后,编译期就会将this引用传递给当前的语音时钟的构造器:
ActionListener listener = new TimePrinter();
4)以上代码完整程序
package corejava_06;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import javax.swing.Timer;
import javax.swing.JOptionPane;
/**
*
* @author sywangu
*
*/
public class InnerClassTest {
public static void main(String[] args) {
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
JOptionPane.showConfirmDialog(null, "退出?");
System.exit(0);
}
}
/**
* 构造一个语音时钟时需要提供两个参数:发布通告的间隔和开关铃声的标志。
* @author sywangu
*
*/
class TalkingClock{
/**
* 实例域
*/
private int interval; // 发布通告的间隔
private boolean beep; // 开关铃声的标志
public TalkingClock(int interval, boolean beep) {
super();
this.interval = interval;
this.beep = beep;
}
public void start() {
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval , listener);
t.start();
}
/**
* TimePrinter对象是由TalkingClock类的方法构造
* @author sywangu
*
*/
public class TimePrinter implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("当前时间是:" + new Date());
/**
* TimePrinter类没有实例域、也没有beep的变量,而是beep引用了创建TimePrinter的TalkingClock对象的域。
* 内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。
*/
if (beep) Toolkit.getDefaultToolkit().beep(); // 检查beep的标志
}
}
}
5)运行结果
2.内部类的特殊语法规则
3.内部类是否有用、必要和安全
编译器会把内部类翻译成用$分隔外部类名与内部类名的常规文件。
TalkingClock
类内部的TimePrinter
类转换成class文件是:TalkingClock$TimePrinter.class
编译器为了引用外围类,生成一个附加的实例域this$0
public class TalkingClock$TimePrinter
{
public TalkingClock$TimePrinter ( TalkingCtock ) ;
public void actionPerformed ( java.awt.event.ActionEvent ) ;
final TalkingClock this$0 ;
}
(1)将TimePrinter
定义成一个常规类,并把它置于TalkingClock类的外部。
(2)在构造TimePrinter
对象的时候,将创建该对象的this指针传递给它。
package corejava_06;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class TalkingClock {
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) {
super();
this.interval = interval;
this.beep = beep;
}
public void start() {
ActionListener listener = new TimePrinter(this);
Timer t = new Timer(interval, listener);
t.start();
}
}
/*
* 将TimePrinter定义成一个常规类,并把它置于TalkingClock类的外部
*/
class TimePrinter implements ActionListener {
private TalkingClock outer;
/*
* 在构造`TimePrinter`对象的时候,将创建该对象的this指针传递给它。
*/
@Override
public void actionPerformed(ActionEvent e) {
// (outer.beep) // error
// 内部类可以访问外部类的私有数据,但是这里面的TimePrinter类则不行
if (outer.beep) Toolkit.getDefaultToolkit().beep();
}
public TimePrinter(TalkingClock clock) {
outer = clock;
}
}