ChatGPT解决这个技术问题 Extra ChatGPT

Java内部类和静态嵌套类

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

Joshua Bloch 的答案是 Effective Java 阅读 item 22 : Favor static member classes over non static
作为记录,它是同一本书第 3 版中的第 24 项。

C
Code-Apprentice

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。
那么,我可以说“静态嵌套类”只是放置在类中的顶级类,而外部类可以被视为“命名空间”。而“嵌套类”是外部类的成员,它需要外部类的封闭实例?
i
informatik01

Java tutorial says

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

通俗地说,术语“嵌套”和“内部”被大多数程序员互换使用,但我将使用正确的术语“嵌套类”,它涵盖了内部和静态。

类可以无限嵌套,例如,A 类可以包含 B 类,B 类包含 C 类,C 类包含 D 类等。但是,多于一级的类嵌套很少见,因为它通常是糟糕的设计。

创建嵌套类的原因有以下三个:

组织:有时将一个类分类到另一个类的命名空间中似乎是最明智的,尤其是当它不会在任何其他上下文中使用时

访问:嵌套类对其包含类的变量/字段具有特殊访问权限(确切地说,哪些变量/字段取决于嵌套类的类型,无论是内部类还是静态类)。

便利性:必须为每种新类型创建一个新文件很麻烦,尤其是当该类型仅在一个上下文中使用时

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

静态类:声明为另一个类的静态成员

内部类:声明为另一个类的实例成员

本地内部类:在另一个类的实例方法中声明

匿名内部类:类似于本地内部类,但写成返回一次性对象的表达式

让我详细说明一下。

静态类

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

静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其命名空间的衣架,例如,在包 Pizza 中声明为类 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,但在包含类内部,它可以通过其简单名称来识别。然而,内部类的每个实例都与它的包含类的一个特定实例相关联:上面,在 jerry 中创建的 Goat,隐式地与在 jerry 中的 Rhino 实例相关联。否则,我们在实例化 Goat 时明确关联的 Rhino 实例:

Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(请注意,您在奇怪的新语法中将内部类型称为 Goat:Java 从 rhino 部分推断包含类型。而且,是的 new rhino.Goat() 对我来说也更有意义。)

那么这对我们有什么好处呢?好吧,内部类实例可以访问包含类实例的实例成员。这些封闭的实例成员在内部类中仅通过它们的简单名称而不是通过 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 所持有的实例相关联,因此包含类的实例成员可以像在实例中一样访问内部阶级。本地内部类仅通过其名称进行实例化,例如本地内部类 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 ... 的评论中间,老实说,这让我感到困惑。一个类别不等于“种类”?
j
jrudolph

我认为上述答案中的真正区别并不明显。

首先要正确使用条款:

嵌套类是在源代码级别包含在另一个类中的类。

如果使用 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.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)

为了完整起见:

匿名类是非静态嵌套类的完美示例,它没有与之关联的名称,以后也无法引用。


“静态嵌套类和其他所有类之间没有语义差异。”除了嵌套类可以看到父类的私有字段/方法,父类可以看到嵌套的私有字段/方法。
非静态内部类不会导致大量内存泄漏吗?例如,每次创建侦听器时,都会产生泄漏?
@G_V 肯定存在内存泄漏的可能性,因为内部类的实例保留对外部类的引用。这是否是一个实际问题取决于对外部和内部类实例的引用在何处以及如何保存。
N
NotCool

我认为以上答案都没有向您解释嵌套类和静态嵌套类在应用程序设计方面的真正区别:

概述

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

区别

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

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

结论

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


:从你的结论“虽然静态不能”,甚至不是容器的静态实例?当然?
静态嵌套类的一个常见用途是 RecyclerView 和 ListView 中的 ViewHolder 设计模式。
在许多情况下,简短的答案更清楚,更好。就是这样一个例子。
静态嵌套类可以访问封闭类的静态字段。
u
user207421

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

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

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

内部类又名非堆栈类可以访问顶级类的其他成员,即使它们被声明为私有,而静态嵌套类无法访问顶级类的其他成员。

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

Inner1 是我们的静态内部类,Inner2 是我们的非静态内部类。它们之间的主要区别在于,您无法在没有 Outer 的情况下创建 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();

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


你是说OuterClass.Inner2 inner = outer.new Inner2();吗?
static inner 是自相矛盾的。
内部类也不称为“非堆栈类”。不要对不是代码的文本使用代码格式,而是对非代码文本使用它。
B
Behzad Bahmanyar

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

