java - java内部类和静态嵌套类

  显示原文与译文双语对照的内容

在Java中内部类和静态嵌套类的主要区别是什么? 设计/implementation 在选择以下任何一个方面是否扮演了一个角色?

时间:

嵌套类分为两类: 静态和 non-static 。声明为静态的嵌套类只是静态嵌套类。 Non-static嵌套类称为内部类。

静态嵌套类使用封闭类名访问:


OuterClass.StaticNestedClass

例如要为静态嵌套类创建一个对象,请使用以下语法:


OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

作为内部类实例的对象存在于外部类的实例中。 请考虑以下类:


class OuterClass {
. . .
 class InnerClass {
. . .
 }
}

InnerClass的实例只能在OuterClass的实例中存在,并且可以直接访问它的封闭实例的方法和字段。

要实例化内部类,必须首先实例化外部类。 然后,使用以下语法在外部对象内创建内部对象:


OuterClass.InnerClass innerObject = outerObject.new InnerClass();

参见:Java教程- 嵌套类

出于完整性的考虑注意,还有这样的事情作为一个内部类没有封闭的实例:


class A {
 int t() { return 1; }
 static A a = new A() { int t() { return 2; } };
}

这里 new A() {.. . } 是一个内部类中定义一个静态上下文和没有一个封闭的实例。

Java教程说明:

术语:嵌套类分为两类: 静态和 non-static 。声明为静态的嵌套类只是静态嵌套类。 Non-static嵌套类称为内部类。

通常来说,"嵌套"和"内部"是大多数程序员交替使用的术语,但我将使用正确的术语"嵌套类",它涵盖了内部和静态。

类可以嵌套无限, 比如 类可以包含类b包含c类包含类d,然而, 等等 不止一个一级类嵌套是罕见的,因为它通常是糟糕的设计。

创建嵌套类有以下三个原因:

  • 组织:有时候,将一个类排序到另一个类的命名空间中似乎是最明智的,特别是当它不会被用于任何其他上下文时
  • 访问:嵌套类对它的包含类的变量/字段具有特殊访问权限。
  • 方便:必须为每个新类型创建一个新文件,这又是烦人的,特别是当类型只在一个上下文中使用时

有在java四种嵌套类。 简而言之,它们是:

  • 静态类: 声明为另一个类的静态成员
  • 内部类: 声明为另一个类的实例成员
  • 本地内部类: 在另一个类的实例方法中声明
  • 匿名内部类: 像一个局部内部类,但作为一个表达式写入,返回一次性对象

更多细节:

静态类

静态类是最容易理解的类,因为它们与包含类的实例无关。

静态类是声明为另一个类的静态成员的类。 就像其他静态成员一样,此类实际上只是一个使用包含类作为它的命名空间的挂起, e.g 。 类山羊声明为类的静态成员犀牛在包比萨众所周知的名字 pizza.Rhino.Goat 。


package pizza;

public class Rhino {

. . .

 public static class Goat {
. . .
 }
}

坦白地说,静态类是一个非常无用的特性,因为类已经被包划分为命名空间。 创建静态类的唯一可以想象的原因是这样一个类可以访问它的类静态成员的私有私有类,但我发现它对静态类特性的存在是一个非常糟糕的理由。

内部类

内部类是声明为另一个类的non-static成员的类:


package pizza;

public class Rhino {

 public class Goat {
. . .
 }

 private void jerry() {
 Goat g = new Goat();
 }
}

