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


当前回答

我认为,通常遵循的惯例是:

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

然而,需要记住的其他几点是:

顶级类和静态嵌套类在语义上是相同的,只是在静态嵌套类的情况下,它可以对其外部〔父〕类的私有静态字段/方法进行静态引用,反之亦然。内部类可以访问外部[父]类的封闭实例的实例变量。然而,并不是所有的内部类都有封闭实例,例如静态上下文中的内部类,比如静态初始值设定项块中使用的匿名类。默认情况下,匿名类扩展父类或实现父接口,并且没有进一步的子句来扩展任何其他类或实现任何其他接口。所以新建YourClass(){};意味着类〔Anonymous〕扩展了YourClass{}新建YourInterface(){};表示类〔Anonymous〕实现YourInterface{}


我觉得还有一个更大的问题,那就是什么时候使用?这主要取决于你正在处理的场景,但阅读@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访问容器的封闭实例。

更多核心解释如下:

如果您查看编译器为(非静态)嵌套类生成的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)

为完整起见:

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

嵌套类是一个非常通用的术语:每个不是顶级的类都是嵌套类。内部类是非静态嵌套类。约瑟夫·达西写了一篇关于嵌套、内部、成员和顶级课程的非常好的解释。

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

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

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

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

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

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

为什么我们使用嵌套类

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

来源:Java™ 教程-嵌套类

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

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

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

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

它们被视为封闭类的成员,因此您可以指定四个访问说明符中的任意一个-私有、包、受保护和公共。对于顶级类,我们没有这种奢侈,它只能声明为public或packageprivate。

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

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

Inner1是我们的静态内部类,Inner2是我们的非静态内部类。它们之间的关键区别是,如果没有Outer,就无法创建Inner2实例,因为您可以独立创建Inner1对象。

你什么时候使用内部类?

想象一下这样一种情况:a类和B类是相关的,B类需要访问a类成员,而B类只与a类相关。

为了创建内部类的实例,需要创建外部类的实例。

OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();

or

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

什么时候使用静态内部类?

当您知道静态内部类与封闭类/顶层类的实例没有任何关系时,可以定义它。如果您的内部类不使用外部类的方法或字段,这只是浪费空间,所以请将其设置为静态。

例如,要为静态嵌套类创建对象,请使用以下语法:

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

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