Hi!请登陆

计算机等级二级考试辅导:“内部类”单元综合复习(第1部分)

2021-3-1 47 3/1

一、什么是内部类?内部类的实现形式及编程特性?

1、什么是内部类(Inner Class)

Java语言中的程序类主要分为正常程序类(也称为外部类)和内部程序类,而所谓的内部类就是某个类的定义体在另一个类的定义体内,在Java语言中将这样定义出的程序类称为内部类(在Java语言中称为Inner Class,但在C++语言中称为Nested Class内嵌类)。而内部类可以为有名内部类(Named Inner Class)和无名内部类(Anonymous Inner Class)。

许多Java程序员在应用编程中都很少应用到内部类,或者对内部类的应用和了解也仅仅是在Java Swing GUI组件编程中的事件注册程序(事件监听)中应用过或在线程的功能实现中可能会涉及到。其实,内部类有许多独特的编程特性,它可以使得程序代码更加简洁和优雅。

2、内部类的实现形式及编程特性

在如下示例图所示的程序代码示例中说明了Java程序中的外部类(参看示例程序中的SomeOneNormalClass类)、有名内部类(参看示例程序中的NamedInnerClass类)和无名内部类(在normalClassMethod方法中创建出的impleClassObject对象所属的类)的定义方式及基本的编程特性。

其中的无名内部类又称为匿名内部类,它一般是某个接口的实现类,参看如下示例图中所示的SomeOneInterface接口定义。可以直接创建出SomeOneInterface接口类型的匿名实现类的对象实例(参看示例图所示程序代码中创建impleClassObject对象的程序代码示例)。

从上面的示例图的程序代码示例中可以了解到,在内部类(包括有名内部类和无名内部类)中可以直接无缝地访问外部类中的成员属性和方法(包括private类型的成员)和创建出外部类的对象实例,但在外部类中的成员方法中却不能访问内部类(包括有名内部类和无名内部类)中的成员属性和方法。这也就是说,外部类中的成员属性和方法对内部类而言是“可见的”(内部类访问外部类不需要任何特殊的条件,拥有外部类所有的访问权限),而内部类中的成员对外部类而言是“隐藏的”(参看上面示例图中的错误代码)。

因为内部类名与它的外部类的对象成员名具有相同的访问权限规则;甚至如果一个内部类是在一个方法内定义(此内部类则称为局部内部类),该方法中的局部变量在此局部内部类中也可以被使用。但此局部变量必须要被声明为final类型,这是由于局部内部类会将调用的变量进行拷贝,为了保证一致性要将此局部变量定义为final类型。

从作用域的角度来看,内部类被隐藏在外部类之中、而且是一个独立的个体,该内部类名只能在外部类中的成员方法中被使用,从而可以在外部类的成员方法中创建出内部类的对象实例,而且创建内部类的对象实例并不依赖于外部类的对象实例的创建。因此,内部类在应用中能够提供更好的封装性。

利用这个特性,从而可以实现在内部类中直接访问和共享外部类中的成员,但内部类自身又可以得以封装。在实际的项目应用开发中,也正是需要获得这样的编程特性才应用内部类。

3、在Java语言中为什么要提供内部类

定义内部类的主要目的在于隐藏类名,减少全局的标识符但仍然可以封装代码块,从而限制用户使用该类建立对象实例。这样可以提高类的抽象能力,并且强调了两个类(外部类和内部类)之间的主从关系。因为内部类只在它的外部类的方法中被使用,而不需要在其它类的方法中被使用。

二、简述内部类的典型应用

1、应用内部类可以使得Java程序类产生多重继承的应用效果

Java与C++语言在继承方面的一个差别就在于不再支持类的多重继承方式,但允许接口支持多重继承方式,其主要的目的一方面是简化类的继承关系的复杂性,另一方面则是避免出现C++语言由于类的多重继承方式而导致“虚基类”的共同祖先类的问题出现。

在Java语言中尽管为接口提供了多重继承方式,但在很多应用中一个类可能需要继承多个不同类,但这些类并不是接口而可能是抽象类或者普通类。此时,如何实现这样的的应用要求?当然,可以应用内部类,参看如下示例图中所示的程序代码示例。

在此示例中,OuterNormalDerivedClass类需要继承于SomeOneBaseClass 和SomeTwoBaseClass这两个类。为此,可以将OuterNormalDerivedClass类直接继承于SomeOneBaseClass类,同时再内嵌一个SomeTwoBaseClass类(由于它是抽象类)的派生类,从而就可以共享访问SomeOneBaseClass
和SomeTwoBaseClass这两个类中所需要的功能方法。

因此,如果在应用开发中涉及应用接口无法解决的问题时,尝试应用内部类就可能得以解决,Java程序设计中的内部类使得Java在多重继承的解决方案方面不仅简化了继承关系,也提高了程序代码的可靠性。

2、多个内部类以不同的方式实现同一接口或者继承同一基类

