Java内部类和静态嵌套类


1764

Java中的内部类和静态嵌套类之间的主要区别是什么?设计/实施在选择其中一项方面是否起作用?


65
约书亚·布洛赫(Joshua Bloch)的答案是《 有效的Java 阅读》 item 22 : Favor static member classes over non static
雷蒙德·谢农

4
作为记录,它是同一本书第三版中的第24项。
ZeroCool,

Answers:


1696

Java教程

嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。

静态嵌套类使用封闭的类名称访问:

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() { ... }在静态上下文中定义内部类,没有封闭的实例。


130
记住,你也可以直接输入一个静态嵌套类,即你可以做(在文件的顶部): import OuterClass.StaticNestedClass; 然后引用类只是为在OuterClass。
卡米洛·迪亚斯·Repka,

4
@Martin这个创建内部类的惯用法有什么具体的技术名称OuterClass.InnerClass innerObject = outerObject.new InnerClass();吗?
极客

7
那么私有静态嵌套类的用途是什么?我认为我们无法像OuterClass.StaticNestedClass那样从外部实例化它。nestedObject = new OuterClass.StaticNestedClass();
RoboAlex

1
@Ilya Kogan:我的意思是我知道我们可以访问父级的类字段是静态的还是非静态的(如果子类是非静态的),我已经使用了几次,但是我想知道哪种存储模型他们跟随吗?OOP概念是否将它们作为单独的概念涵盖?如果不是,那么它们真正适合OOP的范围。在我看来像是一个朋友班:)
Mubashar

4
@martin,我知道我来这线程很晚了,但是:这是您的变量a,它是类A的静态字段。这并不意味着由'new A(){int t创建的匿名类(){返回2;}}'是静态的,这比我将其他任何对象简单地分配给静态字段a的方式一样,例如:class B {static void main(string s){Aa = new A()}}(A和B在同一位置包)这不会使A成为静态类。在您链接到的线程中引用的参考文献中,短语“静态上下文”非常模糊。实际上,在该线程的许多评论中都提到了这一点。
GrantRobertson

599

Java教程说

术语:嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。

通常,大多数程序员可以互换使用“嵌套”和“内部”这两个术语,但是我将使用正确的术语“嵌套类”,它涵盖内部和静态两个方面。

类可以无限嵌套,例如,类A可以包含类B,其中类B包含类C,类C包含类D,等等。但是,很少有多个级别的类嵌套,这通常是不好的设计。

创建嵌套类的三个原因:

  • 组织:有时将一个类分类到另一个类的命名空间中似乎是最明智的,尤其是当它不会在任何其他上下文中使用时
  • 访问:嵌套类具有对其包含的类的变量/字段的特殊访问权限(确切地说,哪些变量/字段取决于嵌套类的类型,无论是内部类还是静态类)。
  • 便利:再次为每个新类型创建一个新文件很麻烦,尤其是当该类型仅在一个上下文中使用时

Java中四种嵌套类。简而言之,它们是:

  • 静态类:声明为另一个类的静态成员
  • 内部阶级:声明为另一个类的实例成员
  • 本地内部阶级:在另一个类的实例方法中声明
  • 匿名内部类:类似于本地内部类,但编写为返回一次性对象的表达式

让我详细说明。


静态类

静态类是最容易理解的种类,因为它们与包含类的实例无关。

静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,此类实际上只是一个使用包含类作为其命名空间的挂衣架,例如,在披萨包中声明为Rhino类的静态成员的Goat类被称为pizza.Rhino.Goat

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

坦白地说,静态类是一个非常不值钱的功能,因为类已经被包划分为名称空间。创建静态类的唯一真正可以想象的原因是,此类可以访问其包含类的私有静态成员,但是我发现这对于存在静态类功能是一个相当la脚的理由。


内部班级

内部类是声明为另一个类的非静态成员的类:

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);
        }
    }
}

在内部类中,可以将包含类的这个称为Rhino.this,并且可以使用来引用其成员,例如Rhino.this.barry


本地内部课程

局部内部类是在方法主体中声明的类。此类仅在其包含方法内是已知的,因此只能实例化它,并在其包含方法内对其成员进行访问。这样做的好处是,本地内部类实例已绑定到该实例,并且可以访问其包含方法的最终局部变量。当实例使用其包含方法的最终局部变量时,即使变量超出范围,变量也会保留其在实例创建时所保存的值(这实际上是Java的闭包的原始版本)。

