内部类是指一个外部类的内部再定义一个类。内部类作为外部类的一个成员,依附于外部类而存在。内部类可为静态,可用public和 private修饰(而外部类只能使用 public 和 protected 的包访问权限)。内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类。
(1)成员内部类,作为外部类的一个成员存在,与外部类的属性、方法并列。
(2)局部内部类,在方法中定义的内部类称为局部内部类。与局部变量类似,局部内部类不能有访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,和此外部类的所有成员。
(3)匿名内部类,没有名字的内部类,匿名内部类为局部内部类,所以局部内部类的所有限制对其生效。
(4)静态内部类,如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static,这通常称为嵌套类(nested class)。
为什么需要内部类?
典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码创建其外围类的对象。所以可以认为内部类提供了某种进入其外围类的窗口。使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计或编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
定义内部类的基本语法
class<外部类>
{
[<成员的访问限制修饰符号>] [ static] class <内部类名>
{
// 内部类的成员
}
//外部类的成员
}
内部类和外部类中的其他成员是一个级别的,其也是外部类的一个成员。在内部类类体中,它又是单独的一个类,一样有自己的成员变量和方法。可以加之于其他成员的访问限制修饰符号都可以用来修饰内部类,包括private 、 protected 、 public。非静态成员内部类被 static 关键字修饰后变成了静态成员内部类。
创建内部类的方式
1.在外部类之内创建内部类对象
//外部类之内创建内部类对象示例
package chapter05.sample5_4;
public class Outter{
//定义内部类
public class Inner{
//定义内部类方法show,用来打印输出
public void show(){
//打印输出
System.out.println("调用了内部类中的show方法");
}
}
//外部类中的方法,调用内部类
public void outterMethod(){
//在外部类中创建内部类的对象
Inner i = new Inner();
//调用内部类中的show方法
i.show();
}
}
public class Sample5_4{
public static void main(String[] args){
//创建外部类的对象
Outter o = new Outter();
//调用外部类中创建内部类对象的方法
o.outterMethod();
}
}
外部类中创建内部类对象的语法与创建普通对象的语法相同,使用new操作符调用构造器即可。另外,虽然内部类在外部类的类体中,但编译后内部类与外部类各自产生一个类文件,“Outter$Inner.class”是内部类的类文件。
2.在外部类之外创建内部类对象
在外部类之外创建内部类对象的基本语法:
<外部类类名>.<内部类类名> 引用变量 = <外部类对象引用>.new <内部类构造器>;
<外部类类名>.<内部类类名> 引用变量 = new <外部类构造器>.new <内部类构造器>;
在外部类之外声明内部类的对象引用,其类型外“外部类类名.内部类类名”。创建内部类对象时不能直接使用操作符new,要使用“<外部类对象引用>.new”来调用内部
public class Sample5_5{
public static void main(String[] args){
//创建外部类的对象
Outter out = new Outter();
//创建内部类对象
Outter.Inner i = out.new Inner();
//调用内部类中的方法
i.show();
}
}
在上例中,使用Outer out = new Outer();语句生成了一个Outer类对象,然后又使用Outer.Inner in = out.new.Inner();语句借助外部类的实例生成了一个内部类的对象。main ( )方法中的两条语句也可以用下面的这一条语句替换:Outer.Inner in = new Outer().new Inner();所以,在一个类中,创建另外一个类(Outer)中的非静态内部类(Inner)必须要借助这个外部类(Outer)的一个实例。
在外部类之外创建内部类对象与在外部类之内有以下区别
(1)在外部类中声明内部类引用与创建其对象时,和常规声明引用与创建对象的语法相同
(2)在外部类之外声明内部类引用时,需要外部类类名加以标识,不能直接使用内部类类名,而创建内部类对象时,首先需要创建外部类的对象,然后才能创建内部类对象。
局部内部类
在方法内定义的内部类称为局部内部类。在这种情况下,其作用域与局部变量相同,只在其所在语句块中有效。与局部变量类似,局部内部类不能有成员的访问限制修饰符,因为它不是外部类的一部分,但是它可以访问当前代码块内的常量,和此外部类的所有成员。
使用局部内部类有如下两个优点。
(1)它对外面的所有类来说都是隐藏的,即使是它所属的外部类,仅仅它所在的方法知道它。
(2)它不仅可以访问它所属外部类中的任何成员,还可以访问局部变量,不过局部变量须声明为final类型。
由于局部内部类只在局部有效,因此只能在其有效的位置访问或创建其对象。
//外部类之内创建内部类对象示例
package chapter05.sample5_4;
public class Outter{
//定义内部类
public class Inner{
//定义内部类方法show,用来打印输出
public void show(){
//打印输出
System.out.println("调用了内部类中的show方法");
}
}
// //外部类中的方法,调用内部类
// public void outterMethod(){
// //在外部类中创建内部类的对象
// Inner i = new Inner();
// //调用内部类中的show方法
// i.show();
// }
}
public class Sample5_4{
public static void main(String[] args){
//创建外部类的对象
Outter o = new Outter();
// //调用外部类中创建内部类对象的方法
// o.outterMethod();
Outter.Inner i = o.new Inner();
i.show();
}
}
public class Sample5_5{
public static void main(String[] args){
//创建外部类的对象
Outter out = new Outter();
//创建内部类对象
Outter.Inner i = out.new Inner();
//调用内部类中的方法
i.show();
}
}
局部内部类可以访问final的局部变量。原因在于,普通的局部变量随着所在语句块的执行结束而消亡,而创建的局部内部类对象并不会随着语句块的结束而消亡。如果在语句块结束后,调用了局部内部类对象中访问普通变量的方法就要出现问题,因为此时要访问的局部变量不存在了。
Final修饰的局部变量的存储方式与普通局部变量不同,其不会因为语句块的结束而消亡,还会长期存在,因此可以被局部内部类访问。
匿名内部类
匿名内部类没有名称,因此匿名内部类在声明类的同时也创建对象。匿名内部类的声明要么基于继承的,要么基于实现接口的。
new <匿名内部类要基于继承父类的对应构造器>
{
//匿名内部类类体
};
上面语法既声明了一个匿名内部类,又同时创建类一个匿名内部类的对象。
在匿名内部类类体中可以覆盖父类的方法,或提供自己新的方法与成员。但要注意的是,因为匿名内部类没有名字,所以没有办法声明匿名内部类类型的引用,因此提供的新的方法与成员只能自己内部使用,外面无法调用。
基于继承的匿名内部类的使用如下
package chapter05.sample5_8;
//这里需要注意,Outter类与前面的Outter不同,下面的Outter类中没有内部类体
public class Outter{
//定义了名称为show的方法,这样便可以在后边的代码定义继承自该类的匿名内部类
public void show(){
//打印输出
System.out.println("这里是Outter类的方法");
}
}
/*
在主方法Sample5_8中定义了继承自outter的匿名内部类,并且重载了其父类的show方法,
随后通过引用调用该方法。
*/
public class Sample5_8{
public static void main(String[] args)
{
//定义匿名内部类并创建其对象
Outter out = new Outter(){
//重载Outter的方法
public void show(){
//打印输出
System.out.println("创建匿名内部类的对象!!!");
}
}; //注意这里有个分号
//访问匿名内部类中重写的方法
out.show();
}
}
从上例可以看出
(1)匿名内部类是没有名字的,所以在定义匿名内部类的同时也就创建了该类的对象,否则过后无法再创建其对象了;
(2)通过引用访问匿名内部类的成员,均是通过多态完成的,因为匿名内部类根本无法定义其自身类型的引用。
另外,由于匿名内部类也是一个独立的类,其编译后将产生一个独立的类文件。但是由于没有名称,所以其类文件的命名规则为<外部类名称>$<n>,其中“n”表示是第n个匿名类
静态内部类
当内部类名前面有static关键字时,该内部类为静态内部类。静态内部类是外部类的静态成员,其不依赖于外部类的对象而存在,因此在外部类外面创建静态内部类对象时不需要首先创建外部类的对象。
<外部类类名>.<内部类类名> = new <外部类类名>.<内部类构造器>;