以前遇到内部类的时候总有一个误区,认为外围类包含内部类所以在构造外围类对象时内部类的数据域也包含在外围类对象中。可以新手大概都会犯一些类似这样的错误吧。对于内部类与外部类是一种类之间的关系,而不是对象之间的关系;因此外围类对象并不包含内部类的数据域。
下面是一些自己的学习总结。
Java中的内部类(innerclass)是定义在另一个类中的类。使用它的主要原因有:
(1)内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据;
(2)内部类可以对同一个包中的其他类隐藏起来;
(3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
1.使用内部类访问外围类对象数据域
内部类既可以访问自身的数据域也可以访问创建它的外围类对象的数据域。这是因为内部类的对象总是有一个隐式引用,它指向了创建它的外围类对象。编译器在编译时会修改所有内部类的构造器,在构造器中添加一个外围类引用的参数。但这个隐式引用在内部类中的定义是不可见的。
内部类隐式使用外围类引用时可以直接使用外围类数据。
内部类显式使用外围类引用的语法为:outerClass.this.data
对于数据域的访问控制,内部类有访问特权,它不需要外围类的访问器,可以直接访问外围类对象的公有数据和私有数据。
对于外围类如果在其作用域外引用内部类,可以使用以下方法:outerClass.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) {
TalkingClock clock = new TalkingClock(1000,true);
clock.start();
}
}
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 TimerPrinter();//在外围类方法中new一个内部类
//construct a timer that calls the listener
//once every 1 seconds
Timer t = new Timer(interval,listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
public class TimerPrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep(); //内部类直接访问外围类对象的数据域
}
}
}
2.局部内部类
在上面的代码中,TimePrinter这个内部类只在start方法中创建内部类对象时使用了一次,当遇到这种情况时可以在一个方法中定义局部内部类。
局部内部类不能用public等访问说明符声明,它的作用域限定在声明这个局部类的块中。局部内部类的优势在于对外部世界完全隐藏,即使TalkingClock类中的其他代码也不能访问它。但内部类依然可以访问外围类对象数据域。
public class TalkingClock2 {
private int interval;
private boolean beep;
public TalkingClock2(int interval, boolean beep) {
this.interval = interval;
this.beep = beep;
}
public void start() {
class TimerPrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimerPrinter();
//construct a timer that calls the listener
//once every 1 seconds
Timer t = new Timer(interval,listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
局部内部类还可以访问被声明为final的局部变量。这是因为非final的局部变量会随着方法结束而消失。
public void start(int interval, boolean beep) { //传进来的实参必须是final类型
class TimerPrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimerPrinter();
//construct a timer that calls the listener
//once every 1 seconds
Timer t = new Timer(interval,listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
3.匿名内部类
new SuperType(construction parameter) {
inner class methods and data
};
其中,SuperType可以使接口,也可以使一个待扩展的类。由于构造器的名字必须与类名相同,而匿名类没有类名,所以匿名类不能有构造器。取而代之的是将构造器参数传递给超类构造器。尤其是在内部类实现接口的时候不能有任何构造参数。如果构造类时,构造参数的圆括号后面跟一个花括号,正在定义的就是匿名内部类。
public class TalkingClock3 {
public void start(int interval, boolean beep) {
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
//construct a timer that calls the listener
//once every 1 seconds
Timer t = new Timer(interval,listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
4.静态内部类
public class StaticInnerClass {
public static void main(String[] args) {
double[] d = new double[20];
for (int i = 0; i< d.length; i++) {
d[i] = 100 * Math.random();
}
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min=" + p.getFirst());
System.out.println("max=" + p.getSecond());
}
}
class ArrayAlg {
/**
* a pair of floating-point numbers
*/
public static class Pair {
private double first;
private double second;
/**
* constructs a pair from two floating-point numbers
*/
public Pair(double f, double s) {
first = f;
second = s;
}
public double getFirst() {
return first;
}
public double getSecond() {
return second;
}
}
/**
* compute both the min and max of an array
*/
public static Pair minmax(double[] value) {
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v: value) {
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min,max);
}
}