因为本地内部类既不是类也不是包的成员,所以不会使用访问级别声明它。(但是请注意,它自己的成员具有与普通班级相同的访问级别。)

如果在实例方法中声明了本地内部类,则内部类的实例化将与实例创建时包含方法的this所持有的实例相关联,因此可以像在实例中那样访问包含类的实例成员。内部阶级。本地内部类仅通过其名称实例化,例如,本地内部类Cat被实例化为new Cat(),而不是您可能期望的new this.Cat()。


匿名内部类

匿名内部类是编写本地内部类的一种在语法上方便的方法。最常见的是,本地内部类每次在其包含方法运行时最多仅实例化一次。那么,如果我们可以将本地内部类定义及其单一实例化组合为一种方便的语法形式,那就太好了,而且如果我们不必为该类想一个名字(无用的次数越少),那也就越好。您的代码包含的名称更好)。匿名内部类允许这两种情况:

new *ParentClassName*(*constructorArgs*) {*members*}

这是一个表达式,它返回扩展了ParentClassName的未命名类的新实例。您不能提供自己的构造函数。而是隐式提供了一个简单地调用超级构造函数的函数,因此提供的参数必须适合超级构造函数。(如果父级包含多个构造函数,则称为“最简单”的构造函数,由一组相当复杂的规则确定的“最简单”,这些规则不值得详细学习,只需注意NetBeans或Eclipse告诉您的内容即可。)

另外,您可以指定一个接口来实现:

new *InterfaceName*() {*members*}

这样的声明创建了一个未命名类的新实例,该实例扩展了Object并实现InterfaceName。同样,您不能提供自己的构造函数。在这种情况下,Java隐式提供了无参数,无所事事的构造函数(因此,在这种情况下永远不会有构造函数参数)。

即使您不能为匿名内部类提供构造函数,也可以使用初始化程序块(放置在任何方法外部的{}块)进行所需的任何设置。

需要清楚的是,匿名内部类只是使用一个实例创建本地内部类的一种较不灵活的方式。如果您想要一个实现多个接口的本地内部类,或者在扩展除Object之外的某个类或指定其自己的构造函数的同时实现接口的本地内部类,则必须创建一个常规的命名本地内部类。


39
好故事,谢谢。它有一个错误。您可以通过Rhino.this.variableName从实例内部类访问外部类的字段。
瑟勒

2
虽然您不能为匿名内部类提供自己的构造函数,但可以使用双括号初始化。c2.com/cgi/wiki?DoubleBraceInitialization
Casebash 2010年

30
很好的解释,但我不同意静态内部类一文不值。参见rwhansen.blogspot.com/2007/07/…关于构建器模式的巨大变化,该模式在很大程度上取决于静态内部类的使用。
Mansoor Siddiqui

9
我也不同意静态内部类是一文不值的:如果要在内部类中使用枚举,则必须将内部类设为静态。
某处某人2012年

9
私有静态嵌套类也非常有用:您想拥有它们,但不想公开它们。像LinkedList <T>中的Entry <T>或Activity(Android)中的AsyncTasks等...
LorenzoDematté14年

150

我认为以上答案并没有真正的区别。

首先弄清楚条款:

  • 嵌套类是在源代码级别包含在另一个类中的类。
  • 如果使用static修饰符声明它,则它是静态的。
  • 非静态嵌套类称为内部类。(我呆在非静态嵌套类中。)

到目前为止,马丁的回答是正确的。但是,实际的问题是:声明嵌套类为静态的目的是什么?

如果只想让您的类在局部上属于一起,或者嵌套类仅在封闭类中使用,则可以使用静态嵌套类。静态嵌套类与其他每个类之间在语义上没有区别。

非静态嵌套类是不同的野兽。与匿名内部类相似,此类嵌套类实际上是闭包。这意味着他们捕获了周围的范围和周围的实例并使它们可访问。也许有一个例子可以阐明这一点。参见容器的以下存根:

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的封闭实例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)

为了完整性:

匿名类刚刚没有与之关联的名称,并且不能在以后引用非静态嵌套类的一个很好的例子。


17
“静态嵌套类与其他每个类之间在语义上没有区别。” 除了嵌套类可以看到父级的私有字段/方法,父类可以看到嵌套的私有字段/方法。
布拉德·库皮