希望能帮助到你!

内部类

可以访问外部类的实例和静态方法和字段

与封闭类的实例相关联,因此要实例化它首先需要一个外部类的实例(注意 new 关键字位置): Outerclass.InnerClass innerObject = outerObject.new Innerclass();

不能定义任何静态成员本身

不能有类或接口声明

静态嵌套类

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

不与封闭类的任何实例关联因此实例化它: OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

相似之处

两个内部类甚至可以访问外部类的私有字段和方法

外部类也可以访问内部类的私有字段和方法

两个类都可以有私有、受保护或公共访问修饰符

为什么使用嵌套类?

根据 Oracle 文档,有几个原因 (full documentation):

这是一种对仅在一个地方使用的类进行逻辑分组的方法:如果一个类仅对另一个类有用,那么将其嵌入该类并将两者放在一起是合乎逻辑的。嵌套这样的“帮助类”使它们的包更加精简。它增加了封装性:考虑两个顶级类 A 和 B,其中 B 需要访问 A 的成员,否则这些成员将被声明为私有。通过将类 B 隐藏在类 A 中,可以将 A 的成员声明为私有的,并且 B 可以访问它们。此外,B 本身可以对外界隐藏。它可以导致代码更易读和更可维护:在顶级类中嵌套小类可以使代码更接近使用它的位置。


我认为这并不完全准确。一个内部类内部可以有另一个内部类。
S
Sumit Singh

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

顶级类中的静态类是嵌套类

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

本地类 - 在块内声明的命名类,如方法或构造函数主体

匿名类 - 在表达式和语句中创建实例的未命名类

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

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

内部类可以访问外部 [父] 类的封闭实例的实例变量。然而,并不是所有的内部类都有封闭的实例,例如静态上下文中的内部类,就像在静态初始化块中使用的匿名类一样,没有。

默认情况下,匿名类扩展父类或实现父接口,并且没有进一步的子句来扩展任何其他类或实现更多接口。所以,新 YourClass(){};表示类 [匿名] 扩展 YourClass {} new YourInterface(){};表示类 [Anonymous] 实现 YourInterface {}

新的 YourClass(){};表示类 [匿名] 扩展 YourClass {}

新的 YourInterface(){};表示类 [Anonymous] 实现 YourInterface {}

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


u
user207421

嵌套类:类内类

类型:

静态嵌套类 非静态嵌套类 [内部类]

区别:

非静态嵌套类[内部类]

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

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

r
rmaruszewski

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

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

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


第一句话不正确。没有“内部类的实例”之类的东西,它的实例可以在外部类被实例化后的任何时候创建。第二句不是从第一句继承而来的。
H
HippoMan

嵌套静态类的使用有一个微妙之处,可能在某些情况下很有用。

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

考虑这个例子:

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


D
Daniel Spiewak

这些术语可以互换使用。如果你真的想学究气,那么你可以定义“嵌套类”来引用一个没有封闭实例的静态内部类。在代码中,你可能有这样的东西:

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
并且这些术语不能“互换使用”。
A
Anders Gustafsson

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

例如:

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

“静态内部”是一个矛盾的术语。嵌套类要么是静态的,要么是内部的。
L
L Joey

我认为这里没有太多要补充的,大多数答案都完美地解释了静态嵌套类和内部类之间的区别。但是,在使用嵌套类与内部类时,请考虑以下问题。正如在几个答案中提到的那样,如果没有其封闭类的实例,内部类就无法实例化,这意味着它们持有指向其封闭类实例的指针,这可能导致内存溢出或堆栈溢出异常,因为 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(); 实例化,上述程序可以打印“I am destroyed !


B
Bill the Lizard

嵌套类是一个非常笼统的术语:每个不是顶级的类都是嵌套类。内部类是非静态嵌套类。 Joseph Darcy 写了一篇关于 Nested, Inner, Member, and Top-Level Classes 的非常好的解释。


M
Marko Bonaci

针对不熟悉 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中内部类和静态嵌套类的主要区别是什么?答:只需浏览上述每个类的细节即可。


S
Sae1962

嗯……内部类是嵌套类……你是说匿名类和内部类吗?

编辑:如果您实际上是指内部与匿名:内部类只是在类中定义的类,例如:

public class A {
    public class B {
    }
}

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

