嵌套顶级类:
当你在其它类的外面声明一个类时,Java就认为该类是一个顶级类。如果你在一个顶级类中声明一个类,并且在该嵌套类的声明前加上static的修饰符,你就得到了一个嵌套顶级类。以下的代码段声明了一个顶级类和一个嵌套顶级类:
- 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对象。下面的代码就是一个很好的例子:
- 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);
-
}
-
}
使用嵌套顶级类,你不可以访问外部类的实例字段或者调用该类的实例方法。要访问实例成员,Java支持内部类。内部类和嵌套顶级类相似,不同的地方是你不会在内部内的声明前加上static关键字。我们以下就讨论一下内部类,先从实例内部类开始。
实例内部类(Instance inner classes):
- package intro.nestedclass;
-
-
class Job3{
-
private String jobTitle;
-
public Job3(String jobTitle){
-
this.jobTitle = jobTitle;
-
}
-
public String toString(){
-
return this.jobTitle;
-
}
-
}
-
-
class Employee3{
-
private String name;
-
private Job3[] jobs;
-
Employee3(String name, Job3[] jobs){
-
this.name = name;
-
this.jobs = jobs;
-
}
-
String getName(){
-
return name;
-
}
-
-
JobIterator getJobIterator(){
-
return new JobIterator();
-
}
-
-
class JobIterator{
-
private int jobIndex = 0;
-
-
boolean hasMoreJobs(){
-
return jobIndex < jobs.length;
-
}
-
-
Job3 nextJob(){
-
return ! hasMoreJobs() ? null : jobs[jobIndex++];
-
}
-
}
-
}
-
-
public class JobIterator3 {
-
public static void main(String [] args){
-
Job3[] jobs = { new Job3("Janitor"), new Job3("Delivery Person") };
-
Employee3 e = new Employee3("John Doe", jobs);
-
System.out.println(e.getName() + " works the following jobs:\n");
-
Employee3.JobIterator eji = e.getJobIterator();
-
while (eji.hasMoreJobs())
-
System.out.println(eji.nextJob());
-
}
-
}
本地内部类(Local inner classes):
除了以上的类嵌套外,Java还允许你在任意的代码段中放入一个类,该类的代码放在一对大括
号中({})。这意味着类可以出现在一个方法中,甚至是在if语句的一对括号中。这样的类就称为本地内部类。相对于实例内部类来说,本地内部类与有一个好处。它除了可以访问外部类的实例和类字段外(称为实例和类方法),还可以访问本地变量和方法的参数。
- package intro.nestedclass;
-
-
import java.util.Enumeration;
-
class ComputerLanguage
-
{
-
private String name;
-
ComputerLanguage(String name)
-
{
-
this.name = name;
-
}
-
-
public String toString()
-
{
-
return name;
-
}
-
}
-
-
class LocalInnerClassDemo
-
{
-
@SuppressWarnings("unchecked")
-
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());
-
}
-
-
@SuppressWarnings("unchecked")
-
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();
-
}
-
}
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的方法仍然可以引用该数组。
匿名内部类(Anonymous inner classes):
如果一个类很短,你可以声明一个没有名字的本地内部类。因为它的名字对该类没有用处,而且,当同一个类中包含有几个本地内部类时,如果不选择一个名字,可以减少名字冲突的机会。没有名字的本地内部类就是匿名类部类。由于匿名内部类没有一个名字,因此你声明的时候就要创建它:
- package intro.nestedclass;
-
-
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")
-
{
-
int count;
-
{
-
count = 10;
-
}
-
void occupation(){
-
System.out.println("Farmer "+ name+ " milks cows" + this.count);
-
}
-
}.occupation();
-
}
-
}
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()方法并且丢弃该对象的引用,以便令该对象符合垃圾收集的条件。
AnonymousInnerClassDemo1使用超类的构造器来做初始化,这样就带来一个问题:我可以在一个匿名类中声明自己的构造器吗?答案是不可以。由于一个构造器需要一个类名,而匿名类是没有名字的,所以编译器不能选择一个名字。
警告:在一个匿名类中声明构造器是非法的,这是由于构造器的名字要和所处的类名一样,而匿名内部类是没有名字的。
虽然由一个匿名内部类创建一个对象时,你不可以建立构造器,但你可以使用一个对象初始块来来执行自定义的初始化。
阅读(3618) | 评论(0) | 转发(0) |