部分内容参考Core Java
为什么要使用内部类:
1、内部类方法可以访问外围类作用域中的数据,包括private类型数据。
2、内部类可以对同一个包中的其他类隐藏起来。
3、当想要定义一一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷(Java8以后可以使用lamba表达式)
一、成员内部类
//OuterClass
public class TalkingClock {
private int interval
private boolean beep;
public TalkingClock(int interval, boolean beep) { //code... }
public void start() {
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
//InnerClass
public class TimePrinter implements ActionListener {
System.out.println("haha");
if(beep)
System.out.println("beep");
}
}
当一个类A定义在一个类B里面,则A称为B的成员内部类。类A隐式持有一个指向类B的this指针,用来访问外围类的成员变量。
在创建TimerPrinter对象时编译器会将this隐式引用传递给其构造器:
ActionListener listener = new TimePrinter(this);
如果对TimePrinter进行反射,会得到下列代码:
class TalkingClock$TimePrinter {
public TalkngClock$TimePrinter(TalkingClcok);
public void actionPerformed(java.awt.event.ActionEvent);
final TalkingClock this$0;
}
注意构造函数列表和this$0。这将使内部类持有外部类引用。
但是beep是私有的,这说明内部类有一种特有的方式使得其可以访问外部类的私有成员变量。那么是如何访问的呢?再对外部类进行反射,可得到下列代码:
class TalkingClock {
private int interval;
private boolean beep;
public TalkingClock(int, boolean);
static boolean access$0(talkingClcok);
private void start();
}
可以看到,反射后的外围类多了一个access$0方法。也就是说,在访问私有变量beep时,也就是调用这个方法来返回beep的值:
if(TalkingClock.access$0(outer))
语法规则:
1、OuterClass.this 比如上述代码中的if(beep)还可以i这样写:
if(TalkingClock.this.beep)
System.out.println("beep");
2、OuterObject.new InnerClass(construction paraments)
ActionListener listener = this.new TimePrinter();
//若TimePrinter为一个公有内部类
TalkingClock c1 = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = c1.new TimePrinter();
3、OuterClass.InnerClass
为什么成员内部类中声明的所有静态域都必须是final?
1、没个外部对象都有自己单独的内部类对象。
2、这些对象无法统一,但是static要求唯一 。因此加入final防止出现不唯一的问题。
此外,内部类的静态方法只能访问外围类的静态域和静态方法。
二、局部内部类
如果某个类的对象只在某个方法中使用了一次,那么可以把这个类直接定义在该方法中。定义在方法体中的类称之为局部内部类。
public void Outermethod(){
class void InnerClass{ //局部内部类
public void innerMethod(){
// TODO
}
}
//...
}
若局部内部类使用了方法中的局部变量,则在其方法中会维护一份方法中局部变量的拷贝。应当使用final修饰局部变量,使得内外的值一致:
public class Test {
public void outmethod() {
int num = 10;
class Inner {
public void show() {
//在Java8中如果内部类不修改内部类外变量可以不加final,如果修改则报错。
//num = num+1; 报错
System.out.println(num);
}
}
new Inner().show();
}
public static void main(String[] args) {
new CopyTest().outmethod();
}
}
三、静态内部类
如果有时只是想把一个类隐藏在另一个类的内部,则内部类不需要引用只想外部类。这是后就可以使用静态内部类。静态内部类除了没有对外部类的引用特权以外,其他与成员内部类一致。注意当外部类加载时静态内部类并不会立即被加载,只有当使用静态内部类时才会加载,且静态内部类的加载是线程安全的。