public class A {
}

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

进一步编辑:

Java 中的 Wikipedia claims there is a difference,但我已经使用 Java 八年了,这是我第一次听到这样的区别——更不用说那里没有任何引用来支持这种说法……底线,一个内部类是在类中定义的类(静态或非静态),而嵌套只是表示同一事物的另一个术语。

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


根据 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 中不可能有一个非静态的“内部类”。 “嵌套”不是“只是表示同一事物的另一个术语”,并且“内部类是在类中定义的类(静态或非静态)”是不正确的。那是不正确的信息。
r
roottraveller

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。他们绝对可以访问私有实例信息——只要他们创建一个实例来访问该实例信息。它们没有像内部类那样的封闭实例,但它们确实可以访问它们的封闭类的实例私有成员。
L
Leogao

我认为以上答案都没有为您提供嵌套类和静态嵌套类在应用程序设计方面的区别的真实示例。静态嵌套类和内部类之间的主要区别在于访问外部类实例字段的能力。

让我们看看下面的两个例子。

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

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

静态嵌套类实例可以在外部类之前创建。在构建器模式中,构建器是一个帮助类,用于创建 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
        }
    }
}

y
yoAlex5

一张图

https://i.stack.imgur.com/4zgFn.png

static nestednon-static nested 类之间的主要区别在于 static nested 没有对非静态外部类成员的访问权


N
Nikhil Agrawal

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

 public static class A {} //ERROR

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

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

 }

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


这都是废话。这一切都表明内部类不能包含静态类。关于“不依赖于它包含哪个类”的部分是没有意义的,就像下面的句子一样。
P
Pankti

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

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." 没有“顶级嵌套类”之类的东西。
P
Pritam Banerjee

以下是 static nested classinner class 的示例:

外部类.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);
     }

}

外部类测试:

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

    }

}

P
Pang

Java 编程语言允许您在另一个类中定义一个类。这样的类称为嵌套类,如下所示:

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

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

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

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

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

为什么我们使用嵌套类

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

来源:The Java™ Tutorials - Nested Classes


P
Pritam Banerjee

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

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

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

首先我们可以实例化 Outerclass 然后我们可以访问 Inner。

但是如果 Class 是嵌套的,那么语法是:

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

它使用静态语法作为静态关键字的正常实现。


“……说它是 Outer Class 的静态成员,这意味着……”:将静态嵌套类视为 Outer Class 的“成员类”并没有错,但与静态字段的相似之处和方法到此为止。静态嵌套类不“属于”外部类。在几乎所有重要的方面,静态嵌套类都是一个独立的顶级类,其类定义已嵌套在外部类的内部以方便打包(希望是因为嵌套类和外部类之间存在逻辑关联...虽然不需要一个)。
“静态内部”是一个矛盾的术语。静态类确实存在,在第一个嵌套级别,根据定义,它们不是内部类。很困惑。
J
JCvanDamme

除了已经提到的那些,嵌套类的另一个用例是嵌套类具有只能从外部类访问的方法。这是可能的,因为外部类可以访问嵌套类的私有构造函数、字段和方法。

在下面的示例中,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;
        }
    }
}

J
Jonathan Cole

静态嵌套类访问它们定义的类的 PRIVATE 类级静态变量。从架构的角度来看,这可能是巨大的(即服务定位器模式在服务中使用嵌套静态帮助器类),并且可以帮助 OP 了解它们为什么存在内部类。


D
DigitalRoss

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

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

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


这是不真实的。在封闭类范围之外创建内部类有一种特殊的语法。
@user207421 那是什么语法?你是说new outer().new inner()吗?
@Scratte 是的,或更一般地说是 outer.newInner(),其中 outer 是对 Outer 实例的任何引用。您不必为每个内部实例创建一个新的外部实例。
A
Ashish Sharma

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

显然是代码部分。如果您没有注意到:您的代码示例非常难以阅读。即使在我巨大的桌面显示器上,我也有一个水平滚动条。考虑将您的评论放在他们评论的内容之上或之下 - 而不是放在后面。
不确定我是否理解您对“同步”的使用。什么时候允许,什么时候不允许,为什么?您的评论与代码显示的内容相矛盾。
static inner 是自相矛盾的。因此这里没有静态内部类。
注释行说->“这很重要,我们必须在下面的非静态内部类中使用静态修饰符保持最终”。所以没有提到静态内部类。