技术背景
- 与C++不一样,Java从一开始就设计为一个“纯”面向对象语言,“一切皆为对象”
- 基于这个理念,Java使用对象“封装一切”
- 想象一个场景:一个类中包容许多方法和字段,有些方法和字段从逻辑上看具有比较紧密的联系,可以把它们“放在一块”,视为一个整体“。
- 针对以上场景,Java设计了“内部类”这个机制
什么叫内部类(inner class)?
- 其定义直接包容于另一个类(或方法)内部的类
- 内部类可以看成是外部类的成员,其地位等同于类中的其他成员
- 内部类编译后,每个内部类都会产生一个.class 文件,其文件名通常有以下格式:
外部类名$内部类名.class
静态内部类
- 一个声明为static的内部类
class TopClass{
static class NestedClass{...}
}
- 静态内部类不能访问其外部类的对象的实例数据,但可以访问其静态成员(包容字段和方法)
- 静态内部类的使用
- 静态内部类,由于是“静态”的,所以,直接使用其类名就能直接调用其方法,存取其属性
- 静态内部类的使用场景
- 当需要编写许多静态方法及静态字段时,将逻辑上密切相光的代码组织成静态内部类,是一种合理的代码管理方式
- 编写静态代码时,如果这些代码有可能在多线程环境下使用,则一定要注意线程安全问题,必要时需要加锁,但要注意这可能带来性能损失
成员内部类
成员内部类,声明为类成员的非静态类
class OuterClass{
class InnerClass{...}
}
- 内部类声明无static。成员内部类和外部类中的任何成员都可以相互访问
- 在能够调用内部类的method和为其数据字段赋值之前,父类必须首先创建内部类的实例
public class MemberInnerClassTest {
public static void main(String[] args) {
OuterClass outer=new OuterClass();
outer.testInnerClass();
}
}
class OuterClass{
private int outerValue=100;
private void printValue(){
System.out.println(outerValue);
}
private static void outerStaticMethod(String info){
System.out.println(info);
}
public void testInnerClass(){
innerObject.innerClassField++;
innerObject.innerClassMethod();
innerObject.visit();
}
private InnerClass innerObject=new InnerClass();
private class InnerClass{
private int innerClassField=200;
public void visit(){
outerValue=200;
printValue();
outerStaticMethod("From InnerClass Object");
}
private void innerClassMethod(){
System.out.println("InnerClass.innerClassMethod");
}
}
}
成员内部类的使用场景
- 当一个类要完成的功能比较多,并且这些功能从逻辑上看很明显的可以划分为几大类,则将这些代码分别设计几个内部成员类是个不错的代码管理方式
- 通常情况下,成员内部类应该设置为private的,不允许外界直接访问它们,这是面向对象“封装”这一特性的具体应用实例
/**
@version 1.10 2004-02-27
@author Cay Horstmann
*/
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class TalkingClockTest
{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
// keep program running until user selects "Ok"
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
/**
A clock that prints the time in regular intervals.
*/
class TalkingClock
{
private int interval;
private boolean beep;
/**
Constructs a talking clock
@param interval the interval between messages (in milliseconds)
@param beep true if the clock should beep
*/
public TalkingClock(int interval, boolean beep)
{
this.interval = interval;
this.beep = beep;
}
/**
Starts the clock.
*/
public void start()
{
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
private class TimePrinter 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();
}
}
}
本地内部类
- 在一个方法体中定义的类
void foo(){
class Mylocal{...};
}
在方法体代码中,可以访问内部类的任何成员
匿名内部类
- 可以在方法调用语句中直接声明一个实现指定接口的匿名内部类并创建一个对象:
obj.ShowAnonymousInnerClassUsage(new MyInterface(){
public void func(){...}
};
分析:上述代码实际上省去了定义单独的实现MyInterface类的步骤;匿名内部类对象可以访问外部类的成员
- 拥有基类的匿名内部类
不使用接口,而是使用Java最顶层的基类Object,我们可以随意定义一个匿名内部类并立即调用之
分析:上述代码实际上是创建了基类是Object的一个匿名内部类对象并立即调用之;这种方法实际在开发中用的不多
匿名内部类的使用场景
- 匿名内部类多用于创建一个实现了某接口的对象或派生自某抽象基类的对象,其特点是:这个对象仅在这个地方使用
- 在拥有可视化界面的GUI应用中,大量使用匿名内部类。比如为按钮的点击编写事件响应代码
- 特别的,Java8中引入了Lambda表达式特性,可看成是“针对接口实现的匿名内部类”的进一步简化
- Java匿名内部类的语法很常见也很重要