Java中的静态嵌套类,为啥?

Posted

技术标签:

【中文标题】Java中的静态嵌套类,为啥?【英文标题】:Static nested class in Java, why?Java中的静态嵌套类,为什么? 【发布时间】:2010-09-20 04:33:37 【问题描述】:

我在查看 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

【问题讨论】:

javatpoint.com/java-inner-class 【参考方案1】:

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

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

【讨论】:

我可能是错的,但这在我看来就像一个静态嵌套类的例子,而不是一个内部类。他们甚至在示例中指定他们无权访问嵌套类中周围类的实例变量。 是的,Colin 说得对——C# 没有内部类,它有嵌套类。请注意,C# 中的静态嵌套类与 Java 中的静态嵌套类不同:) 嵌套类型是 C# 与 Java 相比非常正确的领域之一。我总是惊叹于它的语义/逻辑正确性.. @nawfal:是的,除了一些小问题,我对 C# 语言的设计(和指定)有多好感到敬畏。 @JonSkeet 你有关于这些小问题的文章或博客吗?我很想通过你发现的“小问题”:)【参考方案2】:

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

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

【讨论】:

【参考方案3】:

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

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

【讨论】:

内部类也不是“封闭类实例的一部分”。 一个内部类在存在上依赖于封闭类,并且可以密切访问封闭类的成员,因此它实际上是封闭类的一部分。事实上,它是一个成员。【参考方案4】:

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

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

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

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

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

【讨论】:

呸,我似乎无法让anchor link to the comment 工作,但它的评论是:#comment113712_253507 @matt b 如果静态嵌套类无法访问 Outer 类的实例成员,它如何与 Outer 类的实例成员交互? @mattb 但是@Geek 是如何注意到的,Sun 页面是矛盾的:A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class 如果只是文档前面的一段说:Static nested classes do not have access to other members of the enclosing class 也许他们想说: A nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class @DavidS 感谢您的链接!是的,我错了,现在阅读我的评论,我发现我的改写不正确。正如您所说:An inner class interacts with the instance members through an implicit reference to its enclosing class,这指出了 non-static inner classes 以及 anonymous inner classeslocal classes defined inside a block 的另一个有趣属性:它们都不能拥有 no-arg 构造函数导致compiler 将隐式添加每个构造函数的 arg 序列,以便传递封闭类实例的引用。很简单。 您可以使用静态内部类来实例化只有私有构造函数的外部类。这用于构建器模式。你不能对内部类做同样的事情。【参考方案5】:

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

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

【讨论】:

【参考方案6】:

简单示例:

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

【讨论】:

您的 StaticInnerClass 实际上不是静态嵌套/内部类。它是一个***静态类。【参考方案7】:

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

【讨论】:

如果 'outer' 类是最终类,因此根本无法实例化,那么在这种情况下这个论点是否有意义?因为它拥有/保持对外部类的引用是无用的,如果后者是最终的。【参考方案8】:

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

【讨论】:

“静态内部”用词自相矛盾。 @EJP,哎呀...人们真的会在有人提到“静态内部类”时指出这一点...【参考方案9】:

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

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

【讨论】:

【参考方案10】:

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

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

此类用法的示例,您可以在 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;
    

【讨论】:

【参考方案11】:

内部类的优势--

    一次性使用 支持和改进封装 可读性 私人领域访问

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

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

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

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

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

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

    outer$inner 是内部类名。

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

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
            
        
    

【讨论】:

【参考方案12】:

在某些情况下,使用静态嵌套类而不是非静态类可以节省空间。例如:在一个类中实现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,而不是每次创建新的学生实例时都实例化一个新的。

【讨论】:

【参考方案13】:

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

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

【讨论】:

【参考方案14】:

    JVM 不知道嵌套类。嵌套只是语法糖。

    下图显示 Java 文件:

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

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

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

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

    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;
    
    
    

    私有静态嵌套类代表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
    
    

    更多详情here。

【讨论】:

以上是关于Java中的静态嵌套类,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

Java | 嵌套类(Nested Class)

为啥编译包含静态嵌套类的类会创建一个名为“EnclosureClass$1”的新 .class 文件? [复制]

Java内部类和静态嵌套类

java面试中的简答题(含答案)

Java:非静态嵌套类和 instance.super()

java内部类的静态嵌套类