Java嵌套类
在Java中,嵌套类是在另一个类中定义的类。
嵌套类的目的是将嵌套类与其周围的类明确组合在一起,表示这两个类将一起使用。或者,也许只能在其封闭的(拥有的)类内部使用嵌套的类。
Java开发人员通常将嵌套类称为内部类,但是内部类(非静态嵌套类)只是Java中几种不同类型的嵌套类中的一种。
在Java中,嵌套类被视为其封闭类的成员。因此,嵌套类可以声明为" public"," package"(无访问修饰符)," protected"和" private"(更多信息,请参见访问修饰符)。因此,Java嵌套类也可以由子类继承,如我有关Java继承的教程中所述。
我们可以在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 { } }
这是创建"内部"类的实例的方法:
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); } } }
注意内部类的printText()方法如何引用外部类的私有文本字段。这是完全可能的。这是我们调用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); } } }
在上面的示例中,"外部"类和"内部"类都包含一个名为" text"的字段。当"内部"类引用"文本"时,它引用其自己的字段。当"外部"指代"文本"时,它也指代其自身的字段。
但是,Java使"内部"类可以引用"外部"类的"文本"字段成为可能。为此,它必须在text
字段引用前加上Outer.this.
(外部类名+.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.printText()方法将同时打印Inner.text和Outer.text字段。
本地类
Java中的本地类就像内部类(非静态嵌套类),它们在方法内部定义或者方法内部的作用域块(" {...}")。这是一个例子:
class Outer { public void printText() { class Local { } Local local = new Local(); } }
只能从定义它们的方法或者作用域块内部访问它们。
本地类可以访问其封闭类的成员(字段和方法),就像常规内部类一样。
局部类还可以访问同一方法或者作用域块中的局部变量,前提是这些变量被声明为" final"。
从Java 8开始,局部类还可以访问局部变量和声明了局部类的方法的参数。必须将参数声明为" final"或者实际上是final。实际上,final表示变量在初始化后再也不会更改。方法参数通常实际上是最终的。
局部类也可以在静态方法中声明。在这种情况下,本地类只能访问封闭类的静态部分。局部类不能包含所有类型的静态声明(允许将常量声明为" static final"),因为即使在静态方法中声明了局部类,其本质上也是非静态的。
相同的阴影规则适用于本地类,也适用于内部类。
匿名类
Java中的匿名类是没有类名的嵌套类。通常将它们声明为现有类的子类或者某些接口的实现。
匿名类在实例化时定义。这是一个声明超类(称为"超类")的匿名子类的示例:
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代码将导致将"匿名类doIt()"打印到" System.out"中。匿名类继承(扩展)了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; //static initializer { this.text = textToPrint; } public void doIt() { System.out.println(this.text); } }; instance.doIt();
相同的屏蔽规则适用于匿名类和内部类。
嵌套类的优点
Java嵌套类的好处是我们可以将属于同一类的类组合在一起。我们可以通过将它们放在同一个程序包中来完成此操作,但是将一个类放在另一个程序包中则可以进行更强的分组。
嵌套类通常仅由其封闭类使用或者与其一起使用。有时,嵌套类仅对封闭类可见,仅在内部使用,因此在封闭类之外永远不可见。有时,嵌套类在其封闭类之外是可见的,但只能与封闭类一起使用。
一个例子是" Cache"类。在Cache类中,我们可以声明CacheEntry类,其中可以包含有关特定缓存条目的信息(缓存值,插入时间,访问次数等)。如果Cache类的用户不需要获取有关CacheEntry本身的信息,而只需要获取缓存的值,则可能永远不会看到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"类公开它。