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


当前回答

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

希望它有帮助!

内部类

可以访问实例和静态方法以及字段的外部类与封闭类的实例关联,因此要实例化它,首先需要一个外部类的实例(注意new关键字place):Outerclass.InnerClass innerObject=outerObject.new InnerClass();无法定义任何静态成员本身不能有类或接口声明

静态嵌套类

无法访问外部类实例方法或字段不与封闭类的任何实例关联,因此要实例化它:OuterClass.StaticNestedClass嵌套对象=新OuterClass.staticNestClass();

相似之处

两个内部类甚至可以访问外部类的私有字段和方法外部类也可以访问内部类的私有字段和方法两个类都可以具有私有、受保护或公共访问修饰符

为什么使用嵌套类?

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

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

其他回答

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

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

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

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

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


我觉得还有一个更大的问题,那就是什么时候使用?这主要取决于你正在处理的场景,但阅读@jrudolph给出的回复可能会帮助你做出一些决定。

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

概述

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

差别

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

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

结论

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

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

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

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

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

考虑以下示例:

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