Java基础之内部类
内部类的简介
内部类是定义在另一个类中的类。
内部类的使用场景
内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
内部类可以对同一个包中的其他类隐藏起来。
当想要定义一个回调函数且不想大量编写代码是时,使用匿名内部类比较便捷
下面我们看一个简单程序
package com.jay.innerClass;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
/**
* Created by xiang.wei on 2018/1/26
* 描述:构造一个语音时钟
* @author xiang.wei
*/
public class InnerClassTest {
public static void main(String[] args) {
TalkingClock talkingClock = new TalkingClock(1000, true);
talkingClock.start();
JOptionPane.showMessageDialog(null,"Quit program?");
}
}
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 timer = new Timer(interval, listener);
timer.start();
}
public class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone,the time is" + new Date());
//说明2
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
}
在说明2中,我们看到了内部类直接引用了外部类的been变量。这里他是如何能引用的呢?
我们将外围类对象的引用称为outer。(outer不是Java的关键字)
外围类的引用在构造器中设置。编译器修改了所有内部类的构造器。添加了一个外部类引用的参数。
如上例中,编译器为这个类生成了一个默认的构造器。其代码如下:
public TimerPrint(TalkingClock clock){
outer=clock
}
当在start 方法中创建了TimerPrinter对象后,编译器就会将this引用传递给当前的语音时钟的构造器
ActionListener listener = new TimerPrinter(this)
内部类的特殊语法规则
内部类中声明的所有静态域都必须是final,原因很简单。我们希望一个静态域只有一个实例,不过对于每个外部对象,
会分别有一个单独的内部类实例。如果这个域不是final,它可能就不是唯一的。
内部类中不能有static方法。Java语言规范对这个限制没有做任何解释。也可以允许有静态方法,但只能访问外部类的静态域
和方法。
局部内部类
局部内部类就是在方法内部定义的一个内部类。对外部世界是完全隐藏起来的。即使是外部类类本身的其他的方法也不能访问
如下例所示:
public void start() {
class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone,the time is" + new Date());
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
ActionListener listener = new TimePrinter();
javax.swing.Timer timer = new javax.swing.Timer(interval, listener);
timer.start();
}
该方法的控制流程是
1. 调用start方法
2. 调用内部类TimePrinter的构造器,以便初始化对象listener
3. 将listener引用传递给Timer构造器,定时器开始计时,start方法结束,此时start
方法的beep参数变量不复存在。
4. 然后,actionPerformed 方法执行if(beep)…
假设想更新在一个封闭作用域内的计数器。这里想要统计一下在排序过程中调用
compareTo 方法的次数
public void start2() {
int counter = 0;
Date[] dates = new Date[100];
for (int i = 0; i < dates.length; i++) {
dates[i] = new Date(){
public int compareTo(Date other) {
counter++; //ERROR
return super.compareTo(other);
}
};
Arrays.sort(dates);
System.out.println(counter+"comparisons");
}
}
可以替代的方案是:
public void start2() {
int[] counter = new int[1];
Date[] dates = new Date[100];
for (int i = 0; i < dates.length; i++) {
dates[i] = new Date(){
@Override
public int compareTo(Date other) {
counter[0]++; //ERROR
return super.compareTo(other);
}
};
Arrays.sort(dates);
System.out.println(counter+"comparisons");
}
}
匿名内部类
只创建了一个类的一个对象。
由于构造器的名字必须与类名相同,而匿名内部类没有类名。所以,匿名类不能有构造器。取而代之的是,将构造器参数
传递给超类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。
作者:码农飞哥
微信公众号:码农飞哥