嵌套类
- 静态嵌套类
- 非静态嵌套类(内部类【inner class】)
class OuterClass{
//...
class NestedClass{
//...
}
}
class OuterClass{
//...
static class NestedClass{}
class InnerClass{}
}
嵌套类是它所在封装类的一个成员。非静态嵌套类(内部类)可以访问封装类的其他成员,包括声明为private的成员。静态嵌套类不能访问该风状态的其他成员。作为外部类的一个成员,一个嵌套类可以被声明为private,public,protected或者package private。
为什么使用嵌套类?
使用嵌套类的主要原因:
- 它将仅在一个地方使用的逻辑类组合在一块。如果一个类仅在另外一个类中使用,那么,前者在逻辑上绑定到后者上使得两者在一块。像帮助类的嵌套使得他们的包更加合理化。
- 提高封装性。假如两个顶级类A和B,B需要访问A的private成员,通过将B嵌套在A中,A的成员可以被成名为private并且B可以访问它们。另外,B类也被隐藏起来了。
- 提高可读性和维护性。嵌套类使得它存在的地方靠近定义的地方。
静态嵌套类
和类方法和变量一样,一个静态嵌套类和它的外部类是相关联的。类似静态类方法,一个静态嵌套类不能直接引用它所在封装类实例的变量和方法。它只能通过一个对象引用来使用它们。
备注:一个静态嵌套类和它封装类的实例成员关系,就像任何其他顶级类一样。实际上,一个静态嵌套类行为和顶级类是一样的。
静态嵌套类使用封装类名字来访问:
OuterClass.StaticNestedClass
创建一个静态嵌套类对象,语法如下:
new OuterClass.StaticNestedClass();
内部类
和实例方法和实例变量一样,一个内部类和封装类的实例有关系,可以直接访问封装类实例的方法和属性。同样,因为一个内部类和封装类的实例相关,它不能定义任何静态成员。
内部类的实例存在于外部类的实例中,下面例子:
class OuterClass{
class InnerClass{}
}
一个内部类的实例仅能够存在于外部类的实例中,并且可以直接访问外部类的方法和属性。
为了实例化一个内部类,你必须要先实例化外部类,创建内部类的语法如下:
OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();
存在两种内部类:本地类(local classes)和匿名内部类(anonymous classes)
覆盖/隐藏(Shadowing)
如果在特拟定范围内(如内部类或者方法)的一个类型(如成员变量或者参数名称)定义和封装类的其他声明有相同的名字。然后这个声明将会覆盖掉封装类的声明。一个隐藏声明,你不能单独用它的声明来引用,如下ShadowTest所示:
public class ShadowTest{
public int x = 0;
class FirstLevel{
pulbic int x =1;
void methodInFirstLevel(int x){
System.out.println("x="+x);
System.out.println("this.x="+this.x);
System.out.println("ShadowTest.this.x="+ShadowTest.this.x);
}
}
public static void main(String... args){
ShadowTest st = new ShadowTest();
ShadowTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);
}
}
运行结果:
x=23
this.x=1
ShadowTest.this.x=0
这个例子中定义了三个名称为x的变量:类ShadowTest的成员变量,内部类FirstLevel的成员变量,和方法methodInFirstLevel的参数。方法methodInFirstLevel的参数x覆盖了内部类FirstLevel的成员变量,因此,当你在methodInFirstLevel中使用变量x,它引用的是方法参数。要用内部类FirstLevel成员变量,需要使用关键字this:
System.out.println("this.x="+this.x);
引用更大范围变量:
System.out.println("ShadowTest.this.x="+ShadowTest.this.x);
序列化
本地类和匿名类的序列化,强烈不推荐使用。当Java编译器编译指定构造器,如内部类,它创建复合构造;类、方法、属性和其他构造器,它们在源代码中没有相应的构造。复合构造让Java编译器可以实现新的Java语言特性而不用修改jvm。然而,复合构造非常不同于Java编译器实现,这意味着.class文件实现也非常不同。因此,在一个不同的jre实现中,如果你序列化一个内部类,可能会有容错问题。