静态类一样,内部类被称为限定类名称 pizza.Rhino.Goat,但在包含类中,它可以由它的简单名称知道。 但是,内部类的每个实例都绑定到它的包含类的特定实例: 上面创建的山羊 jerry隐式绑定到犀牛实例这杰瑞 。 否则,我们让犀牛实例相关联明确当我们实例化山羊 :


Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(注意你指的是内心的类型作为山羊奇怪新的语法: Java推断来自 。的包含类型。 而且,新的rhino.Goat() 对我也有更大的意义。

那么这对我们有什么好处? 内部类实例可以访问包含类实例的实例成员。 这些封闭的实例内部类内成员是指通过只是简单的姓名,不通过 这 ( 内部类中的引用内部类实例,而不是关联的包含类实例):


public class Rhino {

 private String barry;

 public class Goat {
 public void colin() {
 System.out.println(barry);
 }
 }
}

内部类,你可以参考这个包含类的 Rhino.this, 你可以使用这个引用其成员, e.g 。 Rhino.this. 。

本地内部类

局部内部类是在方法体中声明的类。 此类类只能在它的包含方法中知道,因此它只能被实例化,并且它的成员可以在它的包含方法中访问。 增益是一个局部内部类实例被绑定,并且可以访问它的包含方法的final 局部变量。 当实例使用它的包含方法的final 局部时,变量保留在创建实例时保留的值,即使变量已经超出了范围( 这实际上是java的粗糙的,限制的闭包版本) 。

因为局部内部类既不是类或者包的成员,也不是用访问级别声明的。 ( 但是,请明确,它自己的成员具有像普通类一样的访问级别。)

如果本地内部类中声明的方法,实例内部类的一个实例与实例由方法的包含创建实例的时候,所以包含实例可以像访问类成员内部类实例。 本地内部类被实例化简单地通过它的名字, e.g 。 本地内部类 cat 被实例化新 Cat(), 不是新 this.Cat() 与你预期的一样。

匿名内部类

匿名内部类是编写本地内部类的一种语法方便的方法。 大多数情况下,局部内部类在每次运行它的方法时都被实例化一次。 那么,如果我们可以将局部内部类定义和它的单个实例化组合成一个方便的语法形式,那么就很好了,如果我们不需要为类( 代码包含的无用名称越少,越好) 设计一个名称。 匿名内部类允许两种情况:


new *ParentClassName*(*constructorArgs*) {*members*}

这是返回一个未命名类的新实例的表达式,它扩展了 ParentClassName 。 你不能提供自己的构造函数;相反,它是隐式提供的,它只是调用超级构造函数,所以提供的参数必须符合超级构造函数。 ( 如果父对象包含多个构造函数,则调用"最简单","最简单"由相当复杂的规则集决定) 。在detail--just中,不值得学习NetBeans或者 Eclipse 告诉你什么。

或者,你可以指定一个接口来实现:


new *InterfaceName*() {*members*}

这样的声明创建了一个未命名类的新实例,它扩展了对象并实现了 。InterfaceName 。 同样,你不能提供自己的构造函数;在本例中,Java隐式提供了一个 no-arg,do-nothing构造函数( 所以在这种情况下不会有构造函数参数) 。

即使不能给匿名内部类提供构造函数,仍然可以使用初始值设定项( 放置在任何方法外部的{} 块) 进行任何设置。

很明显,匿名内部类只是用一个实例创建本地内部类的不太灵活的方法。 如果你想要一个本地内部类实现多个接口或实现接口,扩展一些类除了对象或指定自己的构造函数,你被创建常规命名本地内部类。

我认为在上面的答案中,真正的差异并不明显。

首先获取术语:

  • 嵌套类是包含在源代码级别的另一个类中的类。
  • 它是静态的如果你声明静态修饰符。
  • 一个non-static嵌套类被称为内部类。 ( 我使用non-static嵌套类。)

马丁的回答是正确的。 但是,实际的问题是: 声明嵌套类静态或者非静态的目的是什么?

你使用静态嵌套类如果你只是想让你一起如果他们一起属于局部或者嵌套类是专门用于封闭类。 静态嵌套类和每个其他类之间没有语义差异。

Non-static嵌套类是不同的野兽。 类似于匿名内部类,此类嵌套类实际上是闭包。 这意味着它们捕获周围的作用域和封闭实例,并使它的可以访问。 也许一个例子会阐明。 请查看容器的这个存根:


public class Container {
 public class Item{
 Object data;
 public Container getContainer(){
 return Container.this;
 }
 public Item(Object data) {
 super();
 this.data = data;
 }

 }

 public static Item create(Object data){
//does not compile since no instance of Container is available
 return new Item(data);
 }
 public Item createSubItem(Object data){
//compiles, since 'this' Container is available
 return new Item(data);
 }
}

在本例中,你希望从子项目到父容器的引用。 使用non-static嵌套类,无需一些工作就可以工作。 你可以使用语法 Container.this 访问容器的封闭实例。

以下更严格的解释:

如果你查看编译器为( non-static ) 嵌套类生成的Java字节码,它可能会变得更加清晰:


//class version 49.0 (49)
//access flags 33
public class Container$Item {

//compiled from: Container.java
//access flags 1
 public INNERCLASS Container$Item Container Item

//access flags 0
 Object data

//access flags 4112
 final Container this$0

//access flags 1
 public getContainer() : Container
 L0
 LINENUMBER 7 L0
 ALOAD 0: this
 GETFIELD Container$Item.this$0 : Container
 ARETURN
 L1
 LOCALVARIABLE this Container$Item L0 L1 0
 MAXSTACK = 1
 MAXLOCALS = 1

//access flags 1
 public <init>(Container,Object) : void
 L0
 LINENUMBER 12 L0
 ALOAD 0: this
 ALOAD 1
 PUTFIELD Container$Item.this$0 : Container
 L1
 LINENUMBER 10 L1
 ALOAD 0: this
 INVOKESPECIAL Object.<init>() : void
 L2
 LINENUMBER 11 L2
 ALOAD 0: this
 ALOAD 2: data
 PUTFIELD Container$Item.data : Object
 RETURN
 L3
 LOCALVARIABLE this Container$Item L0 L3 0
 LOCALVARIABLE data Object L0 L3 2
 MAXSTACK = 2
 MAXLOCALS = 3
}

你可以看到编译器创建一个隐藏的字段 Container this$0 。 在构造函数中设置了一个额外的参数,它具有类型容器的附加参数来指定封闭实例。 在源中看不到此参数,但编译器隐式生成它为嵌套类。

马丁的例子


OuterClass.InnerClass innerObject = outerObject.new InnerClass();

将被编译到类似于字节码的调用中


new InnerClass(outerObject)

为了满足完整性:

一个匿名类 non-static嵌套类的一个完美的例子,只是没有与之关联的名字,以后不能引用。

我认为以上的答案解释的真正区别一个嵌套类和静态嵌套类的应用程序设计:

概述

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

差异

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

静态嵌套类: 不能访问封闭类实例并调用它,因此当嵌套类不需要访问封闭类的实例时,应该使用它。 静态嵌套类的一个常见用途是实现外部对象的组件。

结束语

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

我认为,通常遵循的约定是:

  • 顶级类是一个静态类中的 嵌套类
  • 顶级类中的非静态类内部类,进一步有两个更多的形式:
    • 当地类——命名类声明的一块像一个方法或者构造函数体内
    • 匿名类 - 它的实例在表达式和语句中创建的未命名类

然而,还没有其他值得记住的 。: 。

  • 顶级类和静态嵌套类在语义上相同,但在静态嵌套类的情况下,它可以对私有静态字段/它的外部 [parent] 类的方法进行静态引用,反之亦然。

  • 内部类可以访问外部 [parent] 类的封闭实例的实例变量。 然而,并非所有内部类都有封闭实例,例如静态上下文中的内部类,如静态初始化块中使用的匿名类,不包括。

  • 默认情况下,匿名类扩展父类或者实现父接口,没有进一步的子句来扩展任何其他类或者实现任何其他接口。 那么,

    • new YourClass(){}; 意味着 class [Anonymous] extends YourClass {}
    • new YourInterface(){}; 意味着 class [Anonymous] implements YourInterface {}

我觉得更大的问题是一个要使用的问题? 这主要取决于你所处理的场景,但阅读 @jrudolph 给出的回复可以帮助你做出一些决定。

简而言之,我们需要 nested classes 主要是因为Java不提供 closures

nested classes 是在另一个封闭类的体内定义的类。 它们有两种类型- static and non-static

它们被视为封闭类的成员,因此你可以指定四个访问说明符的任何一个 private, package, protected, or public 我们没有top-level类,它只能声明为 public or package

Inner classes aka Non-stack classes 即使 Static nested classes 没有访问顶级类的其他成员的权限,也可以访问顶级类的其他成员。


public class OuterClass {
 public static class Inner1 {
 }
 public class Inner2 {
 }
}

Inner1是我们的静态内部类,而Inner2是我们的内部类,它不是静态的。 它们之间的关键区别是,你不能创建一个没有外部的Inner2实例,因为你可以独立创建一个Inner1对象。

你什么时候使用内部课程。

考虑 Class AClass B 相关的情况,Class B 需要访问 Class A 成员,并且 Class B 只与 Class A 相关。 将内部类别分为磅。

要创建内部类的实例,你需要创建外部类的实例。


OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = OuterClass.new Inner2();

你什么时候使用静态内部类。

当你知道它与 enclosing class/top class的实例没有任何关系时,你将定义一个静态内部类。 如果内部类不使用外部类的方法或者字段,则只是浪费空间,因此使它的成为静态的。

例如要为静态嵌套类创建一个对象,请使用以下语法:


OuterClass.Inner1 nestedObject = new OuterClass.Inner1();

static nested class的优点是它不需要包含类/顶级类的对象。 这可以帮助你减少应用程序在运行时创建的对象数量。

创建外部类实例时创建内部类的实例。 因此内部类的成员和方法可以访问外部类实例( 对象)的成员和方法。 当外部类的实例超出范围时,内部类实例也会停止存在。

静态嵌套类没有具体实例。 在第一次使用( 就像静态方法) 时加载它。 它是一个完全独立的实体,它的方法和变量没有对外部类实例的任何访问。

静态嵌套类与外部对象不耦合,它们更快,并且不会占用堆/堆栈内存,因为不需要创建此类的实例。 因此,经验法则是尝试定义静态嵌套类,使用尽可能有限的范围( 私有> = 类> = 受保护的> = 公共),然后将它的转换为内部类( 通过删除""标识符) 并松开作用域,如果确实需要。

嵌套静态类的使用在某些情况下可能有用。

静态属性在类通过构造函数实例化之前得到实例化,嵌套静态类内部的静态属性似乎直到类的构造函数被调用,或者至少在属性被标记为'final'之后才被实例化。

请考虑以下示例:


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

即使'嵌套'和'innerItem'都被声明为'静态 final'。 在类实例化之后,nested.innerItem的设置才会发生,你可以通过注释和取消注释我引用的行,在上面看到。 'outerItem'同样不适用。

至少这就是我在 Java 6.0中看到的。

在创建实例时,使用外部类的对象引用创建非静态内部类的实例。 这意味着它有inclosing实例。 但是静态内部类的实例是用外部类的引用创建的,而不是外部类的引用。 这意味着它没有inclosing实例。

例如:


class A
{
 class B
 {
//static int x; not allowed here….. 
 }
 static class C
 {
 static int x;//allowed here
 }
}

class Test
{
 public static void main(String… str)
 {
 A o=new A();
 A.B obj1 =o.new B();//need of inclosing instance

 A.C obj2 =new A.C();

//not need of reference of object of outer class….
 }
}

嵌套类:类内部的类

类型:

  1. 静态嵌套类
  2. Non-static嵌套类 [Inner class ]

差异:

Non-static嵌套类 [Inner class]

内部类的non-static嵌套类对象存在于外部类的对象内。 内部类可以访问外部类的数据成员。 要创建内部类的对象,首先必须创建对象的外部类。


outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass(); 

静态嵌套类

在内部类的静态嵌套类对象中不需要外部类对象,因为"静态"表示不需要创建对象。


class outerclass A {
 static class nestedclass B {
 static int x = 10;
 }
}

如果你想访问,则在内部方法中写入


 outerclass.nestedclass.x; i.e. System.out.prinltn( outerclass.nestedclass.x);

...