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


当前回答

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

什么是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中的嵌套静态类?

嵌套静态类是另一个在类内部声明为成员并使其成为静态的类。嵌套的静态类也被声明为外部类的成员,并且可以像任何其他成员一样成为私有的、公共的或受保护的。嵌套静态类相对于内部类的主要优点之一是嵌套静态类的实例不附加到外部类的任何封闭实例。您也不需要任何外部类的实例来在Java中创建嵌套静态类的实例。

1) 它可以访问外部类(包括private)的静态数据成员。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中的内部类和嵌套静态类及其示例

其他回答

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

概述

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

差别

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

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

结论

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

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

A图表

静态嵌套类和非静态嵌套类之间的主要区别在于,静态嵌套类不能访问非静态外部类成员

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

首先要正确使用条款:

嵌套类是包含在源代码级别的另一个类中的类。如果使用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)

为完整起见:

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

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

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

考虑以下示例:

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”为true。

至少这是我在Java6.0中看到的。