内部类
(一) 为什么需要内部类?
a) 每个内部类都能独立地继承自一个类,所以无论外围类是否已经继承了某个类,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。内部类使得多重继承的解决方案变得完整。
b) 内部类方法可以访问外部中的数据,包括私有的数据。内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。
c) 内部类可以对同一个包中的其他类隐藏起来。只有内部类可以是私有类,而常规类只可以具有包可见性,或公有可见性。
d) 方便编写回调函数,且不用编写大量代码时。
e) 减少命名冲突
(二) 什么是内部类?
内部类是指在一个外部类的内部再定义一个类。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。
内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成用$(美元符号)分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知。
(三) 内部类的共性
a) 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件。
b) 外部类不能直接访问内部类的的成员,但可以通过内部类对象来访问
c) 内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。
(四) 内部类分为四种:
a) 局部内部类(local inner classes)(与匿名内部类相似)
局部内部类: 即定义在方法中的内部类。
局部内部类类似于局部变量,不能定义为public,protected,private或者static类型。局部内部类内没有static成员。
它的作用域被限定在声明这个局部类的方法中。局部类有一个优势,即对外部世界可以完全地隐藏起来。即使外部类中的其他代码也不能访问它。除局部类所在方法外,没有任何方法知道局部类的存在。(更好的隐藏性)
u 为什么局部内部类只能访问该方法中的final类型参数?
因为局部变量的生命周期与局部内部类的对象的生命周期的不一致性!
设方法f被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object,它访问了该局部变量i .当方法f()运行结束后,局部变量i就已死亡了,不存在了.但:局部内部类对象inner_object还可能一直存在(只能没有人再引用该对象时,它才会死亡),它不会随着方法f()运行结束死亡.这时:出现了一个"荒唐"结果:局部内部类对象inner_object要访问一个已不存在的局部变量i!
如何才能实现?当变量是final时,通过将final局部变量"复制"一份,复制品直接作为局部内部类中的数据成员.这样:当局部内部类访问局部变量时,其实真正访问的是这个局部变量的"复制品"(即:这个复制品就代表了那个局部变量).因此:当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以访问局部变量(其实访问的是"复制品"),给人的感觉:好像是局部变量的"生命期"延长了.
那么:核心的问题是:怎么才能使得:访问"复制品"与访问真正的原始的局部变量,其语义效果是一样的呢?
当变量是final时,若是基本数据类型,由于其值不变,因而:其复制品与原始的量是一样.语义效果相同.(若:不是final,就无法保证:复制品与原始变量保持一致了,因为:在方法中改的是原始变量,而局部内部类中改的是复制品)
当变量是final时,若是引用类型,由于其引用值不变(即:永远指向同一个对象),因而:其复制品与原始的引用变量一样,永远指向同一个对象(由于是final,从而保证:只能指向这个对象,再不能指向其它对象),达到:局部内部类中访问的复制品与方法代码中访问的原始对象,永远都是同一个即:语义效果是一样的.否则:当方法中改原始变量,而局部内部类中改复制品时,就无法保证:复制品与原始变量保持一致了(因此:它们原本就应该是同一个变量.)
u 访问变量
//在内部类中访问内部类自己的变量直接用变量名,也可以用this.变量名
//在内部类中访问外部类中与内部类同名的成员变量用外部类名.this.变量名,若未同名则可以直接用变量名访问外部类变量
//可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的
//只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
//外部类其它方法无法访问内部类
publicclass LocalClass {
intb = 2;
publicvoid execute() {
finalinta = 1;
intc = 3;
class InnerClass {// 创建局部内部类
publicvoid execute() {
System.out.println("LocalInner Class");
System.out.println(a); // 局部内部类只能访问final类型的变量
System.out.println(b);
System.out.println(c);
}
}
new InnerClass().execute();// 只能在所在方法区域创建
}
publicstaticvoid main(String[] args) {
LocalClasslocalInner= newLocalClass();// 外部不能直接创建局部内部类
localInner.execute();
}
}
b) 匿名内部类(anonymous inner classes)
将局部内部类的使用再深入一步。假如只创建这个类的一个对象,就不必命名了,这种类被称为匿名内部类。
由于构造器的名字必须与类名相同,而匿名类没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给父类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。匿名内部类会隐式地继承一个父类或实现一个接口。
没有class关键字也没有extends和implements等关键字修饰。
匿名内部类不能是public,protected,private,static。不能定义任何静态成员、方法和类。
只能创建匿名内部类的一个实例。
一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
u 访问变量
//在内部类中访问内部类自己的变量直接用变量名,不可以用this.变量名
//在内部类中访问外部类中与内部类同名的成员变量用外部类名.this.变量名,若未同名则可以直接用变量名访问外部类变量
//只可以访问外部类的final变量
publicclass AnonymousInnerClassTest{
publicstaticvoid main(String[] args) {
AnonymousInnerClassTest test = newAnonymousInnerClassTest();
test.print(newDate(){//创建匿名内部类,生成的类名:AnonymousInnerClassTest$1
@Override//重写toString()方法
public String toString() {
return"Hello world.";
}
});
}
publicvoid print(Date date){
System.out.println(date);
}
}
c) 静态内部类(static inner classes)(与成员内部类相似)
有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外部类对象。为此,可以将内部类声明为static,以便取消产生的引用。
静态内部类可以用public,protected,private修饰(这一点与成员内部类相似),只有内部类可以声明为static(这是特殊之处),静态内部类中可以定义静态或者非静态的成员。静态内部类不能访问外部类的非静态成员(包括非静态变量和非静态方法),只能访问外部类的静态成员变量与静态方法。外部类可以访问内部类静态或者非静态成员。声明在接口中的内部类自动成为static和public。
u 访问变量
//在内部类中访问内部类自己的变量直接用变量名
//在内部类中访问外部类中与内部类同名的静态实例变量用外部类名.变量名,若未同名则可以直接用变量名访问外部类变量
// 外部类访问内部类的静态成员:内部类.静态成员
// 外部类访问内部类的非静态成员:实例化内部类即可,step1建立内部类对象,step2访问属性或方法。
// 生成一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成,而不需要通过生成外部类对象来生成。
publicclass StaticInner {
privatestaticinta = 1;
// 静态内部类生成的类名:StaticInner$InnerClass
publicstaticclass InnerClass {
// 静态内部类可以声明静态的成员变量,其他的内部类不可以
privatestaticintb = 1;
publicvoid execute() {
// 静态内部类只能访问静态变量
System.out.println(a + b);
}
}
publicstaticvoid main(String[] args) {
// 创建静态内部类
StaticInner.InnerClass innerClass = newStaticInner.InnerClass();
innerClass.execute();
}
}
d) 成员内部类(member inner classes)
成员内部类与普通的成员没什么区别,可以与普通成员一样进行修饰和限制,可以用public,protected,private修饰。不能用static修饰,成员内部类不能含有static的变量和方法。成员内部类可以访问外部类的静态与非静态的方法和成员变量。
u 访问变量
//在内部类中访问内部类自己的变量直接用变量名,也可以用this.变量名
//在内部类中访问外部类中与内部类同名的实例变量用外部类名.this.变量名,若未同名则可以直接用变量名访问外部类变量
//外部类的非静态方法访问成员内部类,step1建立内部类对象,step2访问属性或方法。
//外部类的静态方法访问成员内部类,与在外部类外部访问成员内部类一样step1 建立外部类对象step2 根据外部类对象建立内部类对象step3 访问内部类(除非已经有了外围类的一个对象,否则不可能生成内部类的对象。)(有待商榷)!!!!!!
publicclass MemberInner{
privateinta = 1;
publicvoid execute(){
InnerClass innerClass = this.new InnerClass();//在外部类中创建成员内部类
}
publicclass InnerClass{ //成员内部类
privateinta = 2;//内部类可以创建与外部类同名的成员变量
publicvoid execute(){
System.out.println(this.a);//this引用的是内部类
System.out.println(MemberInner.this.a); //在内部了中使用外部类的成员变量
}
}
publicstaticvoid main(String[] args) {
MemberInner.InnerClassinnerClass= newMemberInner().newInnerClass();//创建成员内部类
innerClass.execute();
}
}