开始
谈到内部类这个词,我想很多人都是那种并不陌生但是又不熟悉的感觉。在我的记忆中当年Java的Swing模块中一些事件的监听大都是内部类实现的,还有就是Spring的JdbcTemplate中好像也有涉及,实在是记不太清了。因为大部分开发中,内部类用到的很少,所以这里只是总结性的研究。
内部类
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称之为“内部类”。广泛意义上的内部类一般来说包括这四种:普通内部类(成员内部类)、局部内部类、静态内部类和匿名内部类。
普通内部类(成员内部类)
普通内部类是最简单最普通的内部类,请看如下代码:
class Canvas {
class Draw {
}
}
这样看起来,类Draw像是类Canvas的一个成员,Canvas称为外部类,那Draw自然就是内部类。所以普通内部类就是所谓的成员内部类,成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员),请看如下代码:
class Canvas {
private int width = 100;
public static int counts = 1;
public void test() {//外部类的方法
System.out.println("Canvas - test()");
}
class Draw {//内部类
public void drawThing() {
System.out.println(width); //外部类的private成员
System.out.println(counts); //外部类的静态成员
test(); //外部类的方法
}
}
}
不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生外部类失效现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员就必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问,请看如下代码:
class Canvas {
private int width = 100;
public static int counts = 1;
public void test() {//外部类的方法
System.out.println("Canvas - test()");
}
public void test2() {//外部类的方法2
//这里是外部类的一个方法,它想调用内部的方法必须要得到内部类的实例
Draw draw = new Draw();
draw.drawThing();
}
class Draw {//内部类
public void drawThing() {
System.out.println(width);
System.out.println(counts);
test();
}
}
}
成员内部类是存在于外部类里的,如果要创建成员内部类的实例,需要一个外部类的实例。请看如下代码:
public class Test {
public static void main(String[] args) {
Canvas canvas = new Canvas(); //获取外部类Canvas实例
Canvas.Draw draw = canvas.new Draw(); //通过外部类Canvas实例创建内部类Draw实例
}
}
内部类是可以用private、public、protected和包访问修饰权限的。比如上面的例子,如果成员内部类Draw用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和默认访问两种权限修饰。
局部内部类(方法内部类)
从上面一行中,局部内部类也叫方法内部类,您可以粗浅的理解为内部类是定义在外部类方法内的。说白了,局部内部类就是定义在一个方法或者一个作用域里面的类,局部内部类的访问仅限于方法内或者该作用域内。请看如下代码:
class Canvas {
public void test() {//外部类的方法
class Draw {//定义在外部类方法中的内部类
}
}
}
注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private或者static修饰符的。
静态内部类
class Canvas {
static class Draw {
}
}
public class Test {
public static void main(String[] args) {
Canvas.Draw draw = new Canvas.Draw();
}
}
静态内部类也是定义在另一个类里面的类,只不过在内部类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法。这里面涉及到了static这个关键字,如果有兴趣可以移步Java中的static关键字
从技术上讲,静态内部类应该不属于内部类。因为内部类与外部类共享一种特殊关系,更确切地说是对实例的共享关系。而静态内部类则没有上述关系。它只是位置在另一个类的内部,因此也可称为“嵌套类”。
匿名内部类
匿名内部类其实是我们在写代码的时候经常用到的,看下面这段Swing中的代码:
button.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("你点击了按钮");
}
}
);
这段代码中的new ActionListener()就是匿名内部类,很明显直接new的,没有实例名。
接下来我们继续细分匿名内部类。
继承式的匿名内部类
public class Canvas {
public void doDraw() {
System.out.println("draw one line");
}
public static void main(String[] args) {
Canvas canvas = new Canvas(){
public void doDraw() {
System.out.println("draw many line");
}
};
canvas.doDraw(); //输出结果draw many line
}
}
接口式的匿名内部类
public interface Canvas {
public void doDraw();
}
public class Test {
public static void main(String[] args) {
Canvas canvas = new Canvas(){
public void doDraw() {
System.out.println("draw any");
}
};
canvas.doDraw(); //输出结果draw any
}
}
参数式的匿名内部类
public interface Draw {
public void doThing();
}
public class Canvas {
public void doDraw(Draw draw){
draw.doThing();
}
}
public class Test {
public static void main(String[] args) {
Canvas canvas = new Canvas();
canvas.doDraw(new Draw() {
@Override
public void doThing() {
System.out.println("draw a Circle");
}
});
}
}
为什么把这个参数式的匿名内部类放到最后,眼尖的小伙伴就发现和一开始给出的Swing的那段代码很相似。与其所前两个你还不知所措,最后一个案列你可能已经明白了,技术扎实的小伙伴肯定能想到Spring的JdbcTemplate中应该有类似的地方。匿名内部类就说到这吧,总结一下:
- 匿名内部类是没有访问修饰符的。
- 匿名内部类不能有构造方法。
- 匿名内部类不能定义任何静态成员、静态方法。
- 匿名内部类不能是public,protected,private,static。
- 只能创建匿名内部类的一个实例。
结语
讲了那么多的内部类,无非是让大家对内部类有所了解,出现什么不合理或者不对的地方,请多担待。如果你问我到底用不用内部类?那主动权只能交给开发者了。其实在大多数开发过程中,没有什么特别的要求,很难使用到内部类。不过我想说的,那个“参数式的匿名内部类”,我们开发中到是经常用到。