Java内部类和静态嵌套类

时间:2020-03-05 18:55:23  来源:igfitidea点击:

Java中的内部类和静态嵌套类之间的主要区别是什么?设计/实施在选择其中一项方面是否起作用?

解决方案

回答

嗯...内部类是嵌套类...我们是说匿名类还是内部类?

编辑:如果我们实际上是指内部vs匿名...内部类只是一个类中定义的类,例如:

public class A {
    public class B {
    }
}

匿名类是匿名定义的类的扩展,因此未定义实际的"类",如下所示:

public class A {
}

A anon = new A() { /* you could change behavior of A here */ };

进一步编辑:

Wikipedia声称Java有所不同,但是我从事Java已有8年了,这是我第一次听到这样的区别……更不用说那里没有引用来支持该主张了。一行,内部类是在一个类(静态或者非静态)中定义的类,而嵌套只是表示同一事物的另一个术语。

静态和非静态嵌套类之间有细微的区别...基本上,非静态内部类可以隐式访问封闭类的实例字段和方法(因此它们不能在静态上下文中构造,它将是编译器错误)。另一方面,静态嵌套类没有对实例字段和方法的隐式访问,并且可以在静态上下文中构造。

回答

这些术语可以互换使用。如果我们想真正学究它,可以定义"嵌套类"来引用静态内部类,该内部类没有封闭实例。在代码中,我们可能会有这样的事情:

public class Outer {
    public class Inner {}

    public static class Nested {}
}

但是,这并不是一个真正被广泛接受的定义。

回答

从Java教程中:

Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are simply called static nested classes. Non-static nested classes are called inner classes.

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

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(){...}"是在静态上下文中定义的内部类,没有封闭的实例。

回答

嵌套类是一个非常笼统的术语:不是顶级的每个类都是一个嵌套类。
内部类是一个非静态的嵌套类。
约瑟夫·达西(Joseph Darcy)对嵌套类,内部类,成员类和顶级类写了很好的解释。

回答

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

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

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

回答

我认为以上答案并没有真正的区别。

首先弄清楚条款:

  • 嵌套类是在源代码级别包含在另一个类中的类。
  • 如果使用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);
    }
}

在这种情况下,我们需要从子项到父容器的引用。使用非静态嵌套类,此方法无需进行任何工作。我们可以使用语法Container.this访问Container的封闭实例。

以下是更多硬性解释:

如果查看编译器为(非静态)嵌套类生成的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
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
// access flags 1 public getContainer() : Container L0 LINENUMBER 7 L0 ALOAD 0: this GETFIELD Container$Item.this
new InnerClass(outerObject)
: 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
Terminology: Nested classes are
  divided into two categories: static
  and non-static. Nested classes that
  are declared static are simply called
  static nested classes. Non-static
  nested classes are called inner
  classes.
: 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。这是在构造函数中设置的,该构造函数具有一个类型为Container的添加参数以指定封闭实例。我们无法在源代码中看到此参数,但编译器会为嵌套类隐式生成该参数。

马丁的例子

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

因此将被编译为类似于(以字节码形式)的调用

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

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

为了完整性:

匿名类是非静态嵌套类的一个完美示例,该嵌套类没有任何关联的名称,以后无法引用。

回答

Java教程说:

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

通常,大多数程序员可以互换使用"嵌套"和"内部"这两个术语,但是我将使用覆盖内部和静态的正确术语"嵌套类"。

类可以无限嵌套,例如A类可以包含B类,其中B类包含C类,而C类包含D类,等等。但是,很少有多个级别的类嵌套,这通常是不好的设计。

创建嵌套类的三个原因:

  • 组织:有时将一个类分类到另一个类的命名空间中似乎是最明智的,尤其是当它不会在任何其他上下文中使用时
  • 访问:嵌套类具有对其包含的类的变量/字段的特殊访问权限(确切地说,哪个变量/字段取决于嵌套类的类型,无论是内部类还是静态类)。
  • 便利:再次为每个新类型创建一个新文件很麻烦,尤其是当该类型仅在一个上下文中使用时

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

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

让我详细说明。

静态类

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

静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,此类实际上只是将包含类用作其命名空间的挂衣架,例如被称为Pizza包中Rhino类的静态成员的Goat类被称为pizza.Rhino.Goat。

public class Rhino {

