Java内部类和静态嵌套类
Posted
技术标签:
【中文标题】Java内部类和静态嵌套类【英文标题】:Java inner class and static nested class 【发布时间】:2010-09-09 09:04:20 【问题描述】:Java 中内部类和静态嵌套类的主要区别是什么?设计/实现是否在选择其中一个方面发挥作用?
【问题讨论】:
Joshua Bloch 的答案是 Effective Java 阅读item 22 : Favor static member classes over non static
作为记录,它是同一本书的第 3 版中的第 24 条。
【参考方案1】:
嗯……内部类是嵌套类……你是指匿名类和内部类吗?
编辑:如果您实际上是指内部与匿名......内部类只是在类中定义的类,例如:
public class A
public class B
而匿名类是匿名定义的类的扩展,因此没有定义实际的“类,如下所示:
public class A
A anon = new A() /* you could change behavior of A here */ ;
进一步编辑:
Wikipedia claims there is a difference 在 Java 中,但我已经使用 Java 8 年了,这是我第一次听到这样的区别......更不用说那里没有任何引用来支持这种说法......归根结底,内部类是在类中定义的类(静态或非静态),而嵌套只是表示同一事物的另一个术语。
静态嵌套类和非静态嵌套类之间存在细微差别......基本上非静态内部类对封闭类的实例字段和方法具有隐式访问(因此它们不能在静态上下文中构造,它将是编译器错误)。另一方面,静态嵌套类没有对实例字段和方法的隐式访问,并且可以在静态上下文中构造。
【讨论】:
根据 Java 文档,内部类和静态嵌套类之间存在区别——静态嵌套类没有对其封闭类的引用,主要用于组织目的。您应该查看 Jegschemesch 的回复以获得更深入的描述。 我认为语义差异主要是历史性的。当我编写 C#->Java 1.1 编译器时,Java 语言参考非常明确:嵌套类是静态的,内部类不是(因此有 this$0)。无论如何,这令人困惑,我很高兴这不再是一个问题。 JLS 在docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3 中定义了“内部类”,这就是为什么在Java 中不可能有一个非静态的“内部类”。 “嵌套”不是“只是表示同一事物的另一个术语”,并且“内部类是在类中定义的类(静态或非静态)”是不正确的。这是不正确的信息。【参考方案2】:这些术语可以互换使用。如果您真的想学究气,那么您可以定义“嵌套类”来引用静态内部类,它没有封闭实例。在代码中,你可能有这样的东西:
public class Outer
public class Inner
public static class Nested
不过,这并不是一个真正被广泛接受的定义。
【讨论】:
“静态内部”用词自相矛盾。 将内部类定义为非静态嵌套类不是惯例,而是JLS。 docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3 这些术语不“可互换使用”。【参考方案3】:来自Java Tutorial:
嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。
使用封闭类名访问静态嵌套类:
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 Tutorial - Nested Classes
为了完整起见,还有inner class without an enclosing instance这样的东西:
class A
int t() return 1;
static A a = new A() int t() return 2; ;
这里,new A() ...
是一个在静态上下文中定义的内部类,并且没有封闭实例。
【讨论】:
请注意,您也可以直接导入静态嵌套类,即您可以(在文件顶部):import OuterClass.StaticNestedClass;
然后将类 just 引用为 OuterClass .【参考方案4】:
嵌套类是一个非常笼统的术语:每个不是***的类都是嵌套类。 内部类是非静态嵌套类。 Joseph Darcy 写了一篇关于Nested, Inner, Member, and Top-Level Classes 的很好的解释。
【讨论】:
【参考方案5】:在创建外部类的实例时创建内部类的实例。因此,内部类的成员和方法可以访问外部类的实例(对象)的成员和方法。当外部类的实例超出范围时,内部类的实例也将不复存在。
静态嵌套类没有具体实例。它只是在第一次使用时加载(就像静态方法一样)。它是一个完全独立的实体,其方法和变量无法访问外部类的实例。
静态嵌套类不与外部对象耦合,它们速度更快,并且不占用堆/堆栈内存,因为不需要创建此类的实例。因此经验法则是尝试定义静态嵌套类,范围尽可能有限(private >= class >= protected >= public),然后将其转换为内部类(通过删除“静态”标识符)并放松范围,如果真的有必要的话。
【讨论】:
第一句不正确。没有“内部类的实例”这样的东西,它的实例可以在外部类被实例化后的任何时候创建。第二句不是从第一句继承而来的。【参考方案6】:我认为上述答案中的真正区别并不明显。
首先要正确理解条款:
嵌套类是在源代码级别包含在另一个类中的类。 如果使用 static 修饰符声明它是静态的。 非静态嵌套类称为内部类。 (我使用非静态嵌套类。)到目前为止,Martin 的回答是正确的。然而,实际的问题是:将嵌套类声明为静态的目的是什么?
如果您只想将类放在一起,或者如果嵌套类专门用于封闭类,则可以使用静态嵌套类。静态嵌套类与其他所有类之间没有语义差异。
非静态嵌套类是另一种野兽。类似于匿名内部类,这样的嵌套类实际上是闭包。这意味着他们捕获了周围的范围和封闭的实例并使其可访问。也许一个例子可以澄清这一点。查看这个 Container 的存根:
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 this$0
。这是在构造函数中设置的,它有一个 Container 类型的附加参数来指定封闭实例。您在源代码中看不到此参数,但编译器为嵌套类隐式生成它。
马丁的例子
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
将被编译为类似(以字节码)的调用
new InnerClass(outerObject)
为了完整起见:
匿名类是非静态嵌套类的完美示例,它没有与之关联的名称,以后也无法引用。
【讨论】:
“静态嵌套类与其他所有类之间没有语义差异。”除了嵌套类可以看到父类的私有字段/方法,父类可以看到嵌套的私有字段/方法。 非静态内部类不会导致大量内存泄漏吗?例如,每次创建侦听器时,都会产生泄漏? @G_V 肯定存在内存泄漏的可能性,因为内部类的实例保留对外部类的引用。这是否是一个实际问题取决于对外部和内部类实例的引用在何处以及如何保存。【参考方案7】:Java tutorial says:
术语:嵌套类是 分为两类:静态 和非静态的。嵌套类 被声明为静态的被简单地称为 静态嵌套类。非静态 嵌套类称为内部 类。
一般来说,术语“嵌套”和“内部”被大多数程序员互换使用,但我将使用正确的术语“嵌套类”,它涵盖内部和静态。
类可以嵌套无限,例如A 类可以包含 B 类,B 类包含 C 类,C 类包含 D 类等。但是,多于一级的类嵌套很少见,因为它通常是糟糕的设计。
创建嵌套类的原因有以下三个:
组织:有时将一个类分类到另一个类的命名空间中似乎是最明智的做法,尤其是当它不会在任何其他上下文中使用时 访问:嵌套类对其包含类的变量/字段具有特殊访问权限(确切地说,哪些变量/字段取决于嵌套类的类型,无论是内部类还是静态类)。 方便:必须为每种新类型创建一个新文件很麻烦,尤其是当该类型仅在一个上下文中使用时Java 中有四种嵌套类。简而言之,它们是:
静态类:声明为另一个类的静态成员 内部类:声明为另一个类的实例成员 本地内部类:在另一个类的实例方法中声明 匿名内部类:类似于本地内部类,但写成返回一次性对象的表达式让我详细说明一下。
静态类
静态类是最容易理解的,因为它们与包含类的实例无关。
静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其命名空间的吊架,eg 类 Goat 声明为类的静态成员 pizza 包中的 em>Rhino 名为 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 来限定,但在包含类内部,它可以通过其简单名称来识别。然而,内部类的每个实例都与它的包含类的一个特定实例相关联:上面,在 jerry 中创建的 Goat 隐式地与 Rhino 相关联 实例 this 在 jerry 中。否则,我们在实例化 Goat 时明确关联的 Rhino 实例:
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(请注意,您在奇怪的 new 语法中将内部类型称为 Goat:Java 从 rhino 部分推断包含类型. 而且,是的,new rhino.Goat() 对我来说也更有意义。)
那么这对我们有什么好处呢?好吧,内部类实例可以访问包含类实例的实例成员。这些封闭的实例成员在内部类 via 中仅引用它们的简单名称,而不是 via this (this在内部类中是指内部类实例,而不是关联的包含类实例):
public class Rhino
private String barry;
public class Goat
public void colin()
System.out.println(barry);
在内部类中,可以将包含类的this引用为Rhino.this,可以使用this来引用对其成员,例如Rhino.this.barry.
本地内部类
本地内部类是在方法体中声明的类。这样的类仅在其包含方法中是已知的,因此只能在其包含方法中对其进行实例化并访问其成员。好处是本地内部类实例被绑定到并且可以访问其包含方法的最终局部变量。当实例使用其包含方法的 final 局部变量时,该变量将保留它在实例创建时所持有的值,即使该变量已经超出范围(这实际上是 Java 的粗略、受限版本的闭包)。
因为本地内部类既不是类的成员也不是包的成员,所以它没有用访问级别声明。 (但要清楚,它自己的成员具有与普通类一样的访问级别。)
如果在实例方法中声明了本地内部类,则内部类的实例化与创建实例时包含方法的 this 所持有的实例相关联,因此包含类的实例成员可以像在实例内部类中一样访问。一个本地内部类被简单地实例化通过它的名字,eg本地内部类Cat被实例化为new Cat(),不像你想象的那样 new this.Cat()。
匿名内部类
匿名内部类是编写本地内部类的一种语法方便的方法。最常见的是,每次运行其包含方法时,本地内部类最多只实例化一次。那么,如果我们可以将本地内部类定义和它的单个实例化组合成一种方便的语法形式,那就太好了,如果我们不必为类想一个名字(越少无用您的代码包含的名称越好)。匿名内部类允许以下两种情况:
new *ParentClassName*(*constructorArgs*) *members*
这是一个返回未命名类的新实例的表达式,该类扩展了 ParentClassName。您不能提供自己的构造函数;相反,一个是隐式提供的,它只是调用超级构造函数,所以提供的参数必须适合超级构造函数。 (如果父级包含多个构造函数,则调用“最简单”的构造函数,“最简单”由一组相当复杂的规则决定,不值得费心去详细学习——只需注意 NetBeans 或 Eclipse 告诉你的内容。)
或者,您可以指定要实现的接口:
new *InterfaceName*() *members*
这样的声明创建了一个未命名类的新实例,它扩展了 Object 并实现了 InterfaceName。同样,您不能提供自己的构造函数;在这种情况下,Java 隐式提供了一个无参数、什么都不做的构造函数(因此在这种情况下永远不会有构造函数参数)。
即使您不能为匿名内部类提供构造函数,您仍然可以使用初始化程序块(放置在任何方法之外的 块)进行任何您想要的设置。
要清楚,匿名内部类只是用一个实例创建本地内部类的一种不太灵活的方法。如果您想要一个实现多个接口的本地内部类,或者在扩展 Object 以外的某个类或指定自己的构造函数的同时实现接口,那么您将无法创建一个常规的命名本地内部类。
【讨论】:
好故事,谢谢。虽然它有一个错误。您可以通过 Rhino.this.variableName 从实例内部类访问外部类的字段。 您以there are two categories
开头评论,然后在评论中间写there are four kinds ...
,老实说,这让我很困惑。类别与“种类”不同?【参考方案8】:
嵌套静态类的使用有一个微妙之处,可能在某些情况下很有用。
虽然静态属性在类通过其构造函数实例化之前被实例化, 嵌套静态类中的静态属性似乎直到在 类的构造函数被调用,或者至少直到第一次引用属性之后, 即使它们被标记为“最终”。
考虑这个例子:
public class C0
static C0 instance = null;
// Uncomment the following line and a null pointer exception will be
// generated before anything gets printed.
//public static final String outerItem = instance.makeString(98.6);
public C0()
instance = this;
public String makeString(int i)
return ((new Integer(i)).toString());
public String makeString(double d)
return ((new Double(d)).toString());
public static final class nested
public static final String innerItem = instance.makeString(42);
static public void main(String[] argv)
System.out.println("start");
// Comment out this line and a null pointer exception will be
// generated after "start" prints and before the following
// try/catch block even gets entered.
new C0();
try
System.out.println("retrieve item: " + nested.innerItem);
catch (Exception e)
System.out.println("failed to retrieve item: " + e.toString());
System.out.println("finish");
尽管 'nested' 和 'innerItem' 都被声明为 'static final'。那个设定 的nested.innerItem 直到类被实例化(或至少 直到第一次引用嵌套的静态项之后),您可以自己看到 通过注释和取消注释我在上面提到的行。同样不成立 'outerItem' 为真。
至少这是我在 Java 6.0 中看到的。
【讨论】:
【参考方案9】:嵌套类:类内类
类型:
-
静态嵌套类
非静态嵌套类[内部类]
区别:
非静态嵌套类[内部类]
在非静态嵌套类中,内部类的对象存在于外部类的对象中。这样外部类的数据成员就可以被内部类访问。所以要创建内部类的对象,我们必须先创建外部类的对象。
outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass();
静态嵌套类
内部类的静态嵌套类对象不需要外部类的对象,因为“静态”这个词表示不需要创建对象。
class outerclass A
static class nestedclass B
static int x = 10;
如果你想访问x,那么在里面写如下方法
outerclass.nestedclass.x; i.e. System.out.prinltn( outerclass.nestedclass.x);
【讨论】:
【参考方案10】:我认为,通常遵循的约定是这样的:
***类中的静态类是嵌套类 ***类中的非静态类是一个内部类,它进一步 还有两种形式: 本地类 - 在块内声明的命名类,如方法或构造函数体 匿名类 - 在表达式和语句中创建实例的未命名类但是,其他几个需要记住的要点是:
***类和静态嵌套类在语义上相同,只是在静态嵌套类的情况下,它可以对其外部 [父] 类的私有静态字段/方法进行静态引用,反之亦然。
内部类可以访问外部 [父] 类的封闭实例的实例变量。但是,并非所有内部类都具有封闭实例,例如静态上下文中的内部类,例如静态初始化程序块中使用的匿名类。
匿名类默认扩展父类或实现父接口,并且没有进一步的子句来扩展任何其他类或实现更多接口。所以,
new YourClass();
表示 class [Anonymous] extends YourClass
new YourInterface();
表示 class [Anonymous] implements YourInterface
我觉得更大的问题仍然悬而未决,该使用哪个以及何时使用?嗯,这主要取决于您正在处理的情况,但阅读@jrudolph 的回复可能会帮助您做出一些决定。
【讨论】:
【参考方案11】:我认为这里的人们应该注意到海报:静态嵌套类只是第一个内部类。 例如:
public static class A //ERROR
public class A
public class B
public static class C //ERROR
public class A
public static class B //COMPILE !!!
所以,总而言之,静态类不依赖于它包含的类。所以,他们不能在正常的课堂上。 (因为普通类需要一个实例)。
【讨论】:
这都是废话。这一切都表明内部类不能包含静态类。关于“不依赖于它包含哪个类”的部分是没有意义的,如下一句。【参考方案12】:我认为以上答案都没有向您解释嵌套类和静态嵌套类在应用程序设计方面的真正区别:
概览
嵌套类可以是非静态或静态的,并且在每种情况下都是在另一个类中定义的类。 嵌套类应该只为服务于封闭类而存在,如果嵌套类对其他类有用(不仅仅是封闭类),则应将其声明为***类。
区别
非静态嵌套类:与包含类的封闭实例隐式关联,这意味着可以调用封闭实例的方法和访问变量。非静态嵌套类的一种常见用途是定义适配器类。
静态嵌套类:无法访问封闭类实例并调用其上的方法,因此应在嵌套类不需要访问封闭类的实例时使用。静态嵌套类的一个常见用途是实现外部对象的组件。
结论
所以从设计的角度来看,两者的主要区别是:非静态嵌套类可以访问容器类的实例,而静态不能。
【讨论】:
:从你的结论“虽然静态不能”,甚至不是容器的静态实例?确定? 静态嵌套类的一个常见用途是 RecyclerView 和 ListView 中的 ViewHolder 设计模式。 在许多情况下,简短的答案更清晰,更好。就是这样一个例子。 静态嵌套类可以访问封闭类的静态字段。【参考方案13】:首先,没有这样的类称为静态类。与内部类(称为嵌套类)一起使用的静态修饰符表示它是外部类的静态成员,这意味着我们可以像访问其他静态成员一样访问它,并且没有任何外部类的实例。 (这本来就是静态的好处。)
使用嵌套类和普通内部类的区别是:
OuterClass.InnerClass inner = new OuterClass().new InnerClass();
首先我们可以实例化 Outerclass 然后我们可以访问 Inner。
但如果 Class 是嵌套的,那么语法是:
OuterClass.InnerClass inner = new OuterClass.InnerClass();
它使用静态语法作为静态关键字的正常实现。
【讨论】:
"...表示它是 Outer Class 的静态成员,这意味着...":将静态嵌套类视为 Outer Class 的“成员类”并不正确,但与静态字段和方法的相似之处到此为止。静态嵌套类不“属于”外部类。在几乎所有重要的方面,静态嵌套类都是一个独立的***类,其类定义已嵌套在外部类的内部以方便打包(希望是因为嵌套类和外部类之间存在逻辑关联...虽然不需要一个)。 “静态内部”在术语上是矛盾的。静态类确实存在,在第一个嵌套级别,根据定义,它们不是内部类。很困惑。【参考方案14】:简单来说,我们需要嵌套类主要是因为 Java 不提供闭包。
嵌套类是在另一个封闭类的主体内定义的类。它们有两种类型 - 静态和非静态。
它们被视为封闭类的成员,因此您可以指定四个访问说明符中的任何一个 - private, package, protected, public
。***类没有这种奢侈,只能声明为 public
或 package-private。
内部类又名非堆栈类可以访问***类的其他成员,即使它们被声明为私有,而静态嵌套类无法访问***类的其他成员。
public class OuterClass
public static class Inner1
public class Inner2
Inner1
是我们的静态内部类,Inner2
是我们的非静态内部类。它们之间的主要区别在于,您无法在没有 Outer 的情况下创建 Inner2
实例,而您可以独立创建 Inner1
对象。
什么时候使用 Inner 类?
设想Class A
和Class B
相关联,Class B
需要访问Class A
成员,而Class B
仅与Class A
相关联的情况。内部类出现了。
要创建内部类的实例,您需要创建外部类的实例。
OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();
或
OuterClass.Inner2 inner = new OuterClass().new Inner2();
什么时候使用静态内部类?
当您知道静态内部类与封闭类/***类的实例没有任何关系时,您将定义它。如果你的内部类不使用外部类的方法或字段,那只是浪费空间,所以将其设为静态。
例如,要为静态嵌套类创建对象,请使用以下语法:
OuterClass.Inner1 nestedObject = new OuterClass.Inner1();
静态嵌套类的优点是它不需要包含类/***类的对象来工作。这可以帮助您减少应用程序在运行时创建的对象数量。
【讨论】:
你的意思是OuterClass.Inner2 inner = outer.new Inner2();
?
static inner
用词自相矛盾。
内部类也不称为“非堆栈类”。不要对不是代码的文本使用代码格式,而是对非代码文本使用它。【参考方案15】:
在创建实例的情况下,非实例 静态内部类是使用的引用创建的 定义它的外部类的对象。这 意味着它有封闭的实例。 但是静态内部类的实例 是使用外部类的引用创建的,而不是使用 外部类对象的引用。这意味着它 没有封闭实例。
例如:
class A
class B
// static int x; not allowed here…..
static class C
static int x; // allowed here
class Test
public static void main(String… str)
A o=new A();
A.B obj1 =o.new B();//need of inclosing instance
A.C obj2 =new A.C();
// not need of reference of object of outer class….
【讨论】:
“静态内部”在术语上是矛盾的。嵌套类要么是静态的,要么是内部的。【参考方案16】:针对不熟悉 Java 和/或嵌套类的学习者
嵌套类可以是: 1. 静态嵌套类。 2.非静态嵌套类。 (也称为内部类)=>请记住这一点
1.内部类 示例:
class OuterClass
/* some code here...*/
class InnerClass
/* some code here...*/
内部类是嵌套类的子集:
内部类是一种特定类型的嵌套类 内部类是嵌套类的子集 你可以说一个内部类也是一个嵌套类,但你可以不说一个嵌套类也是一个内部类。内部类的特长:
内部类的实例可以访问外部类的所有成员,即使是那些标记为“私有”的成员2.静态嵌套类: 示例:
class EnclosingClass
static class Nested
void someMethod() System.out.println("hello SO");
案例 1:从非封闭类实例化静态嵌套类
class NonEnclosingClass
public static void main(String[] args)
/*instantiate the Nested class that is a static
member of the EnclosingClass class:
*/
EnclosingClass.Nested n = new EnclosingClass.Nested();
n.someMethod(); //prints out "hello"
案例 2:从封闭类实例化静态嵌套类
class EnclosingClass
static class Nested
void anotherMethod() System.out.println("hi again");
public static void main(String[] args)
//access enclosed class:
Nested n = new Nested();
n.anotherMethod(); //prints out "hi again"
静态类的特点:
静态内部类只能访问外部类的静态成员,不能访问非静态成员。结论:问题:Java中内部类和静态嵌套类的主要区别是什么?答案: 只需浏览上述每个类的细节。
【讨论】:
'Static inner' is a contradiction in terms.【参考方案17】:我认为这里没有什么要补充的,大多数答案都完美地解释了静态嵌套类和内部类之间的区别。但是,在使用嵌套类与内部类时,请考虑以下问题。 正如在几个答案中提到的,如果没有它们的封闭类的实例,内部类就无法实例化,这意味着它们 HOLD 有一个 pointer 指向它们的封闭类的实例,它可以导致内存溢出或堆栈溢出异常,因为即使它们不再使用,GC 也无法对封闭类进行垃圾收集。为了清楚起见,请检查以下代码:
public class Outer
public class Inner
public Inner inner()
return new Inner();
@Override
protected void finalize() throws Throwable
// as you know finalize is called by the garbage collector due to destroying an object instance
System.out.println("I am destroyed !");
public static void main(String arg[])
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
// out instance is no more used and should be garbage collected !!!
// However this will not happen as inner instance is still alive i.e used, not null !
// and outer will be kept in memory until inner is destroyed
outer = null;
//
// inner = null;
//kick out garbage collector
System.gc();
如果你删除// inner = null;
上的评论,程序会输出
“我被摧毁了!”,但保留此评论不会。
原因是白色的内部实例仍然被引用,GC 无法收集它,因为它引用(有一个指向)外部实例,它也没有被收集。在您的项目中有足够的这些对象并且可能会耗尽内存。
与不指向内部类实例的静态内部类相比,因为它与实例无关,而是与类相关。
如果您将内部类设为静态并使用Outer.Inner i = new Outer.Inner();
实例化,则上述程序可以打印“我被摧毁了!”
【讨论】:
'Static inner' is a contradiction in terms.【参考方案18】:以下是 Java 内部类和静态嵌套类之间的主要区别和相似之处。
希望对你有帮助!
内部类
可以访问外部类实例和静态方法和字段与封闭类的实例相关联,所以要实例化它首先需要一个外部类的实例(注意 new 关键字位置):
Outerclass.InnerClass innerObject = outerObject.new Innerclass();
不能定义任何静态成员本身
不能有Class或Interface声明静态嵌套类
无法访问外部类实例方法或字段
不与任何封闭类的实例关联所以要实例化它:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
相似之处
两个内部类甚至可以访问外部类的私有字段和方法 外部类也可以访问内部类的私有字段和方法 两个类都可以有私有、受保护或公共访问修饰符为什么要使用嵌套类?
根据 Oracle 文档,有几个原因 (full documentation):
这是一种对仅在一个地方使用的类进行逻辑分组的方法:如果一个类仅对另一个类有用,那么将其嵌入该类是合乎逻辑的并将两者保持在一起。嵌套这样的“帮助类”使它们的包更加精简。
它增加了封装性:考虑两个***类 A 和 B,其中 B 需要访问 A 的成员,否则这些成员将被声明为私有。通过将类 B 隐藏在类 A 中,可以将 A 的成员声明为私有的,并且 B 可以访问它们。此外,B 本身可以对外界隐藏。
它可以使代码更具可读性和可维护性:在***类中嵌套小类可使代码更接近使用它的位置。
【讨论】:
我认为这并不完全准确。一个内部类内部可以有另一个内部类。【参考方案19】:不同之处在于,同样是静态的嵌套类声明可以在封闭类之外实例化。
当您有一个非静态的嵌套类声明(也称为内部类)时,Java 不会让您实例化它,除非通过封闭类。从内部类创建的对象链接到从外部类创建的对象,因此内部类可以引用外部的字段。
但如果它是静态的,则链接不存在,无法访问外部字段(除了像任何其他对象一样通过普通引用),因此您可以自己实例化嵌套类。
【讨论】:
这是不真实的。在封闭类范围之外创建内部类有一种特殊的语法。 @user207421 那是什么语法?你的意思是new outer().new inner()
?【参考方案20】:
当我们在类内部声明静态成员类时,它被称为***嵌套类或静态嵌套类。可以如下演示:
class Test
private static int x = 1;
static class A
private static int y = 2;
public static int getZ()
return B.z+x;
static class B
private static int z = 3;
public static int getY()
return A.y;
class TestDemo
public static void main(String[] args)
Test t = new Test();
System.out.println(Test.A.getZ());
System.out.println(Test.B.getY());
当我们在类中声明非静态成员类时,它被称为内部类。内部类可以演示如下:
class Test
private int i = 10;
class A
private int i =20;
void display()
int i = 30;
System.out.println(i);
System.out.println(this.i);
System.out.println(Test.this.i);
【讨论】:
“当我们在类中声明静态成员类时,它被称为***嵌套类” 这没有意义。 "A top level class is a class that is not a nested class." 没有所谓的“***嵌套类”。【参考方案21】:以下是static nested class
和inner class
的示例:
OuterClass.java
public class OuterClass
private String someVariable = "Non Static";
private static String anotherStaticVariable = "Static";
OuterClass()
//Nested classes are static
static class StaticNestedClass
private static String privateStaticNestedClassVariable = "Private Static Nested Class Variable";
//can access private variables declared in the outer class
public static void getPrivateVariableofOuterClass()
System.out.println(anotherStaticVariable);
//non static
class InnerClass
//can access private variables of outer class
public String getPrivateNonStaticVariableOfOuterClass()
return someVariable;
public static void accessStaticClass()
//can access any variable declared inside the Static Nested Class
//even if it private
String var = OuterClass.StaticNestedClass.privateStaticNestedClassVariable;
System.out.println(var);
OuterClassTest:
public class OuterClassTest
public static void main(String[] args)
//access the Static Nested Class
OuterClass.StaticNestedClass.getPrivateVariableofOuterClass();
//test the private variable declared inside the static nested class
OuterClass.accessStaticClass();
/*
* Inner Class Test
* */
//Declaration
//first instantiate the outer class
OuterClass outerClass = new OuterClass();
//then instantiate the inner class
OuterClass.InnerClass innerClassExample = outerClass. new InnerClass();
//test the non static private variable
System.out.println(innerClassExample.getPrivateNonStaticVariableOfOuterClass());
【讨论】:
【参考方案22】: Java中的内部类和嵌套静态类都是在另一个类中声明的类,在Java中称为***类。在 Java 术语中,如果您声明一个嵌套类静态,则在 Java 中它将称为嵌套静态类,而非静态嵌套类简称为内部类。
什么是 Java 中的内部类?
任何不是***或在另一个类中声明的类称为嵌套类,在这些嵌套类中,声明为非静态的类在 Java 中称为内部类。 Java中有三种Inner类:
1) 本地内部类 - 在代码块或方法中声明。 2) 匿名内部类 - 是一个没有名称可以引用的类,并且在它被创建的地方初始化。 3) 内部类成员 - 声明为外部类的非静态成员。
public class InnerClassTest
public static void main(String args[])
//creating local inner class inside method i.e. main()
class Local
public void name()
System.out.println("Example of Local class in Java");
//creating instance of local inner class
Local local = new Local();
local.name(); //calling method from local inner class
//Creating anonymous inner class in Java for implementing thread
Thread anonymous = new Thread()
@Override
public void run()
System.out.println("Anonymous class example in java");
;
anonymous.start();
//example of creating instance of inner class
InnerClassTest test = new InnerClassTest();
InnerClassTest.Inner inner = test.new Inner();
inner.name(); //calling method of inner class
//Creating Inner class in Java
private class Inner
public void name()
System.out.println("Inner class example in java");
什么是 Java 中的嵌套静态类?
嵌套静态类是另一个在类中声明为成员并设为静态的类。嵌套的静态类也被声明为外部类的成员,并且可以像任何其他成员一样设为私有、公共或受保护。嵌套静态类相对于内部类的主要好处之一是嵌套静态类的实例不附加到外部类的任何封闭实例。 您也不需要任何 Outer 类的实例来在 Java 中创建嵌套静态类的实例。
1) 可以访问外部类的静态数据成员,包括私有的。 2) 静态嵌套类不能访问非静态(实例)数据成员或方法。
public class NestedStaticExample
public static void main(String args[])
StaticNested nested = new StaticNested();
nested.name();
//static nested class in java
private static class StaticNested
public void name()
System.out.println("static nested class example in java");
参考:Inner class and nested Static Class in Java with Example
【讨论】:
“静态嵌套类无法访问非静态(实例)数据成员或方法。”不正确,causing confusion。他们绝对可以访问私有实例信息——只要他们创建一个实例来访问该实例信息。它们没有像内部类那样的封闭实例,但它们确实可以访问其封闭类的实例私有成员。【参考方案23】:我已经说明了java代码中可能发生的各种可能的正确和错误场景。
class Outter1
String OutStr;
Outter1(String str)
OutStr = str;
public void NonStaticMethod(String st)
String temp1 = "ashish";
final String tempFinal1 = "ashish";
// below static attribute not permitted
// static String tempStatic1 = "static";
// below static with final attribute not permitted
// static final String tempStatic1 = "ashish";
// synchronized keyword is not permitted below
class localInnerNonStatic1
synchronized public void innerMethod(String str11)
str11 = temp1 +" sharma";
System.out.println("innerMethod ===> "+str11);
/*
// static method with final not permitted
public static void innerStaticMethod(String str11)
str11 = temp1 +" india";
System.out.println("innerMethod ===> "+str11);
*/
// static class not permitted below
// static class localInnerStatic1
public static void StaticMethod(String st)
String temp1 = "ashish";
final String tempFinal1 = "ashish";
// static attribute not permitted below
//static String tempStatic1 = "static";
// static with final attribute not permitted below
// static final String tempStatic1 = "ashish";
class localInnerNonStatic1
public void innerMethod(String str11)
str11 = temp1 +" sharma";
System.out.println("innerMethod ===> "+str11);
/*
// static method with final not permitted
public static void innerStaticMethod(String str11)
str11 = temp1 +" india";
System.out.println("innerMethod ===> "+str11);
*/
// static class not permitted below
// static class localInnerStatic1
// synchronized keyword is not permitted
static class inner1
static String temp1 = "ashish";
String tempNonStatic = "ashish";
// class localInner1
public void innerMethod(String str11)
str11 = temp1 +" sharma";
str11 = str11+ tempNonStatic +" sharma";
System.out.println("innerMethod ===> "+str11);
public static void innerStaticMethod(String str11)
// error in below step
str11 = temp1 +" india";
//str11 = str11+ tempNonStatic +" sharma";
System.out.println("innerMethod ===> "+str11);
//
//synchronized keyword is not permitted below
class innerNonStatic1
//This is important we have to keep final with static modifier in non
// static innerclass below
static final String temp1 = "ashish";
String tempNonStatic = "ashish";
// class localInner1
synchronized public void innerMethod(String str11)
tempNonStatic = tempNonStatic +" ...";
str11 = temp1 +" sharma";
str11 = str11+ tempNonStatic +" sharma";
System.out.println("innerMethod ===> "+str11);
/*
// error in below step
public static void innerStaticMethod(String str11)
// error in below step
// str11 = tempNonStatic +" india";
str11 = temp1 +" india";
System.out.println("innerMethod ===> "+str11);
*/
//
【讨论】:
显然是代码部分。如果您没有注意到:您的代码示例非常难以阅读。即使在我巨大的桌面显示器上,我也有一个水平滚动条。考虑将您的 cmets 放在他们正在评论的内容之上或之下 - 而不是 在后面。 不确定我是否理解您对“同步”的使用。什么时候允许,什么时候不允许,为什么?您的 cmets 与代码显示的内容相矛盾。【参考方案24】:Java 编程语言允许您在另一个类中定义一个类。这样的类称为嵌套类,如下所示:
class OuterClass
...
class NestedClass
...
嵌套类分为两类:静态和非静态。声明为静态的嵌套类称为静态嵌套类。非静态嵌套类称为内部类。 我们应该记住的一件事是非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有。静态嵌套类只能访问封闭类的其他成员(如果它们是静态的)。它不能访问外部类的非静态成员。 与类方法和变量一样,静态嵌套类与其外部类相关联。 例如,要为静态嵌套类创建对象,请使用以下语法:
OuterClass.StaticNestedClass nestedObject =
new OuterClass.StaticNestedClass();
要实例化内部类,必须先实例化外部类。然后,使用以下语法在外部对象中创建内部对象:
OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();
为什么我们使用嵌套类
-
这是一种对仅在一个地方使用的类进行逻辑分组的方法。
它增加了封装性。
它可以产生更易读和可维护的代码。
来源:The Java™ Tutorials - Nested Classes
【讨论】:
【参考方案25】:我认为以上答案都没有为您提供嵌套类和静态嵌套类在应用程序设计方面的区别的真实示例。静态嵌套类和内部类之间的主要区别在于访问外部类实例字段的能力。
让我们看看下面的两个例子。
静态嵌套类:使用静态嵌套类的一个很好的例子是构建器模式 (https://dzone.com/articles/design-patterns-the-builder-pattern)。
对于BankAccount,我们使用静态嵌套类,主要是因为
静态嵌套类实例可以在外部类之前创建。
在builder模式中,builder是一个帮助类,用于创建BankAccount。
BankAccount.Builder 仅与 BankAccount 关联。没有其他类与 BankAccount.Builder 相关。因此最好将它们组织在一起而不使用名称约定。public class BankAccount
private long accountNumber;
private String owner;
...
public static class Builder
private long accountNumber;
private String owner;
...
static public Builder(long accountNumber)
this.accountNumber = accountNumber;
public Builder withOwner(String owner)
this.owner = owner;
return this;
...
public BankAccount build()
BankAccount account = new BankAccount();
account.accountNumber = this.accountNumber;
account.owner = this.owner;
...
return account;
内部类:内部类的一个常见用途是定义一个事件处理程序。 https://docs.oracle.com/javase/tutorial/uiswing/events/generalrules.html
对于MyClass,我们使用内部类,主要是因为:
内部类 MyAdapter 需要访问外部类成员。
在示例中,MyAdapter 仅与 MyClass 关联。没有其他类与 MyAdapter 相关。所以最好在不使用命名约定的情况下将它们组织在一起
public class MyClass extends Applet
...
someObject.addMouseListener(new MyAdapter());
...
class MyAdapter extends MouseAdapter
public void mouseClicked(MouseEvent e)
...// Event listener implementation goes here...
...// change some outer class instance property depend on the event
【讨论】:
【参考方案26】:图表
static nested
和 non-static nested
类之间的主要区别在于 static nested
没有对非静态外部类成员的访问权
【讨论】:
【参考方案27】:除了已经提到的那些,嵌套类的另一个用例是嵌套类具有只能从外部类访问的方法。这是可能的,因为外部类可以访问嵌套类的私有构造函数、字段和方法。
在下面的例子中,Bank
可以发出一个Bank.CreditCard
,它有一个私有构造函数,并且可以使用Bank.CreditCard
的私有setLimit(...)
实例方法根据当前银行政策更改信用卡的限额. (在这种情况下也可以直接访问实例变量limit
)。从任何其他类只能访问 Bank.CreditCard
的公共方法。
public class Bank
// maximum limit as per current bank policy
// is subject to change
private int maxLimit = 7000;
// ------- PUBLIC METHODS ---------
public CreditCard issueCard(
final String firstName,
final String lastName
)
final String number = this.generateNumber();
final int expiryDate = this.generateExpiryDate();
final int CVV = this.generateCVV();
return new CreditCard(firstName, lastName, number, expiryDate, CVV);
public boolean setLimit(
final CreditCard creditCard,
final int limit
)
if (limit <= this.maxLimit) // check against current bank policy limit
creditCard.setLimit(limit); // access private method Bank.CreditCard.setLimit(int)
return true;
return false;
// ------- PRIVATE METHODS ---------
private String generateNumber()
return "1234-5678-9101-1123"; // the numbers should be unique for each card
private int generateExpiryDate()
return 202405; // date is YYYY=2024, MM=05
private int generateCVV()
return 123; // is in real-life less predictable
// ------- PUBLIC STATIC NESTED CLASS ---------
public static final class CreditCard
private final String firstName;
private final String lastName;
private final String number;
private final int expiryDate;
private final int CVV;
private int balance;
private int limit = 100; // default limit
// the constructor is final but is accessible from outer class
private CreditCard(
final String firstName,
final String lastName,
final String number,
final int expiryDate,
final int CVV
)
this.firstName = firstName;
this.lastName = lastName;
this.number = number;
this.expiryDate = expiryDate;
this.CVV = CVV;
// ------- PUBLIC METHODS ---------
public String getFirstName()
return this.firstName;
public String getLastName()
return this.lastName;
public String getNumber()
return this.number;
public int getExpiryDate()
return this.expiryDate;
// returns true if financial transaction is successful
// otherwise false
public boolean charge(final int amount)
final int newBalance = this.balance - amount;
if (newBalance < -this.limit)
return false;
this.balance = newBalance;
return true;
// ------- PRIVATE METHODS ---------
private int getCVV()
return this.CVV;
private int getBalance()
return this.balance;
private void setBalance(final int balance)
this.balance = balance;
private int getLimit()
return limit;
private void setLimit(final int limit)
this.limit = limit;
【讨论】:
以上是关于Java内部类和静态嵌套类的主要内容,如果未能解决你的问题,请参考以下文章