1 嵌套类定义
在一个类的内部定义另一个类,这种类称为嵌套类。
2 嵌套类的分类
嵌套类根据加不加static修饰符可分为两类:静态嵌套类和非静态嵌套类,后者又叫内部类,可再细分成三类。
顶级类 top-level classes
a.嵌套顶级类 nested top-level classes
b.成员内部类 instance inner classes
c.本地内部类 local inner classes
d.匿名内部类 anonymous inner classes
3 嵌套类的好处
a.令源代码更清晰。
b.减少命名冲突。
c.控制框架:一个“应用程序框架”是指一个或一系列类,它们专门设计用来解决特定类型的问题。为应用该框架,我们可从一个或多个类继承,并覆盖其中的部分方法。我们在覆盖方法中编写的代码用于定制由那些框架提供的常规方案,以解决实际问题。“控制框架”属于应用程序框架的一种特殊类型,受到对事件响应的需要的支配;主要用来响应事件的一个系统叫作“事件驱动的系统”。内部类可以在单独一个类里表达一个控制框架应用的全部实施细节,从而完整地封装与那个实施有关的所有东西。内部类用于表达多种不同类型的action(),以便解决实际的问题。除此以外,若使用了private内部类,实施细节会完全隐藏起来,可以安全地修改。因为能方便地访问外部类的任何成员,内部类使我们具体的实施变得更加巧妙。
4 嵌套类的共性
a.嵌套类是独立的类,编译后会生成相应.class文件,但之前有顶级类的类名和$。
b.嵌套类是顶级类的一部分,可以自由的访问顶级类的成员,不论是否为private的,同理,顶级类也可以访问嵌套类的private成员。
c.嵌套类的成员及方法可以与顶级类重名,在类内会被覆盖。
d.嵌套类也可继承和被继承,但要注意作用域规则。
5 嵌套顶级类
从技术上讲,嵌套顶级类不属于内部类。因为内部类和外部类共享一种实例关系,而嵌套顶级类只是位于一个类的内部,并没有以上共享关系。
静态的含义是,为创建一个嵌套顶级的对象,我们不需要一个顶级类对象。不能从嵌套顶级类的一个对象中访问一个顶级类对象。
与静态方法相似,嵌套顶级类只能访问顶级类的static(静态)成员及方法。
class TLC1 {
private static String s1 = "tlc1:static string";
String s2 = "tlc1:non-static string";
// 嵌套顶级类
static class NTLC {
private String ns1 = "ntlc:string";
private static String ns2 = "ntlc:static string";
public void f() {
System.out.println(s1); // 可访问private
// ! System.out.println(s2); 不能访问顶级类的非static成员
}
}
public void f() {
System.out.println(new NTLC().ns1); // 可访问private
System.out.println(NTLC.ns2);
}
}
main里的验证代码:
TLC1.NTLC ntlc1 = new TLC1.NTLC();
TLC1.NTLC ntlc2 = new TLC1.NTLC();
System.out.println(ntlc1 == ntlc2); // 返回false
ntlc1.f();
new TLC1().f();
运行结果:
false
tlc1:static string
ntlc:string
ntlc:static string
6 成员内部类
成员内部类内不允许有任何静态声明,能够访问成员内部类的唯一方式就是通过顶级类(外部类)对象。
a.从外部类的非静态方法访问
实质上是通过外部类的this引用实现的。
b.从外部类的静态方法访问
由于静态方法没有this引用,必须先创建一个外部类对象,然后调用.new创建成员内部类对象进行访问。
c.从其他类访问
先创建外部类对象,再调用.new创建成员内部类对象。
d.从内部类中访问外部类对象
可以使用Outer.this来访问。
class TLC2 {
private String s = "tlc2:string";
// 成员内部类
class IIC {
private static final String is1 = "iic:static final string";
// ! static String is2 = "iic:static string"; static必须同时是final的
private String is3 = "iic:string";
public void f1() {
System.out.println(s);
System.out.println(TLC2.this); // 访问外部类对象
}
// ! static void f2(){} 不能声明静态函数
}
public void f1(){
new IIC().f1();
}
public static void f2(){
TLC2 tlc2 = new TLC2();
tlc2.new IIC().f1();
}
}
main代码:
TLC2 tlc2 = new TLC2();
tlc2.f1();
tlc2.f2();
TLC2.IIC iic = tlc2.new IIC();
iic.f1();
结果:
tlc2:string
day090521.TLC2@1bab50a
tlc2:string
day090521.TLC2@c3c749
tlc2:string
day090521.TLC2@1bab50a
对于普通的类,可用的修饰符有final、abstract、strictfp、public和默认的包访问。
但是成员内部类更像一个成员变量和方法,可用的修饰符有:final、abstract、public、protected、private、默认、strictfp和static。
一旦用static修饰,它就变成静态嵌套类了囧。
7 本地内部类
本地内部类定义在方法中,类似于本地变量(局部变量)。所以可以用于修饰本地内部类的只有final和abstract。
本地内部类只能在定义该内部类的方法或块内实例化,不可以在外部对其实例化。
本地内部类对象不能使用该内部类所在方法的非final局部变量。因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
静态方法是没有this引用的,因此在静态方法内的内部类遭受同样的待遇,即只能访问外部类的静态成员。
class TLC3 {
private String s = "tlc3:string";
public void f(boolean b) {
String s1 = "tlc3:f:string";
final String s2 = "tlc3:f:final string";
if (b) {
// 本地内部类
class LIC1 { // 块内可见
private String ls;
LIC1(String str) {
this.ls = str;
}
public void f() {
System.out.println(s);
// ! System.out.println(s1); 只能使用final类型的外部对象
System.out.println(s2);
System.out.println(ls);
}
}
// ! new LIC(); 只能有一个构造函数
System.out.println(new LIC1("lic1:string").ls);
final class LIC2 extends LIC1 {
LIC2() {
super("lic2:string");
super.f();
}
}
new LIC2();
// ! class LIC3 extends LIC2{} final类不可继承
} else {
class LIC1 {
}
}
// ! new LIC2(); 超出作用域
}
}
main代码:
new TLC3().f(true);
结果:
lic1:string
tlc3:string
tlc3:f:final string
lic2:string
上面的代码会生成TLC3$1LIC1.class和TLC3$2LIC1.class,可看出命名规则:外部类名+$+数字编号+内部类名,以防内部类重名。
8 匿名内部类
a.用于实现一个接口
b.用于扩展拥有非默认构造函数的一个类
c.用于执行字段初始化
d.通过实例初始化进行构建(匿名内部类不可拥有构造函数)
class TLC4 {
// 匿名内部类
public Contents f1() {
return new Contents() {
@Override
public String get() {
return "tlc4:f1:anonymous:string";
}
};
}
public void f2(AC ac) {
ac.f1();
ac.f2();
}
public AC f3(final int i) {
return new AC(i) {
// 初始化
{
if (i == 1)
System.out.println("i==1 is true");
}
@Override
public void f1() {
System.out.println("tlc4:f3:anonymous:f1");
}
};
}
}
abstract class AC {
private int i;
public AC(int i) {
this.setI(i);
}
abstract public void f1();
public void f2() {
System.out.println("AC:f2");
}
public void setI(int i) {
this.i = i;
}
public int getI() {
return i;
}
}
interface Contents {
String get();
}
main代码:
TLC4 tlc4 = new TLC4();
System.out.println(tlc4.f1().get());
tlc4.f2(new AC(3) {
@Override
public void f1() {
System.out.println("anonymous:f1 i:" + this.getI());
}
});
tlc4.f3(1);
执行结果:
tlc4:f1:anonymous:string
anonymous:f1 i:3
AC:f2
i==1 is true
9 链接到外部类
创建自己的内部类时,那个类的对象同时拥有指向封装对象(这些对象封装或生成了内部类)的一个链接。内部类的一个对象只能与封装类的一个对象联合创建。
10 小结
java嵌套类内容很多,很纷杂。要记清其种类以及每种相应的规则,做到胸有成竹。本文完整源代码下载。
posted on 2009-05-21 17:45
chenkkkabc 阅读(205)
评论(0) 编辑 收藏 所属分类:
java特性