非静态内部类是否可能导致大量内存泄漏?就像在每次创建一个侦听器时一样,您是否会泄漏?
G_V 2014年

3
@G_V绝对有可能发生内存泄漏,因为内部类的实例保留了对外部类的引用。这是否是一个实际问题取决于在何处以及如何保留对外部和内部类的实例的引用。
jrudolph 2014年

94

我认为上述答案都不能向您说明在应用程序设计方面嵌套类和静态嵌套类之间的真正区别:

概观

嵌套类可以是非静态的,也可以是静态的,并且在每种情况下都是在另一个类中定义的类嵌套类仅应存在于封闭类中,如果嵌套类对其他类(不仅仅是封闭类)有用,则应将其声明为顶级类。

区别

非静态嵌套类:与包含类的封闭实例隐式关联,这意味着可以调用封闭实例的方法和访问变量。非静态嵌套类的一种常见用法是定义Adapter类。

静态嵌套类:无法访问封闭类实例并在其上调用方法,因此当嵌套类不需要访问封闭类实例时,应使用此方法。静态嵌套类的常见用法是实现外部对象的组件。

结论

因此,从设计的角度来看,两者之间的主要区别是:非静态嵌套类可以访问容器类的实例,而静态不能


:从您的结论“虽然静态不能”,甚至不是容器的静态实例?当然?
VdeX

静态嵌套类的常见用法是RecyclerView和ListView中的ViewHolder设计模式。
Hamzeh Soboh,2015年

1
在许多情况下,简短的答案会更清晰,更好。这是一个例子。
艾瑞克·王

32

简单来说,我们需要嵌套类,主要是因为Java不提供闭包。

嵌套类是在另一个封闭类的主体内部定义的类。它们有两种类型-静态和非静态。

它们被视为封闭类的成员,因此您可以指定四个访问说明符-中的任何一个private, package, protected, public。顶级类没有这种奢侈,只能声明public或打包私有。

内部类(也称为非堆栈类)可以访问顶级类的其他成员,即使它们被声明为私有的,而静态嵌套类也不能访问顶级类的其他成员。

public class OuterClass {
    public static class Inner1 {
    }
    public class Inner2 {
    }
}

Inner1是我们的静态内部类,并且Inner2是我们的非静态内部类。它们之间的主要区别在于,Inner2没有外部就不能创建实例,因为可以Inner1独立创建对象。

您什么时候使用内部课程?

考虑与Class AClass 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();

静态嵌套类的优点是它不需要包含类/顶级类的对象即可工作。这可以帮助您减少应用程序在运行时创建的对象数量。


3
你的意思是OuterClass.Inner2 inner = outer.new Inner2();
Erik Kaplun 2014年

4
static inner就矛盾而言。
user207421'6

内部类也不称为“非堆栈类”。不要对非代码文本使用代码格式,而对非文本使用代码格式。
user207421'1

30

这是Java内部类和静态嵌套类之间的主要区别和相似之处。

希望能帮助到你!

内部阶层

  • 可以访问外部类的实例和静态方法及字段
  • 与封闭类的实例相关联,因此要实例化它首先需要一个外部类的实例(请注意新的关键字place):

    Outerclass.InnerClass innerObject = outerObject.new Innerclass();
  • 本身无法定义任何静态成员

  • 不能接口声明

静态嵌套类

  • 无法访问外部类实例方法或字段

  • 与封闭类的任何实例都不相关,因此要实例化它:

    OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

相似之处

  • 这两个内部类可以访问甚至是私有字段和方法外部类
  • 另外,外部类访问私有字段和方法内部类
  • 这两个类都可以具有私有,受保护或公共访问修饰符

为什么要使用嵌套类?

根据Oracle文档,有以下几个原因(完整的文档):

  • 这是一种对仅在一个地方使用的类进行逻辑分组的方法:如果一个类仅对另一个类有用,那么将其嵌入该类并将两者保持在一起是合乎逻辑的。嵌套此类“帮助程序类”可使它们的程序包更加简化。

  • 它增加了封装:考虑两个顶级类A和B,其中B需要访问A的成员,否则将其声明为私有。通过将类B隐藏在类A中,可以将A的成员声明为私有,而B可以访问它们。另外,B本身可以对外界隐藏。

  • 这可能会导致代码更具可读性和可维护性:将小类嵌套在顶级类中会使代码更靠近使用位置。


