1.Nested Class 嵌套类
JAVA语言允许你在一个类里面定义另外一个类,这样的类被称为嵌套类。举例如下:
class OuterClass {
...
class NestedClass {
...
}
}
术语:嵌套类分为2大类:静态和非静态。被声明为static的嵌套类称为静态嵌套类;非静态嵌套类则被称为内部类。
class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass
}
一个嵌套类是它的包围类的成员。非静态嵌套类(inner classes)可以访问包围类的其他成员,即使成员被声明为private。静态嵌套类不能访问包围类的其他成员。作为OuterClass的成员,嵌套类可以被声明为private,public, protected, 或者 package private。(记得outerclass只能被声明为public或者package private)
为什么要使用嵌套类?
以下是一些吸引人的理由:
- 是一个逻辑上分组那些只在一个地方使用的方法:如果一个类只对某个类有用,那么逻辑上他就应该嵌入那个类,让他们合二为一。
- 提高封装性:考虑2个top-level的类,A和B, B需要访问A的成员,除此之外,A的成员还将被声明为private。通过在A中隐藏B,A的成员就能被声明为private,B也能访问他们。此外,B本身也可以在外面的世界隐藏掉。
- 可以提高可读性和维护性:在顶层类中嵌套小型类使得代码更接近于被使用的地方
静态嵌套类
作为类方法和变量,嵌套类关联与他的包围类。就像静态类方法,嵌套类不能直接引用他的包围类实例的变量或者方法: 可以通过对象引用来达成目的
请使用包围类的名字来访问静态嵌套类:
OuterClass.StaticNestedClass
举例,要创建一个静态类对象,使用以下语法:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
内部类
作为实例方法和变量,内部类关联于它的包围类的一个实例,并能直接访问对象的方法和成员。同时,因为内部类关联于一个实例,它就不能定义它自己的静态成员
内部类的实例存在于一个外部类的实例中,考虑以下classes:
class OuterClass {
...
class InnerClass {
...
}
}
InnerClass的实例只能存在OuterClass的实例内部,能直接访问包围类的方法和域。
要实例化一个内部类,你首先要实例化OuterClass。然后,使用语法在包围类里面创建内部类对象:
OuterClass.InnerClass innerClass = outerObjct.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 st = new ShadowTest();
ShadowTest.FirstLevel f1 = st,new FirstLevel;
f1.methodInFirstLevel(23);
}
}
以下是例子的输出:
x = 23
this.x = 1
ShadowTest.this.x = 0
这个例子定义了三个变量都名为x:类ShadowTest的成员变量,内部类FirstLevel的成员变量,还有方法methodFirstLevel的参数。定义为方法methodFirstLevel的变量x遮蔽了内部类FirstLevel的变量。结果就是,当你在方法methodLevel里使用变量x的时候,它指向的方法参数。要指向内部类FirstLevel的成员变量,使用关键字this来代表包围类:
System.out.println("this.x = " + this.x);
可以通过所属的类名来指向更大包围范围的变量。举个例子,以下表达式就从方法methodFirstLevel里访问了类ShadowTest的成员变量:
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
序列化
不赞同去序列化内部类,包括local 和 anonymous类。当java编译器去编译某些结构,比如内部类,它会创建复合结构;有类,方法,域和其他的在源代码中没有对应的结构的结构。复合结构让java编译器在不改变JVM的前提下实现了新的java功能。然而,复合结构在不同的java编译器版本中会有不同的变化,也就是说,.class文件会有不同的实现。结果就是你会遇到兼容性问题,假如你在不同版本的JRE里进行序列化。参考章节Implicit and Synthetic Parameters in the section Obtaining Names of Method Parameters for more information about the synthetic constructs generated when an inner class is compiled.
本地类和无名类
此外还有2种类型的内部类。你可以在一个方法的内部声明一个嵌套类。我们称之为本地类。你也可以在方法里声明一个没有名字的嵌套类,我们称之为匿名类。
修饰符
对于嵌套类,你可以使用同样对于外部类成员能使用的修饰符, 来限制其他类的成员访问它的权限