在如下示例图所示的程序代码示例中,定义有一个SomeOneInterface接口,但在外部类SomeOneNormalClass的两个不同方法中分别创建出SomeOneInterface接口的两个不同的实现类的对象实例,SomeOneInterface接口中的anonymousInnerClassMethod();方法将以不同的功能实现方式出现在外部类SomeOneNormalClass中。

如果有读者熟悉Java Swing GUI组件的事件编程,就不会对此形式的程序代码感到陌生。比如,在JFrame的窗体中内嵌有多个不同的JButton按钮组件,可以为不同的按钮提供对ActionListener点击事件的不同实现类,从而产生出不同的点击事件处理方法。

在内部类中甚至可以访问它所在的方法中的局部变量,比如在内部类的anonymousInnerClassMethod方法中分别访问normalClassMethodOne方法和方法normalClassMethodTwo中的局部变量intValOne和intValTwo。

3、内部类的每个对象实例都有自己独立的状态信息,但可通过外部类进行交互

面向对象程序设计方法中的对象状态是通过该对象的属性来体现的,而且多个内部类可以以不同的方式实现同一接口或者继承同一基类。因此,在同一接口的不同实现类(或者同一基类的不同派生类)中可以设计出不同的成员属性,从而使得内部类的每个对象实例都有自己独立的状态信息。

但如果这些内部类的不同对象实例之间需要进行数据通讯或者相互交互,则可以通过外部类作为中间的媒介加以实现。在如下示例图所示的程序代码示例中,为SomeOneInterface接口的两个不同的实现类提供不同的属性,从而使得基于此接口的不同内部实现类对象实例都有自己独立的状态信息,但可以通过外部类中的属性intValOne实现两个不同的内部类属性变量intValTwo和intValThree之间的交互。

三、匿名内部类是否可以继承其它类?是否可以实现接口?

1、匿名内部类不能继承其它类

在类的继承关系中的子类定义中,需要明确地定义出子类的名称,子类不能为匿名类。因此,匿名内部类不能继承其它类,并且没有访问修饰符(public、private、protected等)和没有构造方法定义(因为它连类名都没有),而且也只能创建出某个匿名内部类的一个对象实例——匿名内部类是伴随着类定义的同时就必须要被对象实例化,从而导致匿名内部类不能重复地创建对象实例。

但一个接口也可以像内部类那样直接定义在某个类体中,由另一个内部类(可以是有名或者匿名内部类)加以实现。在如下示例图所示的程序代码示例中,在外部类SomeOneNormalClass中内嵌一个SomeOneInterface接口定义体,但为该接口提供两个不同的内部实现类,其中一个为有名内部类NamedInnerClass和另一个为匿名内部类。

如果接口的不同的内部实现类的对象实例之间需要进行数据通讯或者相互交互,同样可以通过外部类作为中间的媒介加以实现(在代码示例中,通过外部类中的属性intValOne实现两个不同的内部类属性变量intValTwo和intValThree之间的交互)。

2、有名内部类不仅可以实现内部接口,也可以继承其它的类

在如下示例图所示的程序示例代码中,有名内部类NamedInnerClass不仅实现了内部接口SomeOneInterface,而且还继承于Thread线程类。

有名内部类的类名称(如代码示例中的NamedInnerClass)不能与包含它的外部类的类名称同名(如代码示例中的SomeOneNormalClass)。

四、简述Java内部类有哪些形式

1、Java程序设计中的内部类主要形式

在Java程序设计中的内部类,主要分为成员内部类(有名或者匿名)、局部内部类(有名或者匿名)、静态内部类等类型。

2、成员内部类(有名或者匿名)

从字面上理解,成员内部类也就是以外部类的一个成员的形式定义出,参看如下示例图中所示的程序代码示例。在SomeOneOuterClass外部类定义体中定义了两个内部类,分别为有名的内部类NamedInnerClass和匿名内部类。

成员内部类是应用最频繁的内部类,由于它是外部类定义体中的一个成员,所以在成员内部类中可以无限制地访问外部类中的所有成员属性和成员方法(包括private类型的成员);但在外部类中的成员方法中,欲要访问成员内部类中的成员属性和方法,则需要通过创建成员内部类的对象实例来访问。

此外,在成员内部类中不能定义有任何形式的static属性和方法(参看上面示例图所示的程序代码中的错误代码),否则该成员内部类就需要定义为static静态成员内部类。

3、局部内部类(有名或者匿名)

在某个类的成员方法中定义出的内部类,称为局部内部类,它的作用域限定于方法体内,而且局部内部类不能用public或者private或者protected访问说明符修饰局部内部类。之所以要应用局部内部类,是为了解决某个问题时临时性地需要一个类,但又不需要这个类是公共可用的。此时,可以应用局部内部类。由于局部内部类的作用域限定于方法内,因此可以实现“临时”应用的效果。参看如下示例图中所示的程序代码示例。

局部内部类并不只是局限于在方法内定义,其实在某个作用域内(包括条件语句、循环语句等局部代码块{ }中)都可以定义出局部内部类。只是由于在方法内定义的局部内部类在应用方面更常见。在如下示例图所示的程序代码示例中,在for()循环和if条件语句中分别定义出SomeOneInterface接口的两个局部内部实现类。