26

我认为,通常遵循的约定是:

  • 顶级类中的静态类嵌套类
  • 顶层中的非静态类内部类,它还具有另外两种形式:
    • 本地类 -在诸如方法或构造函数体之类的块内声明的命名类
    • 匿名类 -未命名类,其实例在表达式和语句中创建

但是,其他很少 要记住的几点是:

  • 顶级类和静态嵌套类在语义上是相同的,除了在使用静态嵌套类的情况下,它可以静态引用其外部[父]类的私有静态字段/方法,反之亦然。

  • 内部类可以访问外部[parent]类的封闭实例的实例变量。但是,并非所有内部类都有封闭的实例,例如,在静态上下文中的内部类(例如,在静态初始化程序块中使用的匿名类)则没有。

  • 默认情况下,匿名类扩展父类或实现父接口,并且没有进一步的子句扩展任何其他类或实现任何其他接口。所以,

    • new YourClass(){}; 手段 class [Anonymous] extends YourClass {}
    • new YourInterface(){}; 手段 class [Anonymous] implements YourInterface {}

我觉得这个更大的问题仍然悬而未决,什么时候使用?好吧,这主要取决于您正在处理的情况,但是阅读@jrudolph给出的回复可能会帮助您做出一些决定。


15

嵌套类:类内类

类型:

  1. 静态嵌套类
  2. 非静态嵌套类[内部类]

区别:

非静态嵌套类[内部类]

在非静态嵌套类中,内部类的对象存在于外部类的对象之内。这样内部类可以访问外部类的数据成员。因此,要创建内部类的对象,我们必须首先创建外部类的对象。

outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass(); 

静态嵌套类

在静态嵌套类中,内部类的对象不需要外部类的对象,因为单词“ static”表示不需要创建对象。

class outerclass A {
    static class nestedclass B {
        static int x = 10;
    }
}

如果要访问x,请编写以下内部方法

  outerclass.nestedclass.x;  i.e. System.out.prinltn( outerclass.nestedclass.x);

13

创建外部类的实例时,将创建内部类的实例。因此,内部类的成员和方法可以访问外部类的实例(对象)的成员和方法。当外部类的实例超出范围时,内部类实例也将不复存在。

静态嵌套类没有具体实例。它是在第一次使用时加载的(就像静态方法一样)。它是一个完全独立的实体,其方法和变量无权访问外部类的实例。

静态嵌套类不与外部对象耦合,它们更快,并且不占用堆/堆栈内存,因为创建此类的实例不是必需的。因此,经验法则是尝试定义范围尽可能有限的静态嵌套类(私有> =类> =保护> =公共),然后将其转换为内部类(通过删除“静态”标识符)并松开范围,如果确实有必要。


2
第一句话不正确。没有“ 内部类实例”之类的东西,并且可以在实例化外部类后的任何时间创建其实例。第二句话不跟在第一句话之后。
user207421 2015年

11

关于嵌套静态类的使用有些微妙之处,在某些情况下可能会有用。

静态属性是在通过类的构造函数实例化该类之前实例化的,而嵌套的静态类内部的静态属性似乎直到该类的构造函数被调用之后才被实例化,或者至少在首次引用该属性之后才实例化,即使它们被标记为“最终”。

考虑以下示例:

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中看到的。


10

这些术语可以互换使用。如果您想真正学究它,可以定义“嵌套类”来引用静态内部类,该内部类没有封闭实例。在代码中,您可能会有这样的事情:

public class Outer {
    public class Inner {}

    public static class Nested {}
}

但是,这并不是一个真正被广泛接受的定义。


2
“静态内部”是一个矛盾。
2014年

5
将内部类定义为非静态嵌套类不是约定,而是JLS。docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Lew Bloch

1
这些术语不能 “互换使用”。
user207421 '18

为什么上面的示例中的static导致ide错误
DeV

10

在创建实例的情况下,非静态内部类的实例是使用定义了外部类的对象的引用创建的。这意味着它具有封闭实例。但是,静态内部类的实例是使用外部类的引用而不是外部类的对象的引用创建的。这意味着它没有封闭实例。

例如:

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….
  }
}

1
“静态内部”是一个矛盾。嵌套类是静态的或内部的。
user207421

9

