Java中的静态内部类

文章作者:Tyan
博客:noahsnail.com

1. 什么是静态内部类

在Java中有静态代码块、静态变量、静态方法,当然也有静态类,但Java中的静态类只能是Java的内部类,也称为静态嵌套类。静态内部类的定义如下:

1
2
3
4
5
6
public class OuterClass {

static class StaticInnerClass {
...
}
}

在介绍静态内部类之前,首先要弄清楚静态内部类与Java其它内部类的区别。

2. 内部类

什么是内部类?将一个类的定义放在另一个类的内部,就是内部类。Java的内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。

2.1 成员内部类

成员内部类是最普通的内部类,就是一个普通的类定义在另一个类的内部,形式如下:

1
2
3
4
5
6
public class OuterClass {

class InnerClass {
...
}
}

注:

  • 在成员内部类中,可以直接访问外部类的属性、方法,即使是private类型也可以访问,这是因为内部类持有一个外部类的引用,可以自由访问。

  • 成员内部类中不能存在任何静态变量和静态方法。

  • 成员内部类是依附于外部类的,只有先创建了外部类才能够创建内部类。

成员内部类的创建形式如下:

1
2
3
4
5
6
//定义成员内部类的方式一
OuterClass test = new OuterClass();
InnerClass innerA = test.new InnerClass();

//定义成员内部类的方式二
OuterClass.InnerClass innerB = new OuterClass().new InnerClass();

2.2 局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,形式如下:

1
2
3
4
5
6
public void methodInnerClass() {
class InnerClass {
...
}
InnerClass A = new InnerClass();
}

注:

  • 局部内部类就像是方法里面的一个局部变量一样,不能有public、protected、private以及static修饰符。

  • 可以直接访问外部类的属性、方法,即使是private类型也可以访问。

2.3 匿名内部类

用过Swing的人应该对匿名内部类非常熟悉,Swing中使用了大量的匿名内部类。匿名内部类的形式如下:

1
2
3
4
5
6
7
8
9
Button button = new Button();
button.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
...
}

});

注:

  • 匿名内部类没有名字,没有构造方法。

  • 匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

2.4 静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。其定义形式如下:

1
2
3
4
5
6
public class OuterClass {

static class StaticInnerClass {
...
}
}

注:

  • 静态内部类的创建不依赖外部类。

  • 静态内部类不能访问外部类的非静态成员和非静态方法。

2.5 静态内部类与其它内部类的区别

静态内部类与其它内部类最大的区别在于非静态内部类在编译完成之后会隐含地保存一个引用,该引用是指向创建它的外部类,但是静态内部类却没有。静态内部类只是嵌套在外部类中,因此也被称为嵌套内部类。

2.6 为什么要使用内部类

  • 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

  • 典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建外部类的对象。典型的情况是Effective Java 2.0中Item 2讲述的构建器模式。

  • 使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。

3. 为什么要使用静态内部类

使用静态内部类主要是因为静态内部类的两个优点:

  • 增强了类的封装性

  • 提高了代码的可读性

以下面的例子为例:将Builder放在NutritionFacts的内部,说明二者之间有一定的关系,比起将两个类分开要好很多,因此增强了类的封装性。其次,二者放在一起,能很明显的看出Builder类是用来创建NutritionFacts类的,提高了代码的可读性。

4. 静态内部类的例子

静态内部类的创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//Builder Pattern
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;

public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;

public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}

public Builder calories(int val) {
calories = val;
return this;
}

public Builder fat(int val) {
fat = val;
return this;
}

public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}

public Builder sodium(int val) {
sodium = val;
return this;
}

public NutritionFacts build() {
return new NutritionFacts(this);
}
}

private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}

静态内部类的使用:

1
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

参考资料

1、Effective Java 2.0

2、http://www.cnblogs.com/chenssy/p/3388487.html

3、http://www.cnblogs.com/dolphin0520/p/3811445.html

4、http://book.51cto.com/art/201202/317517.htm

如果有收获,可以请我喝杯咖啡!