1.内部类介绍及使用原因
内部类是定义在另一个类中的类。使用内部类的主要原因如下(若A为B的内部类):
- A可以访问B中的数据,包括私有数据。
- 内部类可以对一个包中的其他类隐藏。
- 当想要定义一个回调函数且不想编写大量代码时,可以使用匿名内部类。
需要注意的是:虽然A为B的内部类,但是并不意味着每个B类对象都有同一个A的实例域。
内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。这是由于内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。
内部类内使用this参数,指的是内部类自己,而要使用外部类的this参数,需要使用OuterClass.this。
内部类可以访问外围类的所有域,那它是如何管理这些额外的访问特权呢?
class TalkingClock{
private int interval;
private boolean beep;
public TalkingClock();
这个方法是编译器在外围类中添加的,
由于内部类会访问beep变量,所以添加了这个方法。
内部类调用这个方法,将会得到返回的beep变量的值。
static boolean access$00(TalkingClock);
public void start();
}
内部类中声明的所有静态域都必须是final。因为,一个静态域只有一个实例。不过对于每个外部类对象,会分别由一个单独的内部类实例。如果这个内部类的静态域不是final,就有可能不是唯一。
2.局部内部类
定义在方法中的内部类。
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();
Time t = new Timer(interval, listener);
t.start();
}
局部类不能用public或private进行修饰,局部类对外部完全隐藏,除了start()方法之外,没人知道TimePrinter类的存在。
局部类不仅可以访问包含它们的外部类,还可以访问局部变量。但局部变量必须为final。
public void start(int interval, final boolean beep) {
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();
Time t = new Timer(interval, listener);
t.start();
}
整体流程:
1.调用start方法
2.调用内部类的TimePrinter构造器。初始化listener对象。
3.将listener引用传递给Timer构造器,定时器开始计时,start方法结束。
此时,start方法的变量beep也不存在了。
4.actionPerformed方法执行if(beep)
5.为了能让actionPerformed方法工作,TimePrinter类在beep域释放之前(方法刚开始时)
将beep域用start()方法的局部变量进行备份。
将beep参数声明为final,对它进行初始化后不能再修改。
因此就使得局部变量与在局部类内建立的拷贝保持一致。
有时,final限制显得很不方便。可以使用长度为1的数组来代替局部变量。
final int[] counter = new int[1];
Date [] dates = new Date[100];
for (int i = 0; i < dates.length; i++)
dates[i] = new Date() {
public int compareTo(Date other) {
counter[0]++;
return super.compareTo(other);
}
};
Arrays.sort(dates);
System.out.println(counter[0] + "comparisons.");
3.匿名内部类
匿名内部类一般用在创建实现了某个接口(或扩展某个类)的对象上,直接使用关键字new这个接口(或这个父类)。
匿名内部类没有名字,所以也没有构造器。然而,当匿名内部类是扩展某个父类的时候,可以将构造器参数传递给父类。
语法格式:
new SuperType(constructor parameters){
inner class methods and data
}
new InterfaceType() {
methods and data
}
创建一个person对象
Person queen = new Person("Marry");
创建一个扩展了person类的匿名内部类对象
Person count = new Person("Dracula") {....};
创建一个实现ActionListener接口的类的新对象,需要实现的方法actionPerformed定义在{ }内
public void start(int interval, final boolean beep) {
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone,the time is " + new Date());
if(beep) Toolkit.getDefaultToolkit().beep();
}
};
Time t = new Timer(interval, listener);
t.start();
}
通常用匿名内部类实现事件监听器和其他回调,如今大多数使用lambda表达式。
4.静态内部类
有时候,使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外围对象。为此,可以将内部类声明为static,取消产生的引用。
因为静态内部类是外部类的类相关的,而不是外部类的对象相关的。也就是说,静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的类本身中。
静态内部类只能访问外部类的静态成员。生成(new)一个静态内部类对象不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:Outer.Inner in=new Outer.Inner();而不需要通过生成外部类对象来生成(类似于静态成员也不需要new对象就可直接使用)。
成员内部类的对象生成:
Outer out = new Outer();
Outer.Inner in = outer.new Inner();
网友评论