Java中的静态嵌套类,为什么?

Static nested class in Java, why?

我在看EDCOX1 0的Java代码,并注意到它使用了一个静态嵌套类EDOCX1,1。

1
2
3
4
5
6
public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

使用静态嵌套类而不是普通内部类的原因是什么?

我能想到的唯一原因是,条目没有访问实例变量的权限,所以从OOP的角度来看,它具有更好的封装性。

但我想可能还有其他原因,也许是表演。可能是什么?

注意事项。我希望我的术语是正确的,我会称之为静态内部类,但我认为这是错误的:http://java.sun.com/docs/books/tutorial/java/javaoo/nested.html


您链接到的太阳页面在这两者之间有一些关键区别:

A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. Static nested classes do not have access to other members of the enclosing class.
...

Note: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.

没有必要将LinkedList.Entry作为顶级类,因为它只由LinkedList使用(还有一些其他接口也有名为Entry的静态嵌套类,如Map.Entry相同的概念)。而且由于它不需要访问LinkedList的成员,所以它是静态的,这是一种更干净的方法。

正如jon skeet所指出的,如果您使用的是嵌套类,那么最好从它是静态的开始,然后根据您的使用情况决定它是否真的需要是非静态的。


在我看来,当你看到一个内部类的时候,这个问题应该是相反的——它真的需要一个内部类,具有额外的复杂性,并且隐式(而不是显式和清晰的,imo)引用一个包含类的实例吗?

注意,我有偏见,因为C fan-C没有等价的内部类,尽管它确实有嵌套类型。我不能说我错过了内部课程:)


这里有一些不明显的内存保留问题需要考虑。由于非静态内部类维护对其"外部"类的隐式引用,因此如果内部类的实例被强引用,则外部实例也会被强引用。当外部类没有被垃圾收集时,这可能会导致一些头疼,即使看起来没有任何东西引用它。


首先,非静态内部类有一个额外的隐藏字段,它指向外部类的实例。因此,如果入口类不是静态的,那么除了拥有它不需要的访问权之外,它还将携带四个指针而不是三个指针。

通常,我会说,如果您定义了一个基本上作为数据成员集合的类,就像C中的"结构",那么考虑将其设置为静态的。


构建器模式中使用静态内部类。静态内部类可以实例化它的外部类,该类只有私有构造函数。因此,可以使用静态内部类来实例化只有私有构造函数的外部类。对于内部类,您不能这样做,因为您需要在访问内部类之前创建外部类的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class OuterClass {
    private OuterClass(int x) {
        System.out.println("x:" + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

这将输出x:1


静态嵌套类与任何其他外部类一样,因为它没有访问外部类成员的权限。

为了方便打包,我们可以将静态嵌套类组合到一个外部类中,以提高可读性。除此之外,没有其他静态嵌套类的用例。

例如,对于这种用法,您可以在android r.java(resources)文件中找到。android的res文件夹包含布局(包含屏幕设计)、可绘制文件夹(包含用于项目的图像)、值文件夹(包含字符串常量)等。

sine所有文件夹都是res文件夹的一部分,android工具生成一个r.java(resources)文件,该文件在内部为每个内部文件夹包含许多静态嵌套类。

以下是在Android中生成的r.java文件的外观和感觉:在这里,它们仅用于包装方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */


package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}


从http://docs.oracle.com/javase/tutorial/java/javaoo/whentouse.html:

Use a non-static nested class (or inner class) if you require access
to an enclosing instance's non-public fields and methods. Use a static
nested class if you don't require this access.


简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

如果是非静态的,则类不能在上一个类的实例中进行实例化(因此在main是静态函数的示例中不能这样做)


静态与正常的原因之一与类加载有关。不能在父类的构造函数中实例化内部类。

附言:我一直理解"嵌套"和"内部"是可以互换的。术语中可能有微妙的细微差别,但大多数Java开发人员也会理解。


非静态内部类会导致内存泄漏,而静态内部类则会对其进行保护。如果外部类包含大量数据,则可能会降低应用程序的性能。


在某些情况下,使用静态嵌套类而不是非静态类可以节省空间。例如:学生说,在课堂上实现一个Comparator

1
2
3
4
5
6
7
8
public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

然后,static确保学生类只有一个比较器,而不是每次创建新的学生实例时都实例化一个新的比较器。


我不知道性能的差异,但是正如您所说,静态嵌套类不是封闭类实例的一部分。创建静态嵌套类似乎更简单,除非您真的需要它是一个内部类。

这有点像为什么我总是用Java制作变量——如果它们不是最终的,我知道它们会有一些有趣的事情发生。如果使用内部类而不是静态嵌套类,那么应该有一个很好的理由。


内部阶级的优势--

  • 一次性使用
  • 支持和改进封装
  • 可读性
  • 私有字段访问
  • 没有外部类的存在,内部类就不存在。

    1
    2
    3
    4
    5
    class car{
        class wheel{

        }
    }

    有四种类型的内部类。

  • 普通内部类
  • 方法局部内部类
  • 匿名内部类
  • 静态内部类
  • 点---

  • 从静态内部类,我们只能访问外部类的静态成员。
  • 在内部类中,我们不能声明静态成员。
  • 以便在外部类的静态区域中调用普通的内部类。

    Outer 0=new Outer();
    Outer.Inner i= O.new Inner();

  • 以便在外部类的实例区域中调用普通的内部类。

    Inner i=new Inner();

  • 以便在外部类之外调用普通的内部类。

    Outer 0=new Outer();
    Outer.Inner i= O.new Inner();

  • 在内部类中,指向内部类的指针。

    this.member-current inner class
    outerclassname.this--outer class

  • 对于内部类,适用的修饰符是--public,default,

    final,abstract,strictfp,+private,protected,static

  • outer$inner是内部类名的名称。

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

  • 10.静态方法内部的类,那么我们只能访问静态字段

    外层。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class outer{

        int x=10;
        static int y-20;

        public void m1() {
            int i=30;
            final j=40;

            class inner{

                public void m2() {
                    // have accees x,y and j
                }
            }
        }
    }