第5章 Java内部的秘密——内部类
◎ 本章教学微视频:6个 25分钟
学习指引
在Java中,允许在一个类的内部定义类,这样的类称作内部类,内部类所在的类称作外部类。根据内部类的位置、修饰符和定义的方式可以将内部类分为不同类型。本章介绍Java的内部类,主要内容包括创建内部类、链接到外部类、成员内部类、匿名内部类、局部内部类和静态内部类等。
重点导读
- 掌握创建内部类的方法。
- 掌握链接到外部类的方法。
- 掌握成员内部类的使用方法。
- 掌握匿名内部类的使用方法。
- 掌握局部内部类的使用方法。
- 掌握静态内部类的使用方法。
5.1 创建内部类
内部类就是在一个类的内部再定义一个类。内部类可以是静态的,也可以用public、default、protected和private修饰,而外部类只能使用public和default修饰。
内部类是一个编译时的概念,一旦编译成功,就会和相应的外部类成为完全不同的两个类。对于一个名为OuterTest的外部类和其内部定义的名为Inner的内部类,编译完成后出现OuterTest.class和OuterTest$Inner.class两个类。所以内部类的成员变量、方法名可以和外部类的相同。
下面举例说明如何创建和实例化内部类。
【例5-1】(实例文件:ch05\Chap5.1.txt)创建内部类应用实例。
public class OuterC { public void showOuterC() { System.out.println("这是外部类"); } public class InnerC { public void showInnerC() { System.out.println("这是内部类"); } } }
在本例中,OuterC是一个外部类,在该类中定义了一个内部类InnerC和一个showOuterC()方法,其中,InnerC类有一个showInnerC()方法。
5.2 链接到外部类
如果想通过外部类访问内部类,则需要通过外部类对象创建内部类对象。创建内部类对象的具体语法格式如下:
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
下面通过一个例子来具体说明。
【例5-2】(实例文件:ch05\Chap5.2.txt)实例化内部类(每个类均为单独的文件)。
程序运行结果如图5-1所示。
图5-1 实例化内部类运行结果
本例通过OuterC.InnerC ic = new OuterC().new InnerC();来创建内部类对象ic,并调用内部类的showInnerC()方法将内容显示在控制台上。
5.3 成员内部类
在一个类中除了可以定义成员变量、成员方法,还可以定义类,这样的类就被称作成员内部类,成员内部类是最普通的内部类。在成员内部类中可以访问外部类的所有成员。成员内部类的使用方法如下:
- 成员内部类定义在外部类的内部,相当于外部类的一个成员变量,成员内部类可以使用任意访问控制符,如public、protected、private等。
- 成员内部类的方法可以直接访问外部类的所有数据,包括私有的数据。
- 定义了成员内部类后,必须使用外部类对象来创建内部类对象。即:
内部类 对象名=外部类对象.new内部类();
- 成员内部类class文件格式为“外部类名$内部类名.class”。
另外,还有一些规定如下:
- 外部类不能直接使用内部类的成员和方法。可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法。
- 如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法。如果内部类要访问外部类的成员变量,可以使用this关键字(内部类默认可以引用外部类对象,引用时在对象名前面加上“外部类名.this”)。
- 外部类的外部要声明完整,如外部类名.内部类名,外部类内部则不需要。
【例5-3】(实例文件:ch05\Chap5.3.txt)成员内部类实例。
程序运行结果如图5-2所示。
图5-2 成员内部类运行结果
本例中,外部类定义了变量b=5,内部类也定义了变量b=3,因此内部类如果要访问外部类的变量b,必须通过Outer.this.b才能访问。
另外,成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。因此,通过Outer o = new Outer();先创建了外部类对象,然后通过Inner i = o.new Inner();创建了内部类对象。
5.4 匿名内部类
匿名内部类就是没有名字的内部类,多用于关注实现而不关注实现类的名称。匿名内部类一般是编写代码时用得最多的内部类。在Swing编程中,经常使用这种方式来绑定事件,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。
【例5-4】(实例文件:ch05\Chap5.4.txt)匿名内部类实例。
button2.addActionListener( new ActionListener(){ public void actionPerformed(ActionEvent e) { System.out.println("单击了button2"); } });
本例中,为一个按钮对象button2添加了一个事件监听器,addActionListener()方法的参数是一个匿名内部类:
new ActionListener(){ public void actionPerformed(ActionEvent e) { System.out.println("单击了button2"); } }
匿名内部类是唯一的没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动命名为Outter$1.class。一般来说,匿名内部类用于继承其他类或用于实现接口,并不需要增加额外的方法,因为它只是对继承方法的实现或重写。
5.5 局部内部类
局部内部类是定义在一个方法或者一个作用域中的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。注意,局部内部类就像是方法中的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
【例5-5】(实例文件:ch05\Chap5.5.txt)局部内部类举例:定义在方法内。
【例5-6】(实例文件:ch05\Chap5.6.txt)局部内部类举例:定义在作用域内。
5.6 静态内部类
静态内部类也是定义在另一个类中的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这一点和类的静态成员属性有点类似,并且它不能使用外部类的非静态成员变量或者方法,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非静态成员就会产生矛盾,因为外部类的非静态成员必须依附于具体的对象。
- 静态内部类是static修饰的内部类,这种内部类的特点如下:
- 静态内部类不能直接访问外部类的非静态成员,但可以通过“new外部类().成员”的方式访问。
- 如果外部类的静态成员与内部类的成员名称相同,可通“类名.静态名”访问外部类的静态成员;否则可通过成员名直接调用外部类的静态成员。
- 创建静态内部类中的对象时,不需要外部类的对象,可以直接创建,格式为
内部类名 对象名=new内部类名();
- 在其他类中创建内部类的对象时,不需要用外部类对象创建,格式为
外部类名.内部类名 对象名=new外部类名.内部类名();
【例5-7】(实例文件:ch05\Chap5.7.txt)静态内部类举例。
程序运行结果如图5-3所示。
图5-3 静态内部类运行结果
本例中,内部类访问外部类的静态成员变量b可以通过Outer.b实现,内部类访问外部类的非静态成员变量a必须通过new Outer().a来实现。在Java中需要使用内部类的原因主要有以下4点:
- 每个内部类都能独立地继承一个接口的实现,所以无论外部类是否已经继承了某个实现,对于内部类都没有影响。内部类使得多继承的解决方案变得更加完善。
- 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
- 方便编写事件驱动程序。
- 方便编写线程代码。
5.7 就业面试解析与技巧
5.7.1 面试解析与技巧(一)
面试官:创建了匿名类,为什么编译时提示匿名类不存在呢?
应聘者:创建匿名类时,首先要创建匿名类的接口,否则编译时提示匿名类不存在。
5.7.2 面试解析与技巧(二)
面试官:成员内部类和静态内部类有什么区别?
应聘者:成员内部类的创建必须依赖于外部类,静态内部类的创建不依赖于外部类。成员内部类可以访问外部类的所有成员,静态内部类只可以访问外部类的静态成员变量和静态的方法。