-
前言
前面已经介绍了Java中类或对象的成员,包括方法、成员变量和语句块。其实类不但有这些类型的成员,也可以作为另一个类的成员。充当这些角色的类成为内部类,包含内部类的类成为外部类,使用内部类可以完成很多特殊的任务。
-
内部类概述
内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,依附于外部类而存在。内部类可以为静态,可用protected和private修饰(而外部类只能用public和protected的包访问权限)。内部类主要有以下几个类:成员内部类,静态内部类,匿名内部类。
为什么需要内部类?典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码创建其外围的类的对象。所以可以认为内部类提供了某个类或实现了某个接口,内部类的代码创建其外围类的对象。所以可以认为内部类提供了某种进入外围类的窗口。使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类没有影响。如果没有内部类提供的可以继承多个具体或抽象的类的能力,一些设计编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方法变得完整。接口解决了部分问题,而内部类有效的实现了“多重继承”。
内部类分为以下几种:
- 成员内部类,作为外部类的一个成员的存在,与外部类的属性、方法并列
- 局部内部类,在方法中定义的内部类成为局部内部类。与局部变量的属性相似,局部内部类不能访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内部常量,和此外部类的所有成员
- 匿名内部类,没有名字的内部类。匿名内部类为局部内部类,所以局部内部类所有的限制都对其生效
- 静态内部类,如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static,这通常称为嵌套类(nested class)
-
内部类语法规则
从外面内部类来看,完全可以将其看作是外部类的一个成员,与普通成员没什么区别,对普通成员的限制、修饰等都可以加之非静态内部类。只是这个成员不再是基本数据类型,也不再是对象引用,而是一个类,由一个类来扮演的角色。
下面给出了定义内部类的基本语法
class <外部类名>
{
[<成员的访问限制修饰符>] [static] class <内部类名>
{
//内部类的成员
}
//外部类的其他成员
}
内部类和外部类中的其他成员是一个级别的,其也是外部类的一个成员。在内部类类体中,它又是单独的一个类,一样有自己的成员方法和变量。可以加之于其它成员的访问限制修饰符都可以用来修饰内部类,包括private、protected、public。非静态成员的内部类被static关键字修饰后,就变成了静态成员的内部类。
下面给出一个很简单的定义内部类的例子,代码:
class Outter
{
public class Inner
{
int i=12;
}
int count=0;
}
上述代码中定义了两个类,Outter与Inner,Outter是一个普通的类,Inner是Outter的内部类,他们都有自己的成员变量。
创建内部类的方式有两种:
一、在外部类之内创建内部类对象
外部类的工作经常需要内部类的对象的辅助,在外部类中创建内部类是一种很常见情况。在外部类中创建内部类对象的语法与创建普通对象的语法相同,即用new操作符调用相应构造器即可。但需要注意的是,非静态内部类是外部类的非静态成员,不能在静态上下文中使用。
如下例是一个外部类中使用内部类对象的例子,代码:
package cahpter05;
class Outter
{
public class Inner
{
public void show()
{
System.out.println("调用了内部类的show方法");
}
}
//外部类中的方法调用内部类
public void OutterMethod()
{
Inner i= new Inner();
i.show();
}
}
public class Sample5_3
{
public static void main(String[] args)
{
// TODO Auto-generated method stub
Outter o=new Outter();
o.OutterMethod();
}
}
外部类中创建内部类对象的语法与创建普通对象的语法相同,使用new操作符调用构造器即可,对内部类而言,其自身也是一个类,在其类体中也可以拥有类所拥有的一切成员。
另外,虽然内部类在外部类的类体中,但编译后内部类与外部类各自产生一个类文件:
从图可以看出,内部类的命名规则为 <外部类名>$<内部类名> ,这也表示Inner与Outter是内部类。
二、外部类之外创建内部类对象
上面已经介绍了如何在外部类中创建内部类的对象,下面将介绍如何在外部类之外创建内部类的对象。对于非静态内部类在创建了外部内对象后才可以使用。所以,要调用内部类的对象类,首先要创建内部类的对象。
下面给出了在外部类之外创建内部类对象的基本语法:
<外部类类名> . <内部类类名> 引用变量= <外部类对象引用> . new<内部类构造器>;
<外部类类名> . <内部类类名> 引用变量=new <外部类构造器> . new 内部类构造器;
在外部类之外声明内部类的对象引用,其类型为“外部类类名内部类类名”。创建内部类对象时不能直接使用操作符new,而要使用“<外部类对象引用>new”来调用内部类的构造器。第二种语法与第一种语法含义相同,“new<外部类构造器>”返回的就是外部类的对象引用。
可以根据需要决定使用哪种语法。
下例是一个在外部类之外创建其内部类对象的例子,这里仍然使用 Outter类,不同点在于,不再采用在外部类之内创建内部类对象,而是在外部类外:
package cahpter05;
class Outter
{
public class Inner
{
public void show()
{
System.out.println("调用了内部类的show方法");
}
}
//外部类中的方法调用内部类
public void OutterMethod()
{
Inner i= new Inner();
i.show();
}
}
public class Sample5_3
{
public static void main(String[] args)
{
//创建外部类对象
Outter out=new Outter();
//创建内部类对象
Outter.Inner i= out.new Inner();
//调用内部类中的方法
i.show();
}
}
上面例子中,使用 Outter out= new Outer();语句生成了一个 Outter类对象,然后又使用Outers . Inner in= out . new Inner ();语句借助外部类的实例生成了一个内部类的对象。 main()方法中的两条语句也可以用下面的这一条语句替换: Outer . Inner in= new Outers() . new Inner ();
所以,在一个类中,创建另一个类( Outer)中的非静态内部类( Inner)必须要借助这个外部类(Ouer)的一个实例。可以看出,在外部类之外创建内部类对象与在外部类之内有以下区别:
(1)在外部类中声明内部类引用与创建其对象时,和常规声明引用与创建对象的语法相同。
(2)在外部类之外声明内部类引用时,需要用外部类类名加以标识,不能直接使用内部类类名,而创建内部类对象时,首先需要创建外部类的对象,然后才能创建内部类对象。
-
局部内部类
在方法内定义的内部类成为局部内部类,这种情况下,其作用域与局部变量相同,只在其所在的语句中块中生效。与局部变量相似,局部内部类不能有成员的访问限制修饰符,因为它不是外部类的一部分,但是它可以访问当前代码块内的常量,和此外部类的所有成员。
使用局部内部类有两个有点:
- 它对外面的所有类来说都是隐藏的,即使是它所属的外部类,仅有它所在的方法知道它。
- 它不仅可以访问它所属外部类中的数据,还可以访问局部变量,不过局部变量必须声明为final类型。
由于局部内部类只在局部有效,因此只能在其有效的位置访问或创建其对象,下例说明了局部内部类的简单使用:
package cahpter05;
class Outter
{
public void getInner()
{
class Inner
{
public void show()
{
System.out.println("局部内部类的对象中的show方法!");
}
}
//创建局部内部类对象
Inner i= new Inner();
i.show();
}
}
public class Sample5_4
{
public static void main(String[] args)
{
// TODO Auto-generated method stub
//创建外部类对象
Outter o=new Outter();
//调用外部类中的getInner方法
o.getInner();
}
}
非静态成员内部类中可以访问外部类的任何成员,而在局部类中一样可以访问外部类的成员,但却不可以访问同在一个局部的普通局部变量。
教科书上是这么说的,但是:
我这里可以访问。。。。。应该是jdk版本不一样,先挂上吧。
Final修饰的局部变量的存储方式与普通变量不同,其不会因为语句块的结束而消失,还会长期存在;
-
匿名内部类
匿名内部类就是没有名字的内部类,这里将介绍包括基本语法,对象的创建与使用以及匿名内部类的具体作用等。
匿名内部类没有名称,因此匿名内部类在声明类的同时也创建了对象。匿名内部类的声明要么是基于继承的,要么是基于实现接口的。这里主要介绍基于继承的匿名内部类。
基本语法如下:
new <匿名内部类要继承父类的对应构造器>
{
//匿名内部类体
};
上述语法声明了一个匿名内部类,又同时创建了一个匿名内部类的对象。new后面跟的是匿名内部类要继承父类的某个构造器,可以是无参构造器,也可以是有参构造器,同时,由于匿名内部类没有名称,所以无法为其编写构造器。
在匿名内部类类体中可以覆盖父类的方法,或者提供自己的新方法或成员。但需要注意的是,因为匿名内部类没有名字,所以没有办法声明匿名内部类类型的引用,因此提供的新方法与成员只能在自己的内部使用,外面无法调用。
例:
package cahpter05;
class Outter
{
public void show()
{
System.out.println("局部内部类的对象中的show方法!");
}
}
public class Sample5_8
{
public static void main(String[] args)
{
// TODO Auto-generated method stub
//重载Outter方法
Outter out =new Outter()
{
public void show()
{
System.out.println("创建匿名内部类的对象!");
}
} ;
out.show();
}
}
从上面的例子可以看出,
- 匿名内部类是没有名字的,所以在定义匿名内部类的同时也就创建了该类的对象,否则过后就无法再创建其对象了
- 通过引用访问匿名内部类的成员,均是通过多态完成的,因为匿名内部类根本无法定义其自身类型的引用
另外,由于匿名内部类也是一个独立的类,其编译后也将产生一个独立的类文件。但是由于没有名称,所以其类文件名的命名规则为:<外部类名称>$<n>,其中n表示的是第n个匿名内部类:
-
静态内部类
当内部类明前有static关键字时,该内部类为静态内部类。静态内部类是外部类的静态成员,其不依赖于外部类的对象而存在,因此在外部类外面创建静态内部类对象时不需要首先创建外部类对象。这点与非静态内部类是不同的,创建静态内部类的基本语法:<外部类名>.<内部类名> 引用变量 =new <外部类名> . <内部类构造器> ;
从语法中可以看出,声明引用的方式与非静态内部类相同,但创建对象时不用首先创建外部类对象了,直接调用内部类即可。例:
package cahpter05;
class Outter
{
static class Inner
{
public void show()
{
System.out.println("局部内部类的对象中的show方法!");
}
}
public void get()
{
Inner inner=new Inner();
inner.show();
}
}
public class Sample5_8
{
public static void main(String[] args)
{
//在外部类外创建静态内部类的对象
Outter.Inner i=new Outter.Inner();
i.show();
//在外部类外使用静态内部类的对象
new Outter().get();
}
}
在外部类Outter中创建使用了静态内部类Inner的对象,同时在外部类外也进行了相同的操作。运行结果:
可以看出,两种方式都能够正常访问。在外部类中创建静态内部类对象与创建非静态内部类对象是一样的,只是在外部类外面有所不同。
静态内部类也可以称为静态嵌套类,或称为顶级嵌套类,这是因为静态内部类与非静态内部类有多不同。非静态内部类应该与外部类的对象存在这成员共享的关系,是外部类对象组成的一部分,用来辅助外部类对象工作。
而静态内部类与外部类之间没有这样的关系,静态内部类其实已经脱离了外部类的控制。在创建其对象时已经不再需要外部类对象的存在,其实质只是一个放在别的类中的普通类而已。而static关键字只是说明其在创建对象时不依赖于外部类对象而存在,并不是说这个类本身是静态的。
-
后记
内部类实际上是由一个类扮演了特定的角色,例如,对于成员来说,其扮演了成员的角色,局部内部扮演了局部的角色,而在内部类里面则和其他普通类里面一样。
因此,从内部类里面来看,内部类就是一个类,而从内部类外面来看,内部类则是外部类的某种组成部分。这样用与修饰内部类的修饰符也随其扮演的角色不同而变化,下表列出了各种不同内部类可以使用的修饰符:
内部类 | 可以被修饰的修饰符 |
非静态成员内部类 |
final、abstract、public、private、protected、static |
静态成员内部类 | final、abstract、public、private、protected |
局部内部类 | final、abstract |
匿名内部类 | 不能对匿名内部类进行修饰 |
应该从不同角度去进行理解内部类,作为内部类,内部类拥有类的任何功能,满足类的一切规则。作为其在外部类中扮演的角色,内部类拥有所扮演角色的一切功能,满足所扮演角色的一切规则。