这篇Java教程基于JDK1.8。教程中的示例和实践不会使用未来发行版中的优化建议。
嵌套类
Java编程语言允许你在其他类里面定义类。这样一个类我们叫做嵌套类:
class OuterClass{
···
class NestedClass{
···
}
}
术语:嵌套类一般被分为两类:静态和非静态。通过static声明的嵌套类叫做静态内部类,非静态嵌套类叫做内部类。
class OuterClass{
···
static class StaticNestedClass{
···
}
class InnerClass{
···
}
}
嵌套类是其封闭类的成员。非静态嵌套类(内部类)可以访问封闭类的成员,及时它们被声明为private。静态嵌套类无法访问封闭类中的其他成员。作为OuterClass类的成员,嵌套类可以声明为private,protected,package-private,public。
为什么使用嵌套类?
使用嵌套类的原因主要有以下几点:
- 它是一种逻辑上对只在一个地方使用的类进行分组的方法:如果一个类只对另外一个类有用,从逻辑上来说可以把它们放在一起。嵌套这样的“Helper类”使它们的包更加流线型。
- 它增强了封装:考虑两个顶级类,A和B,B需要访问A中声明为private的成员。通过在类A中隐藏类B,A的成员可以声明为private并且B可以访问它。加上,类B自身也对外部世界隐藏了。
- 它可以产生更易于阅读和维护的代码:在顶层类中嵌套一些小类也让其与使用它的地方更近。
静态嵌套类
如同类方法和类变量,静态嵌套类关联它的外部类。与静态方法一样,静态嵌套类不能直接访问封闭类中的实例变量和方法:你只能通过对象引用来使用它。
使用封闭类名来访问静态嵌套类:
OuterClass.StaticNestedClass
比如,要创建一个静态嵌套类的对象,可以这样来做:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
内部类
如同实例变量和方法,内部类关联的是封闭类的对象,它可以直接访问封闭类对象的变量和方法。因为内部类关联的是对象,它自己不能定义任何静态成员。
内部类对象存在于外部类的对象中,考虑如下的类:
class OuterClass{
···
class InnerClass{
···
}
}
InnerClass 类的实例仅仅只能存在于OuterClass的实例中,并且可以直接访问封闭类对象的字段和方法。
为了实例化一个InnerClass,你必须首先实例化一个OuterClass。在外部类实例中创建内部类实例这样来做:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
内部类有两种形式:局部类和匿名类。
影子
如果在特定的范围(比如内部类或者方法定义)有一个类型的声明(比如成员变量或者参数名),与封闭类中的某个声明同名,那么这个声明就是封闭类声明的影子。单独使用名字不能引用到该影子声明。下面的示例,向你演示了这种情况:
public class ShadowTest{
public int x = 0;
class FirstLevel{
public 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 s1 = new ShadowTest();
ShadowTest.FirstLevel f1 = s1.new FirstLevel();
f1.methodInFirstLevel(23);
}
}
输出如下:
x = 23
this.x = 1
ShadowTest.this.x = 0
这个示例定义了三个名为x的变量:ShadowTest类的成员变量,FirstLevel类的成员变量,methodInFirstLevel方法的参数。变量x定义为methodInFirstLevel方法的一个参数,它对内部类FirstLevel的变量施加阴影。因此,当在methodInFirstLevel方法中使用变量x,它是指方法参数。为了引用FirstLevel类的成员变量,在封闭类范围内使用this关键字
System.out.println("this.x = " + this.x);
为了在封闭类之外的范围里引用成员变量。比如下面的语句表示从methodInFirstLevel方法中访问ShadowTest的成员变量
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
序列化
强烈反对序列化嵌套类,包括本地类和匿名类。Java编译器会对内部类执行合成构造;这些类、方法、字段和其他构造在源代码中没有对应的构造。合成构造使Java编译器能够实现新的Java语言特性,而无需更改JVM。然而,在不同的Java编译器实现中,合成构造可能会有所不同,这意味着.class文件在不同的实现中也可能有所不同。因此,如果你序列化一个内部类,然后使用不同的JRE实现反序列化它,那么可能会有兼容性问题。