226

我正在查看 Java 代码LinkedList并注意到它使用了静态嵌套类Entry.

public class LinkedList<E> ... {
...

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

}

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

我能想到的唯一原因是,Entry 无法访问实例变量,因此从 OOP 的角度来看,它具有更好的封装性。

但我认为可能还有其他原因,也许是性能。可能是什么?

笔记。我希望我的术语是正确的,我会称它为静态内部类,但我认为这是错误的:http: //java.sun.com/docs/books/tutorial/java/javaOO/nested.html

4

14 回答 14

281

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

嵌套类是其封闭类的成员。非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有。静态嵌套类无权访问封闭类的其他成员。
...

注意:静态嵌套类与其外部类(和其他类)的实例成员交互,就像任何其他顶级类一样。实际上,静态嵌套类在行为上是一个顶级类,为了方便打包,它已经嵌套在另一个顶级类中。

不需要LinkedList.Entry是顶级类,因为它由使用LinkedList(还有一些其他接口也具有名为的静态嵌套类Entry,例如Map.Entry- 相同的概念)。而且由于它不需要访问 LinkedList 的成员,因此它是静态的是有意义的——这是一种更简洁的方法。

正如Jon Skeet 指出的那样,我认为如果您使用嵌套类,最好从静态开始,然后根据您的使用情况决定它是否真的需要是非静态的。

于 2008-10-31T13:47:43.830 回答
48

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

请注意,我偏向于 C# 粉丝 - C# 没有内部类的等价物,尽管它确实有嵌套类型。我不能说我已经错过了内部课程:)

于 2008-10-31T13:40:38.697 回答
28

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

于 2008-10-31T14:25:02.367 回答
15

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

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

于 2016-10-26T16:25:59.667 回答
11

静态嵌套类就像任何其他外部类一样,因为它无法访问外部类成员。

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

这种用法的示例,您可以在 Android R.java (resources) 文件中找到。android 的 res 文件夹包含 layouts(包含屏幕设计)、drawable 文件夹(包含用于项目的图像)、values 文件夹(包含字符串常量)等。

由于所有文件夹都是 Res 文件夹的一部分,android 工具会生成一个 R.java(资源)文件,该文件内部包含许多静态嵌套类,用于它们的每个内部文件夹。

这是在 android 中生成的 R.java 文件的外观和感觉: 这里它们仅用于打包方便。

/* 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;
    }
}
于 2013-12-16T10:31:19.713 回答
10

好吧,一方面,非静态内部类有一个额外的隐藏字段,它指向外部类的实例。因此,如果 Entry 类不是静态的,那么除了具有不需要的访问权限外,它还会携带四个指针而不是三个指针。

作为一项规则,我会说,如果您定义一个基本上用作数据成员集合的类,例如 C 中的“结构”,请考虑将其设为静态。

于 2008-10-31T13:52:51.667 回答
6

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

如果您需要访问封闭实例的非公共字段和方法,请使用非静态嵌套类(或内部类)。如果您不需要此访问权限,请使用静态嵌套类。

于 2013-11-21T19:09:03.753 回答
4

简单的例子:

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 是静态函数的示例中除外)

于 2008-10-31T13:58:25.587 回答
2

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

PS:我一直认为“嵌套”和“内部”是可以互换的。术语中可能存在细微差别,但大多数 Java 开发人员都会理解。

于 2008-10-31T13:46:06.737 回答
1

非静态内部类可能导致内存泄漏,而静态内部类将防止它们。如果外部类拥有大量数据,它会降低应用程序的性能。

于 2011-11-24T13:26:18.803 回答
1
  1. JVM 不知道嵌套类。嵌套只是语法糖。

    下图显示了 Java 文件:

    在此处输入图像描述

    下图显示了 java 文件的类文件表示:

    在此处输入图像描述

    请注意,生成了 2 个类文件,一个用于父类,另一个用于嵌套类。

  2. 非静态嵌套类的对象可以访问封闭范围。通过在嵌套对象中保存封闭范围对象的隐式引用来维护对封闭范围的访问

  3. 嵌套类是一种表示嵌套类类型表示父类组件的意图的方式。

    public class Message {
    
    private MessageType messageType; // component of parent class
    
    public enum MessageType {
        SENT, RECEIVE;
    }
    }
    
    
    
    class Otherclass {
    
    public boolean isSent(Message message) {
        if (message.getMessageType() == MessageType.SENT) { // accessible at other places as well
            return true;
        }
        return false;
    }
    }
    
  4. 私有静态嵌套类代表Point#3&嵌套类型只能是父类的子组件的事实。不能单独使用。

    public class Message {
    
     private Content content; // Component of message class
    
     private static class Content { // can only be a component of message class
    
      private String body;
      private int sentBy;
    
      public String getBody() {
         return body;
      }
    
      public int getSentBy() {
         return sentBy;
      }
    
    }
    }
    
    class Message2 {
      private Message.Content content; // Not possible
    }
    
  5. 更多细节在这里

于 2020-09-18T05:53:38.803 回答
0

我不知道性能差异,但正如你所说,静态嵌套类不是封闭类实例的一部分。除非您确实需要将其作为内部类,否则创建静态嵌套类似乎更简单。

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

于 2008-10-31T13:46:27.700 回答
0

在某些情况下,使用静态嵌套类而不是非静态类可以节省空间。例如:Comparator在一个类中实现一个,比如 Student。

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确保 Student 类只有一个 Comparator,而不是每次创建新的学生实例时都实例化一个新的。

于 2016-10-22T13:13:37.187 回答
-1

内部阶级的优势——

  1. 一次性使用
  2. 支持和改进封装
  3. 可读性
  4. 私有字段访问

没有外部类的存在,内部类将不存在。

class car{
    class wheel{

    }
}

有四种类型的内部类。

  1. 普通内部类
  2. 方法本地内部类
  3. 匿名内部类
  4. 静态内部类

观点 - -

  1. 从静态内部类,我们只能访问外部类的静态成员。
  2. 在内部类中,我们不能声明静态成员。
  3. 为了在外部类的静态区域调用正常的内部类。

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

  4. 为了在外部类的实例区域调用普通的内部类。

    Inner i=new Inner();

  5. 为了在外部类之外调用正常的内部类。

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

  6. inside Inner class 这个指向内部类的指针。

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

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

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

  8. outer$inner 是内部类名的名称。

  9. 实例方法内部的内部类,然后我们可以访问外部类的静态和实例字段。

10.inner class inside static method 然后我们只能访问静态字段

外层。

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
            }
        }
    }
}
于 2016-03-01T07:10:09.810 回答