Java 中的内部类和静态嵌套类之间的主要区别是什么?设计/实现在选择其中之一时是否起作用?
网友回答:
Java 教程说:
术语:嵌套类分为
两类:静态类和非静态
类。声明为 static 的
嵌套类简称为
静态嵌套类。非静态
嵌套类称为内部
类。
通俗地说,术语“嵌套”和“内部”被大多数程序员互换使用,但我将使用正确的术语“嵌套类”,它涵盖了内部和静态。
类可以无限嵌套,例如.class A 可以包含类 B,其中包含包含类 C 的类,其中包含类 D 等。但是,多个级别的类嵌套很少见,因为它通常是糟糕的设计。
创建嵌套类的原因有三个:
Java 中有四种嵌套类。简而言之,它们是:
让我更详细地阐述一下。
静态类是最容易理解的类型,因为它们与包含类的实例无关。
静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其命名空间的衣架,例如,在包披萨中声明为类 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,但在包含类中,可以通过其简单的名字来知道。但是,内部类的每个实例都绑定到其包含类的特定实例:上面,在 jerry 中创建的 Goat 隐式绑定到 jery 中的 Rhino 实例。否则,我们在实例化 Goat 时显式显示关联的 Rhino 实例:
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(请注意,您在奇怪的新语法中将内部类型称为Goat:Java从rhino部分推断包含类型。而且,是的,新犀牛。山羊()对我来说也更有意义。
那么这对我们有什么好处呢?好吧,内部类实例可以访问包含类实例的实例成员。这些封闭的实例成员在内部类中仅通过其简单名称引用,而不是通过 this(这在内部类中是指内部类实例,而不是关联的包含类实例):
public class Rhino {
private String barry;
public class Goat {
public void colin() {
System.out.println(barry);
}
}
}
在内部类中,你可以将包含类的这个称为 Rhino.this,你可以用它来引用它的成员,例如 Rhino.this.barry。
本地内部类是在方法主体中声明的类。此类仅在其包含方法中是已知的,因此它只能实例化并在其包含方法中访问其成员。好处是局部内部类实例绑定并可以访问其包含方法的最终局部变量。当实例使用其包含方法的最终局部时,变量将保留它在创建实例时持有的值,即使变量已超出范围(这实际上是 Java 的粗略、有限版本的闭包)。
由于本地内部类既不是类的成员,也不是包的成员,因此不会使用访问级别声明它。(但是,请注意,它自己的成员具有与普通类一样的访问级别。
如果在实例方法中声明了本地内部类,则内部类的实例化与创建实例时包含方法的 this 持有的实例相关联,因此可以像在实例内部类中一样访问包含类的实例成员。本地内部类仅通过其名称进行实例化,例如,本地内部类 Cat 被实例化为新的 Cat(),而不是新的 this。Cat() 正如您所料。
匿名内部类是编写本地内部类的一种语法上方便的方法。最常见的是,每次运行本地内部类的包含方法时,最多只实例化一次。那么,如果我们可以将本地内部类定义及其单个实例化组合成一个方便的语法形式,那就太好了,如果我们不必为类想出一个名称(代码包含的无用名称越少越好),那就太好了。匿名内部类允许以下两项操作:
new *ParentClassName*(*constructorArgs*) {*members*}
这是一个表达式,返回扩展 ParentClassName 的未命名类的新实例。您不能提供自己的构造函数;相反,一个是隐式提供的,它只是调用超级构造函数,因此提供的参数必须适合超级构造函数。(如果父级包含多个构造函数,则“最简单的”构造函数称为“最简单”构造函数,它由一组相当复杂的规则确定,不值得费心去详细学习 — 只需注意 NetBeans 或 Eclipse 告诉您的内容。
或者,您可以指定要实现的接口:
new *InterfaceName*() {*members*}
这样的声明创建一个未命名类的新实例,该实例扩展了 Object 并实现 InterfaceName。同样,您不能提供自己的构造函数;在这种情况下,Java 隐式地提供了一个无参数、无所事事的构造函数(因此在这种情况下永远不会有构造函数参数)。
即使不能为匿名内部类提供构造函数,仍可以使用初始值设定项块(放置在任何方法外部的 {} 块)进行所需的任何设置。
请注意,匿名内部类只是使用一个实例创建本地内部类的一种不太灵活的方法。如果你想要一个实现多个接口的本地内部类,或者在扩展 Object 以外的某个类时实现接口,或者指定自己的构造函数,则只能创建一个名为本地内部类的常规。
网友回答:
来自 Java 教程:
嵌套类分为两类:静态类和非静态类。声明为 static 的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。
静态嵌套类使用封闭类名进行访问:
OuterClass.StaticNestedClass
例如,若要为静态嵌套类创建对象,请使用以下语法:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
作为内部类实例的对象存在于外部类的实例中。请考虑以下类:
class OuterClass {
...
class InnerClass {
...
}
}
InnerClass 的实例只能存在于 OuterClass 的实例中,并且可以直接访问其封闭实例的方法和字段。
若要实例化内部类,必须先实例化外部类。然后,使用以下语法在外部对象中创建内部对象:
OuterClass outerObject = new 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() { ... }
网友回答:
我不认为真正的区别在上面的答案中变得很明显。
首先要正确使用术语:
到目前为止,马丁的回答是正确的。但是,实际的问题是:声明嵌套类静态的目的是什么?
如果只想将类放在一起(如果它们在主题上属于一起),或者嵌套类在封闭类中专门使用,则可以使用静态嵌套类。静态嵌套类与其他所有类之间没有语义差异。
非静态嵌套类是另一种野兽。与匿名内部类类似,此类嵌套类实际上是闭包。这意味着它们捕获其周围的范围及其封闭实例,并使其可访问。也许一个例子可以澄清这一点。请参阅容器的存根:
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
更多硬核解释如下:
如果您查看编译器为(非静态)嵌套类生成的 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 类型的附加参数来指定封闭实例。在源代码中看不到此参数,但编译器会为嵌套类隐式生成此参数。Container this$0
马丁的例子
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
将被编译为类似(以字节码为单位)的调用
new InnerClass(outerObject)
为了完整起见:
匿名类是非静态嵌套类的完美示例,它只是没有与之关联的名称,以后无法引用。
模板简介:该模板名称为【Java 中的内部类和静态嵌套类之间的主要区别是什么?设计/实现在选择其中之一时是否起作用?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Java】栏目查找您需要的精美模板。