嵌套类
嵌套类是在另一个类中定义的类。嵌套类应该只为外部类服务。
如果嵌套类在其他环境中有用,那么它应该是顶级类。有四种嵌套类:静态成员类、非静态成员类、匿名类和局部类。除了第一种,所有的类都被称为内部类。
静态成员类
静态成员类是最简单的嵌套类。最好把它看做是一个普通的类,只是碰巧在另一个类中声明而已,并且可以访问外部类的所有成员,甚至那些声明为 private 的成员。
静态成员类是其外部类的静态成员,并且遵守与其他静态成员相同的可访问性规则。如果声明为私有,则只能在外部类中访问,等等。
静态成员类的一个常见用法是作为公有的辅助类,只有与它的外部类一起使用时才有意义。
比如需要返回一个比较复杂的嵌套类给前端,用静态成员类能更好组织返回结构。
非静态成员类
从语法上讲,静态成员类和非静态成员类之间的唯一区别是静态成员类在其声明中具有修饰符 static。
尽管语法相似,但这两种嵌套类有很大不同。非静态成员类的每个实例都隐式地与外部类的外部实例相关联。在非静态成员类的实例方法中,你可以调用外部实例上的方法,或者使用受限制的 this 构造获得对外部实例的引用 [JLS, 15.8.4]。
如果嵌套类的实例可以独立于外部类的实例存在,那么嵌套类必须是静态成员类:如果没有外部实例,就不可能创建非静态成员类的实例。
非静态成员类的一个常见用法是定义一个 Adapter ,它允许外部类的实例被视为某个不相关类的实例。
例如,Map 接口的实现通常使用非静态成员类来实现它们的集合视图,这些视图由 Map 的 keySet、entrySet 和 values 方法返回。类似地,集合接口的实现,例如 Set 和 List,通常使用非静态成员类来实现它们的迭代器:
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
//...
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
}
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
如果声明的成员类不需要访问外部的实例,那么应始终在声明中添加 static 修饰符,使其成为静态的而不是非静态的成员类。
如果省略这个修饰符,每个实例都有一个隐藏的对其外部实例的额外引用。如前所述,存储此引用需要时间和空间。更严重的是,它可能会在满足进行垃圾收集条件时仍保留外部类的实例。
匿名类
匿名类没有名称。它不是外部类的成员。它不是与其他成员一起声明的,而是在使用时同时声明和实例化。匿名类在代码中任何一个表达式合法的地方都是被允许的。当且仅当它们发生在非静态环境中时,匿名类才具有外部类实例。
但是,即使它们发生在静态环境中,它们也不能有除常量变量以外的任何静态成员,常量变量是最终的基本类型或初始化为常量表达式的字符串字段。
匿名类的适用性有很多限制。
你不能实例化它们,除非在声明它们的时候。你不能执行 instanceof 测试,也不能执行任何其他需要命名类的操作。你不能声明一个匿名类来实现多个接口或扩展一个类并同时实现一个接口。
匿名类的客户端除了从超类型继承的成员外,不能调用任何成员。因为匿名类发生在表达式的中间,所以它们必须保持简短——大约 10 行或几行,否则可读性会受到影响。
在 lambda 表达式被添加到 Java(Chapter 6)之前,匿名类是动态创建小型函数对象和进程对象的首选方法,但 lambda 表达式现在是首选方法。
CompletableFuture.runAsync(() -> System.out.println("匿名类"));
局部类
局部类是四种嵌套类中最不常用的。
局部类几乎可以在任何能够声明局部变量的地方使用,并且遵守相同的作用域规则。局部类具有与其他嵌套类相同的属性。与成员类一样,它们有名称,可以重复使用。
与匿名类一样,它们只有在非静态环境中定义的情况下才具有外部类实例,而且它们不能包含静态成员。和匿名类一样,它们应该保持简短,以免损害可读性。
public static void Test() {
int a =4;
//局部内部类
//inner 局部内部类 不能添加访问权限修饰符
class Inner {
private int age;
private String name;
public void eat()
{
System.out.print("吃");
}
}
//局部内部类只能在声明这个内部类的方法中创建对象
Inner inner = new Inner();
inner.eat();
System.out.println(inner.name);
}