嵌套类是指被定义在另一个类内部的类。

嵌套类存在的目的应该只是为它的外围类服务。如果嵌套类将来可能会用于其他的某个环境中,他就应该是顶层类。

嵌套类有4种:

  • 静态成员类
  • 非静态成员类
  • 匿名类
  • 局部类

除了第一种外,其他三种都是内部类。

静态成员类 与 非静态成员类

静态成员类是最简单的一种嵌套类。最好把他看作是普通的类,只是碰巧被声明在另一个类的内部而已。他可以访问外围类的所有成员,包括哪些声明为私有的成员。静态成员类是外围类的一个静态成员,与其他的静态成员一样,也遵守同样的可访问性规则。如果它被声明为是私有的,他就只能在外围类的内部才可以被访问。

静态成员类的一种常见用法是作为共有的辅助类,仅当与他的外部类一起使用时才有意义。

  1. //内部类实现懒汉模式
  2. public class Singleton {
  3. public static class SingletonHolder{
  4. private static Singleton singleton = new Singleton();
  5. }
  6. //私有的构造函数,使外部类无法通过 构造器构造实例
  7. private Singleton() {}
  8. //获取单例对象
  9. public static Singleton getInstance() {
  10. return SingletonHolder.singleton;
  11. }
  12. }

从语法上讲,静态成员类和非静态成员类之间的唯一的区别是,静态成员类的说明中包含了修饰符static。尽管他们的语法非常相似,但是这两种嵌套类有很大的不同。非静态成员类的每一个实例都隐含着与外围类的一个外围实例相关联。在非静态成员类的实例方法内部,可以调用实例上的方法,或者利用修饰过的this构造(外围类.this)获得外围实例的引用。如果嵌套类的实例可以在它外围类的实例之外独立存在,这个嵌套类就必须是静态成员类:在没有外围实例的情况下,想要创建非静态成员类的实例是不可能的。

非静态成员类的一种常见用法是定义一个Adapter,他允许外部类的实例被看作是另外一个不相关的类的实例。
例如,Map接口的实现往往使用了非静态成员类来实现他们的集合视图(不存储数据,但是需要依赖外部类的数据)**,这些集合视图是由Map的keySet、entrySet、Values方法返回的。

  1. final class KeySet extends AbstractSet<K> {
  2. public final int size() { return HashMap.this.size; }
  3. public final void clear() { HashMap.this.clear(); }
  4. public final Iterator<K> iterator() { return new KeyIterator(); }
  5. public final boolean contains(Object o) { return containsKey(o); }
  6. public final boolean remove(Object key) {...}
  7. public final Spliterator<K> spliterator() {...}
  8. public final void forEach(Consumer<? super K> action) {...}
  9. }


如果声明成员类不要求访问外围实例,就要始终把static修饰符放在他的声明中,使他成为静态成员类,而不是非静态成员类。
如果省略了static修饰符,则每个实例都将包括一个额外的指向外围对象的引用。保存这份引用要消耗时间和空间,并且会导致外围实例在符合垃圾回收是却依然得以保留。**

私有静态成员类的一种常见用法是用来代表外围类所代表的对象的组件。

  1. static class Node<K,V> implements Map.Entry<K,V> {
  2. final int hash;
  3. final K key;
  4. V value;
  5. Node<K,V> next;
  6. Node(int hash, K key, V value, Node<K,V> next) {
  7. this.hash = hash;
  8. this.key = key;
  9. this.value = value;
  10. this.next = next;
  11. }
  12. public final K getKey() { return key; }
  13. public final V getValue() { return value; }
  14. public final String toString() { return key + "=" + value; }
  15. public final int hashCode() {...}
  16. public final V setValue(V newValue) {...}
  17. public final boolean equals(Object o) {...}
  18. }

匿名类

匿名类没有名字。他不是外围类的一个成员。它并不是与其他的成员一起被声明,而是在使用的同时被声明和实例化。当且仅当匿名类出现在非静态的环境中时,它才有外围实例。但是即使他们出现在静态的环境中,也不可能拥有任何静态成员。
**

局部类

在任何“可以声明局部变量”的地方,都可以声明局部类。有名字,可以被重复利用。

选择哪一种嵌套类

如果一个嵌套类需要在单个方法之外仍然是可见的,或者他太长了,不适合于放在方法内部,就该使用成员类。如果成员类的每一个实例都需要一个指向外围实例的引用,就要把成员类做成非静态的;否则就做成静态的。

假设这个嵌套类属于一个方法的内部,如果你只需要在一个地方创建实例,并且已经有了一个预置的类型可以说明这个类的特征(通常是一个接口),就要把他做成匿名类;否则就做成局部类。