我认为这里没有太多要添加的内容,大多数答案可以完美地解释静态嵌套类和内部类之间的区别。但是,在使用嵌套类与内部类时,请考虑以下问题。作为一对夫妇的答案提到的内部类不能没有被实例化和它们所包含的类的实例,它的意思是,他们持有一个指针,以他们的包围类的实例,它可能会导致内存溢出或堆栈溢出异常由于在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无法收集它,并且因为它引用(具有指向)外部实例也未被收集。项目中有足够的这些对象,可能会耗尽内存。
与静态内部类相比,静态内部类不指向内部类实例,因为它不是实例相关的,而是类相关的。如果您将Inner类设为静态并使用实例化,则上面的程序可以显示“ 我被摧毁!Outer.Inner i = new Outer.Inner();




8

嗯...一个内部类就是一个嵌套类...您是说匿名类还是内部类?

编辑:如果您实际上是指内部vs匿名...内部类只是一个类中定义的类,例如:

public class A {
    public class B {
    }
}

匿名类是匿名定义的类的扩展,因此未定义实际的“类”,如下所示:

public class A {
}

A anon = new A() { /* you could change behavior of A here */ };

进一步编辑:

维基百科声称有区别 Java,但是我从事Java已有8年了,这是我第一次听到这样的区别……更不用说那里没有引用来支持该主张了……底部第一行,内部类是在一个类(静态或非静态)中定义的类,而嵌套只是表示同一事物的另一个术语。

静态和非静态嵌套类之间有细微的区别...基本上,非静态内部类可以隐式访问封闭类的实例字段和方法(因此它们不能在静态上下文中构造,它将是一个编译器。错误)。另一方面,静态嵌套类没有对实例字段和方法的隐式访问,并且可以在静态上下文中构造。


3
根据Java文档,内部类和静态嵌套类之间是有区别的-静态嵌套类没有对其封闭类的引用,并且主要用于组织目的。您应该看到Jegschemesch的回复,以获得更详细的描述。
mipadi

1
我认为语义上的差异主要是历史性的。当我编写C#-> Java 1.1编译器时,Java语言参考非常明确:嵌套类是静态的,内部类不是(因此具有$ 0)。无论如何,这令人困惑,我很高兴这不再是一个问题。
Tomer Gabel,2009年

2
JLS在docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3中定义了“内部类” ,这就是为什么不可能有一个非静态的“内部类”的原因类”。“嵌套”不是“仅是另一个术语来表示同一件事”,并且“内部类是在类内定义的类(是否静态)”是不正确的。那是不正确的信息。
Lew Bloch

6

面向新手和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中的内部类和静态嵌套类之间的主要区别是什么?
答:只需仔细阅读上述每个类的细节即可。



6

内部类嵌套静态类Java中的都是在另一个类中声明的类,在Java中称为顶级类。在Java术语中,如果将嵌套类声明为static,则它将在Java中称为嵌套静态类,而将非静态嵌套类简称为Inner Class。

Java中的内部类是什么?

非顶级或在另一个类内部声明的任何类都称为嵌套类,而在这些嵌套类中,被声明为非静态的类在Java中称为内部类。Java中有三种内部类:

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");
        }
    }
}

参考:Java中的内部类和嵌套的静态类(带有示例)


2
“静态嵌套类无法访问非静态(实例)数据成员或方法。” 是不正确的,并引起混乱。他们绝对有权访问私有实例信息-只要他们创建实例来访问该实例信息即可。它们没有内部类那样的封闭实例,但是它们确实可以访问其封闭类的实例私有成员。
TJ Crowder'4

5

我认为这里的人应该注意到Poster:静态嵌套类只是第一个内部类。例如:

 public static class A {} //ERROR

 public class A {
     public class B {
         public static class C {} //ERROR
     }
 }

 public class A {
     public static class B {} //COMPILE !!!

 }

因此,总而言之,静态类并不依赖于它所包含的类。因此,他们不能上普通班。(因为普通类需要一个实例)。


2
这都是废话。所有这些表明,内部类不能包含静态类。关于“不依赖于它所包含的类”的部分是没有意义的,下面的句子也是如此。
user207421 '16

5

当我们在类中声明静态成员类时,它称为顶级嵌套类或静态嵌套类。可以证明如下:

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);
        }
    }
}

“当我们在一个类中声明静态成员类时,它被称为顶级嵌套类”,这没有任何意义。顶级类是不是嵌套类的类。” 没有所谓的“顶级嵌套类”。
Radiodef

