对Java嵌套类的接头
副标题#e#
摘要:与字段和要领雷同,Java答允类是其它类的成员。在这里,我们将嵌套类分为4种–嵌套顶级类(nested top-level classes),成员内部类(instance inner classes),当地内部类(local inner classes)和匿名内部类(anonymous inner classes)。
在传授Java时,我常常发明学生实验在要领中声明其它的要领。不外,与Pascal语言差异–Pascal答允嵌套声明进程procedures(与要领雷同),而Java是不答允这样做的。对付以下的代码,实验在outerMethod()中声明innerMethod()要领,编译时将会通不外。
void outerMethod ()
{
void innerMethod ()
{
}
}
不外,由Java语言类型1.1开始,Java就支持类嵌套;Java编译器答允一个类呈此刻另一个类中。以下的代码段就展示了如安在outerClass类中嵌套innerClass类。
class outerClass
{
class innerClass
{
}
}
为什么Java支持类嵌套,尚有Java支持哪些类嵌套?以下的部门将会答复这些问题,当你读完这篇文章,你就会进修到类嵌套的相关常识,而且运用它来写出强大的Java措施。首先我们先来看一下为什么Java支持类嵌套。
留意:在宣布JDK1.1后,Sun宣布了内部类类型的文档。该文档谈到嵌套顶级类和内部类的运用。在读完这篇文章后,我强烈发起你欣赏这些文档。
为什么Java支持类嵌套?
Java并非必然要支持类嵌套。实际上,假如你看过内部类类型文档,你将会发明类嵌套的应用在那边。不外,Java支持类嵌套至少有两个长处:
.令源代码越发清晰
.淘汰定名的斗嘴
#p#副标题#e#
通过类嵌套可以令源代码变得更为清楚,因为你声明白一个包括有一些工具的类,它必需可以打点和答允类中的要领来直接会见工具的字段,以及挪用工具的要领–纵然是外部类中的私有字段和要领。要领略这个长处,可用以下的一个例子说明,在这个措施中要轮回遍历一个Employee工具中的Job工具:
Listing 1. JobIterator1.java
// JobIterator1.java
class Job
{
private String jobTitle;
Job (String jobTitle)
{
this.jobTitle = jobTitle;
}
public String toString ()
{
return jobTitle;
}
}
class Employee
{
private String name;
private Job [] jobs;
private int jobIndex = 0;
Employee (String name, Job [] jobs)
{
this.name = name;
this.jobs = jobs;
}
String getName ()
{
return name;
}
boolean hasMoreJobs ()
{
return jobIndex < jobs.length;
}
Job nextJob ()
{
return !hasMoreJobs () ? null : jobs [jobIndex++];
}
}
class JobIterator1
{
public static void main (String [] args)
{
Job [] jobs = { new Job ("Janitor"), new Job ("Delivery Person") };
Employee e = new Employee ("John Doe", jobs);
System.out.println (e.getName () + " works the following jobs:\n");
while (e.hasMoreJobs ())
System.out.println (e.nextJob ());
}
}
运行后, JobIterator1发生如下的输出:
John Doe works the following jobs:
Janitor
Delivery Person
JobIterator1包括有Job, Employee和JobIterator1类。Job封装了一个job的title,而Employee封装了一个员工的名字以及该员工所做事情的一个数组。JobIterator1包括有一个main()的要领用来建设Job和Employee工具,而且打印出该员工的名字和事情。
Employee类中包括有要领hasMoreJobs()和nextJob()。这两个要领组成了一个轮回。当一个Employee工具初始化时,在私有jobs数组中的一个内部索引被配置为0。假如该索引的值比jobs数组的长度少,hasMoreJobs()要领返回一个布尔值true。nextJob()利用该索引值由数组中返回一个Job工具–而且增加该索引的值,以便下一次挪用nextJob()时返回下一个job工具的一个引用。
JobIterator1类在设计上有一些问题。首先,在轮回竣事后你不能从头启动一个轮回。虽然,你可以很容易地办理这个问题,只要Employee类插手一个reset()要领就可以了,该要领将jobIndex配置为0。第二个问题越发严重,你不能为一个Employee建设多个轮回。这是由于hasMoreJobs()和nextJob()要领已经写死在Employee中了。要办理这两个问题,开拓者可以声明一个轮回类,它的工具可以轮回jobs数组。在轮回竣事后,措施可以通过建设一个新的轮回工具来启动一个新的轮回。同样,通过建设多个轮回工具,一个措施可以在同一个Employee工具的jobs数组上执行多个轮回。列表2的措施为我们展示了一个名字为JobIterator的轮回类:
Listing 2. JobIterator2.java
#p#分页标题#e#
// JobIterator2.java
class Job
{
private String jobTitle;
Job (String jobTitle)
{
this.jobTitle = jobTitle;
}
public String toString ()
{
return jobTitle;
}
}
class Employee
{
private String name;
private Job [] jobs;
Employee (String name, Job [] jobs)
{
this.name = name;
this.jobs = jobs;
}
String getName ()
{
return name;
}
JobIterator getJobIterator ()
{
return new JobIterator (jobs);
}
}
class JobIterator
{
private Job [] jobs;
private int jobIndex = 0;
JobIterator (Job [] jobs)
{
this.jobs = jobs;
}
boolean hasMoreJobs ()
{
return jobIndex < jobs.length;
}
Job nextJob ()
{
return !hasMoreJobs () ? null : jobs [jobIndex++];
}
}
class JobIterator2
{
public static void main (String [] args)
{
Job [] jobs = { new Job ("Janitor"), new Job ("Delivery Person") };
Employee e = new Employee ("John Doe", jobs);
System.out.println (e.getName () + " works the following jobs:\n");
JobIterator ji = e.getJobIterator ();
while (ji.hasMoreJobs ())
System.out.println (ji.nextJob ());
}
}
JobIterator2的输出和JobIterator1一样,但有所差异的是,JobIterator2将轮回的代码由Employee转移到JobIterator。尚有,Employee声明白一个getJobIterator()的要领,可返回一个新的JobIterator工具引用。要留意到JobIterator和 Employee是紧耦合的类:JobIterator的结构器需要一个Employee私有jobs数组的引用。我们要记下这个耦合干系,因为它为领略类嵌套内部的事情提供了一个线索。
固然JobIterator2很利便地办理了JobIterator1的问题,但这个新的措施又引入了一个新问题:由于新插手了一个和Employee类处在同一级的JobIterator类,这样未来就不能在同一级的源文件中插手一个普遍的JobIterator接口。因为在源文件的同一级中,你差异拥有同样名字的两个类/接口。固然在我们的例子中这不是一个严重的问题,可是在重要的措施中,有些环境下必需在同一个源文件中利用沟通名字的类/接口。要令那些名字共存,你必需认识到一些类完全依赖其它的类。你应该在其依赖的类中声明这些类。列表3展示了如安在一个Employee类中声明一个JobIterator类–JobIterator依赖Employee类。
Listing 3. JobIterator3.java
// JobIterator3.java
class Job
{
private String jobTitle;
Job (String jobTitle)
{
this.jobTitle = jobTitle;
}
public String toString ()
{
return jobTitle;
}
}
class Employee
{
private String name;
private Job [] jobs;
Employee (String name, Job [] jobs)
{
this.name = name;
this.jobs = jobs;
}
String getName ()
{
return name;
}
JobIterator getJobIterator ()
{
return new JobIterator ();
}
class JobIterator
{
private int jobIndex = 0;
public boolean hasMoreJobs ()
{
return jobIndex < jobs.length;
}
public Object nextJob ()
{
return !hasMoreJobs () ? null : jobs [jobIndex++];
}
}
}
class JobIterator3
{
public static void main (String [] args)
{
Job [] jobs = { new Job ("Janitor"), new Job ("Delivery Person") };
Employee e = new Employee ("John Doe", jobs);
System.out.println (e.getName () + " works the following jobs:\n");
Employee.JobIterator eji = e.getJobIterator ();
while (eji.hasMoreJobs ())
System.out.println (eji.nextJob ());
}
}
JobIterator3的输出和JobIterator1、JobIterator2的一样,它利用了类嵌套:Employee类包括有JobIterator类的声明。这样做的功效是,JobIterator不再需要一个结构器,因为JobIterator可直接会见Employee的私有字段job。尚有,在JobIterator3的main()要领中,假如需要会见JobIterator,你都要插手一个Employee前缀。由于JobIterator3的JobIterator类不再需要一个结构器和本身的jobs字段,因此源代码要比JobIterator2的清楚一些。
除了令源代码越发清楚之外,类嵌套尚有第二个长处。这就是名字的斗嘴淘汰了。在列表3的措施中,顶级的类是Job, Employee和JobIterator3。尚有一个Employee.JobIterator类。假如我们在与Employee的同一级中插入一个JobIterator接口,我们就会有Job, Employee, JobIterator3和Employee.JobIterator类,以及一个JobIterator接口。由于Employee.JobIterator和JobIterator暗示两个差异的名字,因此没有名字斗嘴。
留意:
#p#分页标题#e#
假如你编译JobIterator3而且查察获得的class文件,你将会发明有一个名字为Employee$JobIterator.class的文件。该文件包括有嵌套在Employee中的JobIterator类的字节码。要留意个中的“$”字符。对付嵌套类,Java的编译器城市发生一个雷同的class名,它以外面的类名和“$”开头,接着是嵌套类的名字。为什么利用$标记呢?这是因为对付操纵系统平台来说,这是一个正当的字符。而“.”凡是是用来断绝文件的扩展名字的,凡是都不行在文件名中利用。
Java支持哪些嵌套类?
Java将嵌套类分成两个主要的分类:嵌套顶级类(nested top-level classes)和内部类(inner classes)。Java还进一步将内部类分为实例内部类(instance inner class) ,当地内部类(local inner class)和匿名内部类(and anonymous inner class)。要把握嵌套类,你需要分明每个分类。以下我们就来接头以下这些分类,首先我们来看一下嵌套顶级类。
嵌套顶级类
当你在其它类的外面声明一个类时,Java就认为该类是一个顶级类。假如你在一个顶级类中声明一个类,而且在该嵌套类的声明前加上static的修饰符,你就获得了一个嵌套顶级类。以下的代码段声明白一个顶级类和一个嵌套顶级类:
class TopLevelClass
{
static class NestedTopLevelClass
{
}
}
就象静态的字段和静态的要领一样(也可称为类字段和类要领),一个嵌套的顶级类也是与工具无关的。思量以下的代码段:
class TopLevelClass
{
static int staticField;
int instanceField;
static class NestedTopLevelClass
{
static
{
System.out.println ("Can access staticField " + staticField);
// System.out.println ("Cannot access instanceField " + instanceField);
}
{
System.out.println ("Can access staticField " + staticField);
// System.out.println ("Cannot access instanceField " + instanceField);
}
}
}
在上面NestedTopLevelClass类的类初始化和工具初始化代码块中,你可以会见TopLevelClass的staticField变量。不外你在这两个代码块中都不能会见instanceField变量。因为NestedTopLevelClass和任何的TopLevelClass工具都是无关的,所以NestedTopLevelClass 不能会见TopLevelClass的instanceField变量。
告诫:一个嵌套顶级类不能会见任何外部类的实例成员(包罗字段和要领)。
固然NestedTopLevelClass不能会见TopLevelClass的实例字段,可是static的修饰符并不会故障NestedTopLevelClass声明本身的实例字段,以及建设NestedTopLevelClass工具。列表4的代码就是一个很好的例子:
Listing 4. NestedTopLevelClassDemo.java
// NestedTopLevelClassDemo.java
class TopLevelClass
{
static class NestedTopLevelClass
{
int myInstanceField;
NestedTopLevelClass (int i)
{
myInstanceField = i;
}
}
}
class NestedTopLevelClassDemo
{
public static void main (String [] args)
{
TopLevelClass.NestedTopLevelClass ntlc;
ntlc = new TopLevelClass.NestedTopLevelClass (5);
System.out.println (ntlc.myInstanceField);
}
}
在运行后,NestedTopLevelClassDemo发生以下的输出:
5
NestedTopLevelClassDemo的main()要领建设了一个NestedTopLevelClass变量–ntlc。声明该变量的语法和列表3的一样(Employee.JobIterator eji = e.getJobIterator ())。 凡是,当你需要一个嵌套类的变量时,要在该嵌套类名的前面插手所有外部类的名字,而且用“.”将它们分隔。用new要害字声明该嵌套类的时候也要这样做。
这时,你大概想知道你是否可以在一个嵌套顶级类中声明一个顶级嵌套类。尚有,假如两个差异的外部类声明白同样的字段变量名,但该变量的范例可能初始化值是差异的?要解答这些问题,可看列表5:
Listing 5. NestingAndShadowingDemo.java
// NestingAndShadowingDemo.java
class TopLevelClass
{
private static int a = 1;
private static int b = 3;
static class NestedTopLevelClass
{
private static int a = 2;
static class NestedNestedTopLevelClass
{
void printFields ()
{
System.out.println ("a = " + a);
System.out.println ("b = " + b);
}
}
}
}
class NestingAndShadowingDemo
{
public static void main (String [] args)
{
TopLevelClass.NestedTopLevelClass.NestedNestedTopLevelClass nntlc;
nntlc = new TopLevelClass.NestedTopLevelClass.
NestedNestedTopLevelClass ();
nntlc.printFields ();
}
}
在运行时,NestingAndShadowingDemo发生以下的输出:
a = 2
b = 3
#p#分页标题#e#
NestingAndShadowingDemo可以编译运行证明你可以在顶级嵌套类中嵌套顶级嵌套类。功效也显示了NestedTopLevelClass的一个字段屏蔽了TopLevelClass的字段。功效是打印出NestedTopLevelClass的字段内容。
利用嵌套顶级类,你不行以会见外部类的实例字段可能挪用该类的实例要领。要会见实例成员,Java支持内部类。内部类和嵌套顶级类相似,差异的处所是你不会在内部内的声明前加上static要害字。我们以下就接头一下内部类,先从实例内部类开始。
提示:你可以在顶级嵌套类前加上private,protected可能public要害字,以指示该类在外部类以外的会见级别。
实例内部类(Instance inner classes)
假设你在声明嵌套类时,没有在前面加上static要害字。这样你获得的不是一个嵌套顶级类,你获得的是一个实例内部类。嵌套顶级类只可以会见外部内的static成员,而实例内部类可以同时会见static和实例成员。列表3的代码中有实例内部内的例子。在Employee类中,你可以看到实例内部类JobIterator。仔细调查这两个类,你可以看到JobIterator可以会见Employee的private jobs实例字段。
提示:你可以在实例内部类前加上private,protected可能public要害字,以指示该类在外部类以外的会见级别。
当地内部类(Local inner classes)
除了以上的类嵌套外,Java还答允你在任意的代码段中放入一个类,该类的代码放在一对大括号中({ })。这意味着类可以呈此刻一个要领中,甚至是在if语句的一对括号中。这样的类就称为当地内部类。
相对付实例内部类来说,当地内部类与有一个长处。它除了可以会见外部类的实例和类字段外(称为实例和类要领),还可以会见当地变量和要领的参数。列表6就是一个当地内部类:
Listing 6. LocalInnerClassDemo.java
// LocalInnerClassDemo.java
import java.util.*;
class ComputerLanguage
{
private String name;
ComputerLanguage (String name)
{
this.name = name;
}
public String toString ()
{
return name;
}
}
class LocalInnerClassDemo
{
public static void main (String [] args)
{
ComputerLanguage [] cl =
{
new ComputerLanguage ("Ada"),
new ComputerLanguage ("Algol"),
new ComputerLanguage ("APL"),
new ComputerLanguage ("Assembly - IBM 360"),
new ComputerLanguage ("Assembly - Intel"),
new ComputerLanguage ("Assembly - Mostek"),
new ComputerLanguage ("Assembly - Motorola"),
new ComputerLanguage ("Assembly - VAX"),
new ComputerLanguage ("Assembly - Zilog"),
new ComputerLanguage ("BASIC"),
new ComputerLanguage ("C"),
new ComputerLanguage ("C++"),
new ComputerLanguage ("Cobol"),
new ComputerLanguage ("Forth"),
new ComputerLanguage ("Fortran"),
new ComputerLanguage ("Java"),
new ComputerLanguage ("LISP"),
new ComputerLanguage ("Logo"),
new ComputerLanguage ("Modula 2"),
new ComputerLanguage ("Pascal"),
new ComputerLanguage ("Perl"),
new ComputerLanguage ("Prolog"),
new ComputerLanguage ("Snobol")
};
Enumeration e = enumerator ((Object []) cl);
while (e.hasMoreElements ())
System.out.println (e.nextElement ());
}
static Enumeration enumerator (final Object [] array)
{
class LocalInnerClass implements Enumeration
{
private int index = 0;
public boolean hasMoreElements ()
{
return index < array.length;
}
public Object nextElement ()
{
return array [index++].toString ();
}
}
return new LocalInnerClass ();
}
}
在运行后, LocalInnerClassDemo发生以下的输出:
Ada
Algol
APL
Assembly – IBM 360
Assembly – Intel
Assembly – Mostek
Assembly – Motorola
Assembly – VAX
Assembly – Zilog
BASIC
C
C++
Cobol
Forth
Fortran
Java
LISP
Logo
Modula 2
Pascal
Perl
Prolog
Snobol
#p#分页标题#e#
LocalInnerClassDemo展示了在LocalInnerClassDemo的enumerator()类要领中声明当地内部类LocalInnerClass。要留意到enumerator()返回一个工具的引用,该工具的类实现了Enumeration接口(在java.util包中)。由于LocalInnerClass也实现了Enumeration,所以enumerator() 正当地返回一个到LocalInnerClass工具的引用。
enumerator()的参数列表中是一个单维数组object[],尚有一个要害字final。final要害字的浸染是什么?假如一个当地内部类诡计会见一个当地变量可能是该类所处要领的一个参数,这些当地变量/参数必需是final的。两个差异的工具不能共享会见同样的非final变量可能参数,不然在设计上就会带来巨大的同步问题。利用final的当地/参数变量就没有这些坚苦。
留意:假如当地变量可能参数中有原始范例的变量,而不是引用的范例,编译器就用一个常数取代该变量。
在enumerator() 返回后,LocalInnerClassDemo可以会见该数组吗?对付一个参数来说(final可能其它的),会否在一个要领退出后消失呢?是的,该参数消失了;不外,假如你查察一下相应class文件的字节代码,你将会发明编译器发生了一些代码。首先,编译器在LocalInnerClass中建设了一个LocalInnerClass(Object val$array)结构器。第二,编译器建设了一个隐含的工具val$array;该字段在LocalInnerClass中。该字段被称为是一个synthetic字段 ,因为它是由编译器建设的。最后,该编译器返回new LocalInnerClass ();在enumerator()中,返回new LocalInnerClass (array);由于LocalInnerClass拥有指向同一个Object []数组(也就是传送到enumerator()的数组)的引用拷贝。所以在enumerator()的数组参数消失后,LocalInnerClass的要领仍然可以引用该数组。
提示:要知道更多的编译器是如何打点顶级类中当地内部类,可以进修以下内部类类型的文档,也可以通过javap措施来反解class文件。
匿名内部类(Anonymous inner classes)
假如一个类很短,你可以声明一个没有名字的当地内部类。因为它的名字对该类没有用处,并且,当同一个类中包括有几个当地内部类时,假如不选择一个名字,可以淘汰名字斗嘴的时机。没有名字的当地内部类就是匿名类部类。
由于匿名内部类没有一个名字,因此你声明的时候就要建设它,如列表7所示:
Listing 7. AnonymousInnerClassDemo1.java
// AnonymousInnerClassDemo1.java
abstract class Farmer
{
protected String name;
Farmer (String name)
{
this.name = name;
}
abstract void occupation ();
}
class BeefFarmer extends Farmer
{
BeefFarmer (String name)
{
super (name);
}
void occupation ()
{
System.out.println ("Farmer " + name + " raises beef cattle");
}
}
class AnonymousInnerClassDemo1
{
public static void main (String [] args)
{
BeefFarmer bf = new BeefFarmer ("John Doe");
bf.occupation ();
new Farmer ("Jane Doe")
{
void occupation ()
{
System.out.println ("Farmer " + name + " milks cows");
}
}.occupation ();
}
}
在运行时,AnonymousInnerClassDemo1发生以下的输出:
Farmer John Doe raises beef cattle
Farmer Jane Doe milks cows
AnonymousInnerClassDemo1声明白一个抽象的Farmer类,内里封装了一个farmer的名字和职业—通过一个抽象的occupation()要领。BeefFarmer类扩展Farmer,而且包围了occupation()来标识beef farmer的事情。该类呈此刻AnonymousInnerClassDemo1的main()要领中,它建设了一个BeefFarmer工具而且挪用它的occupation()要领来打印出该beef farmer的事情。这里并没有什么奇怪的处所!不外当你继承查察main()要领时,你会看到一些奇怪的对象:new Farmer ("Jane Doe") { … }。它看来要由抽象的Farmer类中建设一个工具,这看来是一件不行能的事。不外在("Jane Doe") 下面的大括号又是什么意思呢?看来这不是正当的Java代码?实际上,这些代码是正当的,它的寄义是:让JVM由Farmer匿名子类中建设一个工具。该子类包围了Farmer的occupation()要领,而且挪用Farmer (String name)的结构器来初始化Farmer的protected字段name。在建设该工具后,挪用它的occupation()要领而且扬弃该工具的引用,以便令该工具切合垃圾收集的条件。
#p#分页标题#e#
AnonymousInnerClassDemo1利用超类的结构器来做初始化,这样就带来一个问题:我可以在一个匿名类中声明本身的结构器吗?谜底是不行以。由于一个结构器需要一个类名,而匿名类是没有名字的,所以编译器不能选择一个名字。
告诫:在一个匿名类中声明结构器是犯科的,这是由于结构器的名字要和所处的类名一样,而匿名内部类是没有名字的。
固然由一个匿名内部类建设一个工具时,你不行以成立结构器,但你可以利用一个工具初始块来来执行自界说的初始化。譬喻,假如你想定制AnonymousInnerClassDemo匿名子类Farmer中的Farmer Jane Doe milks cows信息。你想将要挤的奶牛数目通过呼吁行通报已往,而且令该数值呈此刻信息中。由于一个工具初始块是在工具建设时执行的,你只要在工具初始化中执行相应的呼吁行参数初始化就可以了,如列表8所示:
Listing 8. AnonymousInnerClassDemo2.java
// AnonymousInnerClassDemo2.java
abstract class Farmer
{
protected String name;
Farmer (String name)
{
this.name = name;
}
abstract void occupation ();
}
class BeefFarmer extends Farmer
{
BeefFarmer (String name)
{
super (name);
}
void occupation ()
{
System.out.println ("Farmer " + name + " raises beef cattle");
}
}
class AnonymousInnerClassDemo2
{
public static void main (final String [] args)
{
BeefFarmer bf = new BeefFarmer ("John Doe");
bf.occupation ();
new Farmer ("Jane Doe")
{
private String count;
{
if (args.length == 1)
count = args [0];
}
void occupation ()
{
if (count == null)
System.out.println ("Farmer " + name + " milks cows");
else
System.out.println ("Farmer " + name + " milks " +
count + " cows");
}
}.occupation ();
}
}
假设你在呼吁行中输入java AnonymousInnerClassDemo2 10,你就会获得以下的输出:
Farmer John Doe raises beef cattle
Farmer Jane Doe milks 10 cows
AnonymousInnerClassDemo2仍然通过挪用结构器Farmer (String name)来初始化所得工具的Farmer层。不外,所得工具的匿名层也有一个时机初始化,这就是通过工具初始代码块。
留意:固然一个匿名内部类没有名字,编译器仍然需要为该class文件发生一个名字。编译器选择的是一个整数,该数字放在外部类和$标记的后头,作为匿名内部类的名字。譬喻,在AnonymousInnerClassDemo2中,编译器为该匿名内部类发生的class文件名是AnonymousInnerClassDemo2$1.class。
在竣事这部门前,这里提及一下匿名内部类的实际应用:开拓者常常利用匿名内部类来简化事件处理惩罚–即重要事件的告示,譬喻移动鼠标可能按一个按钮–在措施中会发生和显示一个图形用户界面(GUIs)。利用匿名内部类作事件处理惩罚是很利便的,因为这些处理惩罚凡是都不需要一个类名。列表9就是这样一个例子,这里的匿名内部类简化了windows-closing的事件处理惩罚。
Listing 9. AnonymousInnerClassDemo3.java
// AnonymousInnerClassDemo3.java
import java.awt.*;
import java.awt.event.*;
class AnonymousInnerClassDemo3
{
public static void main (String [] args)
{
// Create a rectangular frame window with a title bar at the top.
Frame f = new Frame ("Anonymous Inner Class Demo #3");
// Add a window listener that will generate a window closing event
// in response to user attempts to click the little X button (on
// Windows platforms) to the right of the title bar. When the user
// clicks that button, the window closing event results in a call
// to a method named windowClosing(). By calling System.exit (0);
// from within that method, the application exits.
f.addWindowListener (new WindowAdapter ()
{
public void windowClosing (WindowEvent e)
{
System.exit (0);
}
});
// Establish the frame window's size as 300 horizontal pixels by
// 100 vertical pixels.
f.setSize (300, 100);
// Display the frame window and get the underlying event handling
// system running.
f.setVisible (true);
}
}
在运行时,AnonymousInnerClassDemo3显示了一个矩形的窗口,即框架窗口,它是所得GUI的主要窗口。在框架窗口呈现后,用户可以移除该窗口而且遏制AnonymousInnerClassDemo3的执行。这凡是在用户点击窗口右上角的X按钮时产生。
#p#分页标题#e#
当用户按X时,底层的Java windowing东西包建设了一个事件工具,而且挪用一个出格的要领–windowClosing (WindowEvent e),e包括有该事件工具的一个引用–该要领放在一个被称为frame窗口的window监听者(window listener)工具中。该监听者工具通过挪用Frame的addWindowListener (WindowListener wl)要领注册到底层的东西包中(因此东西包可以知道要领的位置)。
WindowListener 接口声明白几个要领,每个要领都用作一个大概的window事件。开拓者不必实现全部这些要领,因为Java的windowing东西包的设计者成立了一个WindowAdapter类,它实现了全部的WindowListener要领。不外都是空的要领体,开拓者可以声明一个匿名内部类来扩展WindowAdapter,而且实现个中的一个可能多个要领。如上所示,我选择包围windowClosing (WindowEvent e)要领,而且在个中挪用System.exit (0);这样可以通过windowing东西包挪用windowClosing (WindowEvent e),就可以终止一个措施(同时封锁窗口)。
留意:在为你的GUI开拓事件处理惩罚时,你将会常常用到匿名内部类。在今后的文章中,我将会提供很多由匿名内部类来举办事件处理惩罚的例子。
总结
这篇文章先容了类嵌套对付Java语言是很有用的。类嵌套不单令源代码变得清晰–因为你可以在类要打点的工具四周声明类–它同样可以淘汰一个源文件中,在同一级中声明类的名字的斗嘴时机。
有4种差异的嵌套类:nested top-level classes(嵌套顶级类), instance inner classes(实例内部类), local inner classes(当地内部类)和anonymous inner classes(匿名内部类)。嵌套顶级类只可以会见外部类的类级字段和挪用类级的要领,而实例内部类可以会见外部类的类级和实例字段,而且挪用一个外部类的类级可能实例要领。嵌套顶级类和实例内部类只可以呈此刻其它类的界说中,Java还提供了当地内部类,可以呈此刻任意的代码块中–包罗一个要领块可能一个if语句的代码块。由于一些当地内部类很短,因此可以不利用名字,Java提供了匿名内部类。当地和匿名内部类可以会见外部代码块中当地的变量和参数,但要留意的是这些当地变量和参数必需是标识为final的。