4、静态内部类(必须有类名)

(1)什么是静态内部类

熟悉Java程序设计的读者一般都熟悉静态方法、静态属性以及静态语句块,其实static定义符也可以修饰一个内部类,从而形成静态内部类(注意:static定义符只能修饰内部类而不能修饰普通类)。

静态内部类属于外部类本身,而不再属于外部类的某个对象实例。静态内部类与非静态内部类(普通的内部类)之间有什么不同吗?

(2)静态内部类与非静态内部类的不同

非静态内部类(普通的内部类)在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,但这个引用在静态内部类中却没有提供。从而导致在静态内部类中不能使用任何外部类的非static成员属性和成员方法(参看如下示例图所示的程序代码示例中的错误代码),也不能从一个static内部类的对象实例中访问一个外部类的对象实例,这是由Java语法中“静态方法不能直接访问非静态成员”的规则所限定。此外,静态内部类的创建也不需要依赖于外部类,这表明静态内部类更加独立于对应的外部类。

Java内部类(Inner Class)与C++嵌套类(Nested Class)最大的不同就在于是否有指向外部类的引用上。在普通的内部类中由于有对外部类的引用,故在普通的内部类成员方法中是可以直接访问外部类中的static静态成员(属性和方法)和普通的成员(属性和方法)。

此外,静态内部类在定义时需要应用static定义符加以修饰,因此不存在无名静态内部类。在静态内部类中可以定义有静态成员,而在非静态内部类中则不能定义有静态成员(参看上面示例图中所示的程序代码示例中的错误代码);静态内部类的非静态成员可以访问外部类的静态成员属性,而不可访问外部类的非静态成员属性;非静态内部类的非静态成员可以访问外部类的非静态成员属性。

生成一个静态内部类的对象不需要外部类成员,静态内部类的对象可以直接生成,而不需要通过生成外部类对象来生成(参看如下的程序代码示例):

OutClass.InnerStaticClass someOneObject = new OutClass.InnerStaticClass ();

5、在什么应用要求下必须要将普通内部类设计为静态内部类

(1)应用场合之一

如果在普通内部类中要求声明出static的成员属性或者方法,则该普通内部类本身也必须要声明为静态内部类。参看如下示例图所示的程序代码示例,由于在NamedInnerClass内部类中定义有static类型的成员属性intValFour,该NamedInnerClass内部类也必须要定义为静态内部类(由static加以修饰)。

而在普通内部类中不能定义有静态成员(属性和方法),否则将出现语法错误(参看如下示例图所示的程序代码示例中的错误提示信息)。

(2)应用场合之二

如果在应用开发中使用内部类只是为了将一个类隐藏到另一个类的内部,而并不需要在内部类中引用外部类的对象而访问外部类的成员,此时可以将此种应用要求的内部类声明为static静态内部类,以便取消产生的对外部类的对象引用。

6、接口中所定义的内部类默认为静态内部类

许多读者对Java语言中的接口应该不会感觉到陌生,一般的程序员对接口的理解可能还停留在接口中只允许定义方法的原型的认知上。其实,在接口中也允许包含有内部类的定义体。

在如下示例图所示的程序代码示例中,在StaticInnerClassOuterInterface接口中定义有一个方法someOneInterfaceMethod。但该方法提供有两个不同的实现版本,其一版本是在StaticInnerClassOuterInterface接口中通过内部类StaticInnerClass加以实现,而另一个版本则是由普通类NormalImplementClass加以实现。

在接口中定义的内部类默认为静态内部类,也就是示例图中的StaticInnerClass类也可以采用如下的定义形式(注意其中的static定义符的应用):

public staticclass StaticInnerClass implements StaticInnerClassOuterInterface{ }

接口中为什么可以允许内部类定义?该内部类有什么应用意义?

首先,在Java接口中并不是什么都不能定义,而是在接口中只能定义有static或者abstract类型的成员。而这两种类型的成员,都是不依赖于对象实例而存在的成员——因此,接口中允许定义不依赖于对象实例而存在的成员,而static静态内部类在接口中作为static成员而存在。

其次,接口的定义者可以将在接口中定义的内部类作为接口的默认实现类,从而为接口提供默认的功能实现。当然,接口也允许在应用中根据应用的需要重新加以实现以满足特定的应用需要。

这给接口的应用带来很大的灵活性——接口可以提供默认实现,也允许在应用中重新实现。它也充分符合面向对象程序设计方法中所倡导的封装性和高内聚等原则,把接口相关联的类(包括默认实现类)都内聚在接口中,使得该接口的内聚性更强。

《Java语言程序设计》考试题及参考答案

计算机等级二级Java考试辅导:“多线程”单元综合复习

计算机等级二级Java考试辅导:“类中成员”单元综合复习

计算机等级二级Java考试辅导:“类与对象”单元综合复习

计算机等级二级Java考试辅导:“异常处理”单元综合复习

相关推荐