从Java 教程 :
嵌套类分为两类:静态和非静态。声明为 static 的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。
使用封闭的类名访问静态嵌套类:
OuterClass.StaticNestedClass
例如,要为静态嵌套类创建对象,请使用以下语法:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
作为内部类的实例的对象存在于外部类的实例中。考虑以下类:
class OuterClass {
...
class InnerClass {
...
}
}
InnerClass 的实例只能存在于 OuterClass 的实例中,并且可以直接访问其封闭实例的方法和字段。
要实例化内部类,必须首先实例化外部类。然后,使用以下语法在外部对象中创建内部对象:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
请参阅: Java 教程 - 嵌套类
class A {
int t() { return 1; }
static A a = new A() { int t() { return 2; } };
}
这里, new A() { ... }
是在静态上下文中定义的内部类,并且没有封闭的实例。
Java 教程说 :
术语:嵌套类分为两类:静态和非静态。声明为 static 的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。
一般来说,术语 “嵌套” 和 “内部” 可以被大多数程序员互换使用,但我会使用正确的术语“嵌套类”,它涵盖了内部和静态。
类可以无限制地嵌套,例如,类 A 可以包含类 B,其包含类 C,其包含类 D 等。然而,多于一级的类嵌套是罕见的,因为它通常是糟糕的设计。
您可以创建嵌套类有三个原因:
Java 中有四种嵌套类 。简而言之,它们是:
让我详细说明一下。
静态类是最容易理解的类,因为它们与包含类的实例无关。
静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其命名空间的挂钩, 例如 ,在包披萨中声明为类Rhino的静态成员的类Goat被称为pizza.Rhino.Goat 。
package pizza;
public class Rhino {
...
public static class Goat {
...
}
}
坦率地说,静态类是一个非常没用的功能,因为类已经被包分成了命名空间。创建静态类的唯一真正可能的原因是这样的类可以访问其包含类的私有静态成员,但我发现这是静态类特性存在的相当蹩脚的理由。
内部类是声明为另一个类的非静态成员的类:
package pizza;
public class Rhino {
public class Goat {
...
}
private void jerry() {
Goat g = new Goat();
}
}
与静态类一样,内部类被称为包含类名称pizza.Rhino.Goat 的限定类,但在包含类中,它可以通过其简单名称来识别 。然而,一个内部类的每一个实例被绑定到其包含类的特定实例:以上, 山羊在杰里创建,被隐式地绑定到犀牛实例这 杰里 。否则,我们在实例化Goat时使关联的Rhino实例显式化:
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(注意你用奇怪的新语法将内部类型称为Goat :Java 从rhino部分推断出包含类型。而且, 新的 rhino.Goat()对我来说也更有意义。)
那么这对我们有什么影响呢?好吧,内部类实例可以访问包含类实例的实例成员。这些封闭的实例成员仅通过它们的简单名称引用到内部类中,而不是通过 它 ( 这在内部类中引用内部类实例,而不是关联的包含类实例):
public class Rhino {
private String barry;
public class Goat {
public void colin() {
System.out.println(barry);
}
}
}
在内部类中,您可以将包含类的this引用为Rhino.this ,您可以使用它来引用其成员, 例如 Rhino.this.barry 。
本地内部类是在方法体中声明的类。这样的类只在其包含方法中是已知的,因此它只能被实例化并且在其包含方法中访问其成员。获得的是本地内部类实例绑定并可以访问其包含方法的最终局部变量。当实例使用其包含方法的最终局部时,该变量将保留实例创建时保留的值,即使该变量已超出范围(这实际上是 Java 的粗略,有限版本的闭包)。
由于本地内部类既不是类或包的成员,也不会使用访问级别声明它。 (但要明确的是,它自己的成员具有像普通类一样的访问级别。)
如果局部内部类是一个实例方法声明,内部类的一个实例是依赖于在实例的创建时由含有方法的这一举行的实例,所以包含类的实例成员都像一个实例访问内心阶级。本地内部类只是通过其名称实例化, 例如本地内部类Cat被实例化为新的 Cat() ,而不是你想象的新的 this.Cat()。
匿名内部类是编写本地内部类的一种语法上方便的方法。最常见的是,每次运行包含方法时,本地内部类最多只被实例化一次。那么,如果我们可以将本地内部类定义和它的单个实例化组合成一种方便的语法形式,那将是很好的,如果我们不必为该类想出一个名称也会很好(无用的更少)你的代码包含的名称越多越好)。匿名内部类允许这两件事:
new *ParentClassName*(*constructorArgs*) {*members*}
这是一个返回未命名类的新实例的表达式,该实例扩展了ParentClassName 。你不能提供自己的构造函数; 相反,一个是隐式提供的,只是调用超级构造函数,因此提供的参数必须适合超级构造函数。 (如果父级包含多个构造函数,则称为 “最简单” 的构造函数,“最简单”,由一组相当复杂的规则决定,不值得详细学习 - 只需注意 NetBeans 或 Eclipse 告诉您的内容。)
或者,您可以指定要实现的接口:
new *InterfaceName*() {*members*}
这样的声明创建了一个未命名类的新实例,它扩展了 Object 并实现了InterfaceName 。同样,你不能提供自己的构造函数; 在这种情况下,Java 隐式地提供了一个 no-arg,do-nothing 构造函数(因此在这种情况下永远不会有构造函数参数)。
即使你不能给匿名内部类一个构造函数,你仍然可以使用初始化块(在任何方法之外放置一个 {} 块)进行任何你想要的设置。
要明确的是,匿名内部类只是一种使用一个实例创建本地内部类的灵活性较低的方法。如果你想要一个实现多个接口的本地内部类,或者在扩展某个类而不是Object或者指定自己的构造函数的情况下实现接口,那么你就会陷入创建常规命名的本地内部类的困境。
我不认为上述答案中真正的区别变得清晰。
首先要使条款正确:
到目前为止,马丁的答案是正确的。但是,实际问题是:声明嵌套类静态的目的是什么?
如果您只想将类保持在一起,或者如果嵌套类专门用于封闭类,则可以使用静态嵌套类。静态嵌套类与每个其他类之间没有语义差异。
非静态嵌套类是一种不同的野兽。与匿名内部类相似,这种嵌套类实际上是闭包。这意味着他们捕获周围的范围及其封闭的实例,并使其可访问。或许一个例子可以澄清这一点。看到这个容器的存根:
public class Container {
public class Item{
Object data;
public Container getContainer(){
return Container.this;
}
public Item(Object data) {
super();
this.data = data;
}
}
public static Item create(Object data){
// does not compile since no instance of Container is available
return new Item(data);
}
public Item createSubItem(Object data){
// compiles, since 'this' Container is available
return new Item(data);
}
}
在这种情况下,您希望从子项目到父容器的引用。使用非静态嵌套类,这可以在没有一些工作的情况下工作。您可以使用语法Container.this
访问 Container 的封闭实例。
更多核心解释如下:
如果查看编译器为(非静态)嵌套类生成的 Java 字节码,它可能会变得更加清晰:
// class version 49.0 (49)
// access flags 33
public class Container$Item {
// compiled from: Container.java
// access flags 1
public INNERCLASS Container$Item Container Item
// access flags 0
Object data
// access flags 4112
final Container this$0
// access flags 1
public getContainer() : Container
L0
LINENUMBER 7 L0
ALOAD 0: this
GETFIELD Container$Item.this$0 : Container
ARETURN
L1
LOCALVARIABLE this Container$Item L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 1
public <init>(Container,Object) : void
L0
LINENUMBER 12 L0
ALOAD 0: this
ALOAD 1
PUTFIELD Container$Item.this$0 : Container
L1
LINENUMBER 10 L1
ALOAD 0: this
INVOKESPECIAL Object.<init>() : void
L2
LINENUMBER 11 L2
ALOAD 0: this
ALOAD 2: data
PUTFIELD Container$Item.data : Object
RETURN
L3
LOCALVARIABLE this Container$Item L0 L3 0
LOCALVARIABLE data Object L0 L3 2
MAXSTACK = 2
MAXLOCALS = 3
}
如您所见,编译器创建一个隐藏字段Container this$0
。这是在构造函数中设置的,该构造函数具有 Container 类型的附加参数以指定封闭实例。您无法在源中看到此参数,但编译器会为嵌套类隐式生成它。
马丁的例子
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
会被编译成类似的调用(在字节码中)
new InnerClass(outerObject)
为了完整起见:
匿名类是刚刚没有与之关联的名称,并且不能在以后引用非静态嵌套类的一个很好的例子。