    private String barry;

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

坦白地说,静态类是一个非常不值钱的功能,因为类已经被包划分为名称空间。创建静态类的唯一真正可以想象的原因是,此类可以访问其包含类的私有静态成员,但是我发现这对于存在静态类功能是一个很la脚的理由。

内部班级

内部类是声明为另一个类的非静态成员的类:

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

像静态类一样,内部类通过其包含的类名称pizza.Rhino.Goat被限定为合格,但是在包含的类内部,可以通过其简单名称来知道内部类。但是,内部类的每个实例都与其包含类的特定实例相关联:上面,在jerry中创建的Goat,隐式绑定到在jerry中的Rhino实例。否则,我们在实例化Goat时使关联的Rhino实例显式:

new *InterfaceName*() {*members*}

(注意,我们在奇怪的新语法中将内部类型称为Goat:Java从rhino部分推断出包含类型。而且,是的,新的rhino.Goat()对我来说也更有意义。)

那么,这有什么好处呢?好吧,内部类实例可以访问包含的类实例的实例成员。这些封闭的实例成员仅通过其简单名称而不是通过其内部名称在内部类内部进行引用(内部类中的此引用是内部类实例,而不是关联的包含类实例):

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

在内部类中,我们可以将包含类的this称为Rhino.this,并且可以使用this来引用其成员,例如犀牛

本地内部课程

局部内部类是在方法主体中声明的类。这样的类仅在其包含方法内是已知的,因此只能实例化它,并在其包含方法内对其成员进行访问。这样做的好处是,本地内部类实例与该实例相关联并且可以访问其包含方法的最终局部变量。当实例使用其包含方法的最终局部变量时,即使变量超出范围(实际上是Java的有限的封闭版本),该变量仍保留其在实例创建时所持有的值。

因为本地内部类既不是类也不是包的成员,所以不会使用访问级别声明它。 (但是请注意,它自己的成员具有与普通班级一样的访问级别。)

如果在实例方法中声明了本地内部类,则在创建实例时,内部类的实例将与包含方法的this所持有的实例绑定,因此可以像在实例中那样访问包含类的实例成员。内部阶级。本地内部类仅通过其名称实例化,例如本地内部类Cat被实例化为new Cat(),而不是我们可能期望的new this.Cat()。

匿名内部类

匿名内部类是编写本地内部类的一种在语法上方便的方法。最常见的是,本地内部类每次在其包含方法运行时最多仅实例化一次。那么,如果我们可以将本地内部类的定义及其单个实例组合为一种方便的语法形式,那将是很好的,而且如果我们不必为该类想一个名字(无用的更少)代码包含的名称越好)。匿名内部类允许这两种情况:

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

这是一个表达式,它返回扩展了ParentClassName的未命名类的新实例。我们不能提供自己的构造函数。而是隐式提供了一个简单地调用超级构造函数的函数,因此提供的参数必须适合超级构造函数。 (如果父级包含多个构造函数,则称为最简单的构造函数,它由一组相当复杂的规则确定的,这些规则不值得详细学习,只需注意NetBeans或者Eclipse告诉内容即可。)

另外,我们可以指定一个接口来实现:

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

这样的声明创建了一个未命名类的新实例,该实例扩展了Object并实现了InterfaceName。同样,我们不能提供自己的构造函数。在这种情况下,Java隐式提供了无参,无所事事的构造函数(因此,在这种情况下永远不会有构造函数参数)。

即使我们不能为匿名内部类提供构造函数,也可以使用初始化程序块(放置在任何方法外部的{}块)进行所需的任何设置。

需要清楚的是,匿名内部类只是使用一个实例创建本地内部类的一种较不灵活的方式。如果我们想要一个实现多个接口的本地内部类,或者在扩展除Object之外的某个类或者指定其自己的构造函数的同时实现接口,则必须创建一个常规的命名本地内部类。

回答

关于嵌套静态类的使用有些微妙之处,在某些情况下可能有用。

静态属性是在通过类的构造函数实例化该类之前实例化的,
嵌套静态类内部的静态属性似乎要在实例化之后才被实例化。
类的构造函数被调用,或者至少在首次引用属性之后才被调用,
即使它们被标记为"最终"。

考虑以下示例:

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

即使'nested'和'innerItem'都声明为'static final'。那个设定
直到实例化该类之后(或者至少在实例化之后)
直到我们第一次看到嵌套的静态项目之后)
通过注释和取消注释我上面提到的行。一样不成立
对于'outerItem'为true。

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

回答

嵌套类:类内类

类型:

  • 静态嵌套类
  • 非静态嵌套类[内部类]

区别:

非静态嵌套类[内部类]

在非静态嵌套类中,内部类的对象存在于外部类的对象之内。这样内部类可以访问外部类的数据成员。因此,要创建内部类的对象,我们必须首先创建外部类的对象。

##代码##

静态嵌套类

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

##代码##

如果要访问x,请编写以下内部方法

##代码##

回答

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

  • 顶级类中的静态类是嵌套类
  • 匿名类-未命名类,其实例在表达式和语句中创建

但是,要记住的其他几点是:

  • 顶级类和静态嵌套类在语义上是相同的,除了在使用静态嵌套类的情况下,它可以静态引用其外部[父]类的私有静态字段/方法,反之亦然。
  • 内部类可以访问外部[parent]类的封闭实例的实例变量。但是,并非所有内部类都有封闭的实例,例如,在静态上下文中的内部类(例如,在静态初始化程序块中使用的匿名类)则没有。
  • " new YourInterface(){};"的意思是"类[匿名]实现YourInterface {}"。

我觉得还有待解决的更大问题是使用哪个以及何时使用?好吧,这主要取决于我们正在处理的情况,但是阅读@jrudolph给出的回复可能会做出一些决定。