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

时间:2008-10-31 13:36:51

标签: java class static member

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

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

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

}

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

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

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

请注意。我希望我的条款正确无误,我会称它为静态内部类,但我认为这是错误的:http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

14 个答案:

答案 0 :(得分:257)

您链接的Sun页面在两者之间存在一些关键差异:

  

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

     

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

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

作为Jon Skeet points out,我认为如果你使用嵌套类是一个更好的主意,那就是从静态开始,然后根据你的用法确定它是否真的需要非静态。

答案 1 :(得分:43)

在我看来,每当你看到一个内部类时,问题应该是另一回事 - 真的是否需要成为一个内部类,具有额外的复杂性和隐含的(而不是显式和更清晰,IMO)引用包含类的实例?

请注意,我有偏见作为C#粉丝 - 虽然它确实有嵌套类型,但C#没有内部类的等价物。我不能说我错过了内课:)

答案 2 :(得分:26)

此处需要考虑非显而易见的内存保留问题。由于非静态内部类维护对其“外部”类的隐式引用,因此如果强引用内部类的实例,则外部实例也被强引用。当外部类没有被垃圾收集时,这可能会导致一些令人头疼的问题,即使它出现没有引用它。

答案 3 :(得分:10)

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

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

答案 4 :(得分:7)

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

为了便于打包,我们可以将静态嵌套类添加到一个外部类中以便于阅读。除此之外,没有静态嵌套类的其他用例。

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

正弦所有文件夹都是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;
    }
}

答案 5 :(得分:7)

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

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

答案 6 :(得分:6)

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

  

如果需要访问权限,请使用非静态嵌套类(或内部类)   一个封闭的实例的非公共领域和方法。使用静态   如果您不需要此访问权限,则为嵌套课程。

答案 7 :(得分: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是静态函数的示例中不是这样)

答案 8 :(得分:2)

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

PS:我一直都认为'嵌套'和'内'是可以互换的。这些术语可能有微妙的细微差别,但大多数Java开发人员都会理解。

答案 9 :(得分:1)

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

答案 10 :(得分:0)

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

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

答案 11 :(得分:0)

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

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

答案 12 :(得分:0)

  1. JVM没有嵌套类。嵌套只是语法糖。

    下图显示了Java文件:

    enter image description here

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

    enter image description here

    请注意,将生成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. 更多详细信息here

答案 13 :(得分:-1)

内在阶级的优势 -

  1. 一次使用
  2. 支持并改进封装
  3. 可读性
  4. 私人字段访问
  5. 没有外部类的内部类将不存在。

    class car{
        class wheel{
    
        }
    }
    

    内部类有四种类型。

    1. 普通的内部课程
    2. 方法本地内部课程
    3. 匿名内部课程
    4. 静态内部课程
    5. 点---

      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. 内部类中的
      7. 指向内部类的指针。

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

      8. 内部类适用修饰符的
      9. 是 - public,default,

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

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

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

      12. 10.inner类在静态方法中然后我们只能访问

        的静态字段

        外层。

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