3

以下是static nested classand 的示例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()); 

    }

}

3

我认为以上所有答案都无法为您提供真正的例子,就应用程序设计而言,嵌套类和静态嵌套类之间的区别。静态嵌套类和内部类之间的主要区别是访问外部类实例字段的能力。

让我们看一下以下两个示例。

静态嵌套类:构建器模式(https://dzone.com/articles/design-patterns-the-builder-pattern)是使用静态嵌套类的一个很好的例子。

对于BankAccount,我们使用静态的嵌套类,主要是因为

  1. 可以在外部类之前创建静态嵌套类实例。

  2. 在构建器模式中,构建器是用于创建BankAccount的帮助程序类。

  3. 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,我们使用内部类,主要是因为:

  1. 内部类MyAdapter需要访问外部类成员。

  2. 在示例中,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
        }
    }
}

2

在此处输入图片说明

static nestednon-static nested类之间的主要区别是static nested 无法访问非静态外部类成员


0

首先没有这样的类称为静态类。内部类使用的static修饰符(称为嵌套类)表示它是外部类的静态成员,这意味着我们可以像其他静态成员一样访问它,而无需任何其他外部类的实例。(这原本是静态的。)

使用嵌套类和常规内部类的区别是:

OuterClass.InnerClass inner = new OuterClass().new InnerClass();

首先,我们可以实例化外部类,然后可以访问内部。

但是,如果Class为Nested,则语法为:

OuterClass.InnerClass inner = new OuterClass.InnerClass();

它使用static语法作为static关键字的常规实现。


1
“ ...表示它是外部类的静态成员,这意味着....”:将静态嵌套类视为外部类的“成员类”并不是正确的,但是与静态字段和方法到此结束。静态嵌套类不“属于”外部类。在几乎所有重要的方面,静态嵌套类都是一个独立的顶级类,其类定义已嵌套在外部类的内部,以方便打包(并且希望,因为嵌套类与外部类之间存在逻辑关联) ...虽然不需要一个。
斯科特,

1
“静态内部”是一个矛盾。静态类确实存在于第一个嵌套级别,并且根据定义它们不是内部类。非常困惑。
2014年

0

Java编程语言允许您在另一个类中定义一个类。这样的类称为嵌套类,并在此处进行说明:

class OuterClass {
...
class NestedClass {
    ...
    }
}

嵌套类分为两类:静态和非静态。声明为静态的嵌套类称为静态嵌套类。非静态嵌套类称为内部类。我们应该记住的一件事是,非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有的也是如此。如果静态嵌套类是静态的,则它们只能访问封闭类的其他成员。它不能访问外部类的非静态成员。与类方法和变量一样,静态嵌套类与其外部类相关联。例如,要为静态嵌套类创建一个对象,请使用以下语法:

OuterClass.StaticNestedClass nestedObject =
 new OuterClass.StaticNestedClass(); 

要实例化内部类,必须首先实例化外部类。然后,使用以下语法在外部对象内创建内部对象:

OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();

为什么我们使用嵌套类

  1. 这是一种对仅在一个地方使用的类进行逻辑分组的方法。
  2. 它增加了封装。
  3. 它可以导致更易读和可维护的代码。

来源:Java™教程-嵌套类


-1

不同之处在于,可以在封闭类之外实例化也是静态的嵌套类声明。

当您有一个静态的嵌套类声明时,也称为内部类,Java将不允许您实例化它,除非通过封闭类。从内部类创建的对象链接到从外部类创建的对象,因此内部类可以引用外部的字段。

但是,如果它是静态的,则该链接不存在,无法访问外部字段(通过像其他任何对象一样的普通引用除外),因此您可以自己实例化嵌套类。


1
这是不正确的。在封闭类的范围之外创建内部类有一种特殊的语法。
user207421

-1

比较静态本地类和非静态内部类非常简单,
不同之处:
静态本地类:
只能访问外部类的静态成员。
不能具有静态初始值设定项,
不能从声明其的函数外部直接访问


-2

我已经说明了在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);
                        }*/
                    //}
                }
    }

1
显然是代码部分。而且,如果您没有注意到:您的代码示例非常难以阅读。即使在巨大的台式机显示器上,我也有一个水平滚动条。考虑将您的评论放在要评论的内容的上方或下方,而不是放在后面
GhostCat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.