嵌套类
Java嵌套类是指在一个类中定义另一个类。
嵌套类的目的是将嵌套类与其周围的类进行明确的分组,表明这两个类将被一起使用。或者这个嵌套类只能在它的封闭(拥有)类中使用。
Java开发人员通常将嵌套类称为内部类,但内部类(非静态嵌套类)只是几种不同类型嵌套类中的一种。
嵌套类可以认为是其封闭类的成员。因此,嵌套类可以被声明为public,package(无访问修饰符),protected和private类型的。因此,Java中的嵌套类也可以由子类继承。
你可以在Java中创建几种不同类型的嵌套类。Java嵌套类类型包括:
- 静态嵌套类
- 非静态嵌套类
- 本地嵌套类
- 匿名嵌套类
静态嵌套类
静态嵌套类可以像如下示例这样声明:
public class Outer {
public static class Nested {
}
}
为了创建一个Nested类的实例,你必须通过在Outer类的名字前加上前缀来引用它,如下所示:
Outer.Nested instance = new Outer.Nested();
在Java中,静态嵌套类本质上是一个正常的类,它嵌套在另一个类中。作为静态的,静态嵌套类只能通过对包含类实例的引用来访问封闭类的实例变量。
非静态嵌套类(内部类)
Java中的非静态嵌套类也称为内部类。内部类关联外部类的实例。因此,你必须首先创建一个封闭类的实例来创建一个内部类的实例。示例如下:
public class Outer {
public class Inner {
}
}
下面是创建了一个Inner的实例
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
请注意,为了创建内部类的实例,需要在外部类的实例后面加上new关键字。
非静态嵌套类(内部类)可以访问封闭类的字段,即使它们被声明为私有。示例如下:
public class Outer {
private String text = "I am private!";
public class Inner {
public void printText() {
System.out.println(text);
}
}
}
注意Inner类的printText()方法引用了Outer类的私有text字段。这是可以的。下面示例调用了printText()方法:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.printText();
内部类影子
如果Java内部类中声明了与其封闭类字段或方法名称相同的字段或方法,则内部字段或方法被称为在外部字段或方法上影子。示例如下:
public class Outer {
private String text = "I am Outer private!";
public class Inner {
private String text = "I am Inner private";
public void printText() {
System.out.println(text);
}
}
}
在上面的例子中,Outer和Inner类都包含一个名为text的字段。当Inner类引用text时,它指的是它自己的字段。当Outer引用text时,它也指它自己的字段。
Inner类也可以引用Outer类的text字段。此时,需要在text字段前加上Outer.this。示例如下:
public class Outer {
private String text = "I am Outer private!";
public class Inner {
private String text = "I am Inner private";
public void printText() {
System.out.println(text);
System.out.println(Outer.this.text);
}
}
}
上例中会打印出Inner.text和Outer.text字段。
本地类
Java中的本地类就像内部类(非静态嵌套类)一样,它们是在方法内或作用域块({...})内定义。下面是一个例子:
class Outer {
public void printText() {
class Local {
}
Local local = new Local();
}
}
本地类只能从定义它们的方法或作用域块中访问。
本地类可以像普通的内部类一样访问其封闭类的成员(字段和方法)。
本地类也可以访问在同一个方法或作用域块内的局部变量,只要这些变量被声明为final。
从Java 8开始本地类也可以访问声明本地类的方法的局部变量和参数。参数必须声明为final的或effectually final。effectually final意味着变量在初始化后永远不会被改变。方法参数通常就是这样的。
本地类也可以在静态方法中声明。在这种情况下,本地类只能访问封闭类的静态部分。本地类不能包含各种静态声明(允许使用常量 - 变量声明为effectually final),因为本质类本质上是非静态的,即使在静态方法中声明也是如此。
同样的影子规则适用于本地类和内部类。
匿名类
Java中的匿名类是没有类名的嵌套类。它们通常被声明为现有类的子类,或者作为某个接口的实现。
匿名类在实例化时被定义。下面的例子声明了一个匿名子类,其超类的名SuperClass:
public class SuperClass {
public void doIt() {
System.out.println("SuperClass doIt()");
}
}
SuperClass instance = new SuperClass() {
public void doIt() {
System.out.println("Anonymous class doIt()");
}
};
instance.doIt();
运行这个Java代码会打印出“Anonymous class doIt()”。匿名类的子类(扩展)SuperClass并重写doIt()方法。
Java匿名类也可以实现一个接口。示例如下:
public interface MyInterface {
public void doIt();
}
MyInterface instance = new MyInterface() {
public void doIt() {
System.out.println("Anonymous class doIt()");
}
};
instance.doIt();
正如你所看到的,实现接口的匿名类非常类似于扩展一个类的匿名类。
匿名类可以访问封闭类的成员。它也可以访问声明为final或有效的final变量(Java 8版本后)。
你可以在匿名类中声明字段和方法,但不能声明构造方法。不过,你可以给匿名类声明静态初始化程序。示例如下:
final Strint textToPrint = "Text...";
MyInterface instance = new MyInterface() {
private String text;
//静态初始程序
{ this.text = textToPrint; }
public void doIt() {
System.out.println(this.text);
}
};
instance.doIt();
匿名类同样适用影子规则。
嵌套类的好处
Java嵌套类的优点是可以将属于一起的类组合在一起。你可以把它们放在同一个包里,但是把一个类放到另一个类里面就可以实现更强大的分组。
一个嵌套的类通常只被其封闭的类使用。有时嵌套类只对封闭类可见,只在内部使用,因此在封闭类之外永远不可见。其他时候,嵌套类在其封闭类之外是可见的,但只能与封闭类一起使用。
一个例子是一个Cache类。在Cache类中,你可以声明一个CacheEntry类,它可以包含关于特定缓存条目(缓存值,插入时间,访问次数等)的信息。如果Cache类的用户不需要获取有关CacheEntry本身的信息,而只能获取缓存的值,则Cache类的用户可能永远不会看到CacheEntry类。但是,Cache类可能会选择使CacheEntry类对外部可见,因此它们可以访问的不仅仅是缓存的值(例如有关值最后刷新时间的信息等)。
以下是两个Cache实现框架:
public class Cache {
private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();
private class CacheEntry {
public long timeInserted = 0;
public object value = null;
}
public void store(String key, Object value){
CacheEntry entry = new CacheEntry();
entry.value = value;
entry.timeInserted = System.currentTimeMillis();
this.cacheMap.put(key, entry);
}
public Object get(String key) {
CacheEntry entry = this.cacheMap.get(key);
if(entry == null) return null;
return entry.value;
}
}
public class Cache {
private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();
public class CacheEntry {
public long timeInserted = 0;
public object value = null;
}
public void store(String key, Object value){
CacheEntry entry = new CacheEntry();
entry.value = value;
entry.timeInserted = System.currentTimeMillis();
this.cacheMap.put(key, entry);
}
public Object get(String key) {
CacheEntry entry = this.cacheMap.get(key);
if(entry == null) return null;
return entry.value;
}
public CacheEntry getCacheEntry(String key) {
return this.cacheMap.get(key);
}
}
第一个Cache类隐藏了内部嵌套类CacheEntry,而第二个Cache则开放了CacheEntry访问权限。
下一篇:抽象类