Java中内部类和静态嵌套类的主要区别是什么?设计/实现是否在选择其中一个方面发挥作用?


当前回答

Java教程说:

术语:嵌套类是分为两类:静态并且是非静态的。嵌套的类被声明为静态的静态嵌套类。非静态嵌套类称为内部类类。

一般来说,术语“嵌套”和“内部”被大多数程序员互换使用,但我将使用正确的术语“嵌套类”,它涵盖内部和静态。

类可以无限嵌套,例如类A可以包含B类,B类包含C类,C类包含D类等。但是,很少有一个以上的类嵌套,因为这通常是错误的设计。

创建嵌套类有三个原因:

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

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

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

让我详细说明一下。

静态类

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

静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其名称空间的挂接器,例如,pizza包中声明为Rhino类静态成员的Goat类被称为pizza.Hino.Goat。

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

坦率地说,静态类是一个非常无用的特性,因为类已经被包划分为名称空间。创建静态类的唯一真正可以想象的原因是这样的类可以访问其包含类的私有静态成员,但我发现这是静态类特性存在的一个非常蹩脚的理由。

内部类

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

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

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

与静态类一样,内部类通过其包含类名pizza.Hino.Goat来限定,但在包含类内部,可以通过其简单名称来限定。然而,内部类的每个实例都与其包含类的一个特定实例相关联:上面,在jerry中创建的Goat被隐式地绑定到Rhino实例,这是在jerrry中创建的。否则,当我们实例化Goat时,我们将显式显示关联的Rhino实例:

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

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

那么这给我们带来了什么?内部类实例可以访问包含类实例的实例成员。这些封闭实例成员仅通过其简单名称在内部类中引用,而不是通过this(内部类中的this引用内部类实例,而不是关联的包含类实例):

public class Rhino {

    private String barry;

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

在内部类中,您可以将包含类的this引用为Rhino.this,也可以使用它来引用其成员,例如Rhino.tis.barry。

本地内部类

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

因为本地内部类既不是类或包的成员,所以它没有用访问级别声明。(但是,要清楚,它自己的成员拥有与普通类一样的访问级别。)

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

匿名内部类

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

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

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

或者,您可以指定要实现的接口:

new *InterfaceName*() {*members*}

这样的声明创建了一个未命名类的新实例,该类扩展了Object并实现了InterfaceName。同样,您不能提供自己的构造函数;在本例中,Java隐式地提供了一个无参数、不做任何事情的构造函数(因此在本例中将不会有构造函数参数)。

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

请注意,匿名内部类只是用一个实例创建本地内部类的一种不太灵活的方式。如果您想要一个实现多个接口的本地内部类,或者在扩展除Object之外的某个类时实现接口,或者指定自己的构造函数,那么您只能创建一个常规的命名本地内部类。

其他回答

Java教程说:

术语:嵌套类是分为两类:静态并且是非静态的。嵌套的类被声明为静态的静态嵌套类。非静态嵌套类称为内部类类。

一般来说,术语“嵌套”和“内部”被大多数程序员互换使用,但我将使用正确的术语“嵌套类”,它涵盖内部和静态。

类可以无限嵌套,例如类A可以包含B类,B类包含C类,C类包含D类等。但是,很少有一个以上的类嵌套,因为这通常是错误的设计。

创建嵌套类有三个原因:

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

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

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

让我详细说明一下。

静态类

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

静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其名称空间的挂接器,例如,pizza包中声明为Rhino类静态成员的Goat类被称为pizza.Hino.Goat。

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

坦率地说,静态类是一个非常无用的特性,因为类已经被包划分为名称空间。创建静态类的唯一真正可以想象的原因是这样的类可以访问其包含类的私有静态成员,但我发现这是静态类特性存在的一个非常蹩脚的理由。

内部类

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

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

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

与静态类一样,内部类通过其包含类名pizza.Hino.Goat来限定,但在包含类内部,可以通过其简单名称来限定。然而,内部类的每个实例都与其包含类的一个特定实例相关联:上面,在jerry中创建的Goat被隐式地绑定到Rhino实例,这是在jerrry中创建的。否则,当我们实例化Goat时,我们将显式显示关联的Rhino实例:

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

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

那么这给我们带来了什么?内部类实例可以访问包含类实例的实例成员。这些封闭实例成员仅通过其简单名称在内部类中引用,而不是通过this(内部类中的this引用内部类实例,而不是关联的包含类实例):

public class Rhino {

    private String barry;

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

在内部类中,您可以将包含类的this引用为Rhino.this,也可以使用它来引用其成员,例如Rhino.tis.barry。

本地内部类

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

因为本地内部类既不是类或包的成员,所以它没有用访问级别声明。(但是,要清楚,它自己的成员拥有与普通类一样的访问级别。)

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

匿名内部类

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

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

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

或者,您可以指定要实现的接口:

new *InterfaceName*() {*members*}

这样的声明创建了一个未命名类的新实例,该类扩展了Object并实现了InterfaceName。同样,您不能提供自己的构造函数;在本例中,Java隐式地提供了一个无参数、不做任何事情的构造函数(因此在本例中将不会有构造函数参数)。

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

请注意,匿名内部类只是用一个实例创建本地内部类的一种不太灵活的方式。如果您想要一个实现多个接口的本地内部类,或者在扩展除Object之外的某个类时实现接口,或者指定自己的构造函数,那么您只能创建一个常规的命名本地内部类。

首先,没有这样的类称为静态类。与内部类一起使用的静态修饰符(称为嵌套类)表示它是外部类的静态成员,这意味着我们可以像其他静态成员一样访问它,而不需要外部类的任何实例。(这本来就是静态的好处。)

使用嵌套类和常规内部类之间的区别是:

OuterClass.InnerClass inner = new OuterClass().new InnerClass();

首先我们可以实例化Outerclass,然后我们可以访问Inner。

但如果类是嵌套的,则语法为:

OuterClass.InnerClass inner = new OuterClass.InnerClass();

它使用静态语法作为静态关键字的正常实现。

在创建实例的情况下静态内部类是使用的引用创建的定义它的外部类的对象。这表示它具有包含实例。但是静态内部类的实例使用外部类的引用创建,而不是使用外部类对象的引用。这意味着没有包含实例。

例如:

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

嗯……内部类是嵌套类……您是指匿名类和内部类吗?

编辑:如果您实际上是指内部v.s.匿名:内部类只是在类中定义的类,例如:

public class A {
    public class B {
    }
}

…而匿名类是匿名定义的类的扩展,因此没有定义实际的“类”,如:

public class A {
}

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

进一步编辑:

维基百科声称Java存在差异,但我已经用Java工作了八年,这是我第一次听到这样的区别——更不用说那里没有引用来支持这一说法……总之,内部类是在类中定义的类(静态或非静态),嵌套只是另一个意思相同的术语。

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

这里是Java内部类和静态嵌套类之间的关键区别和相似之处。

希望它有帮助!

内部类

可以访问实例和静态方法以及字段的外部类与封闭类的实例关联,因此要实例化它,首先需要一个外部类的实例(注意new关键字place):Outerclass.InnerClass innerObject=outerObject.new InnerClass();无法定义任何静态成员本身不能有类或接口声明

静态嵌套类

无法访问外部类实例方法或字段不与封闭类的任何实例关联,因此要实例化它:OuterClass.StaticNestedClass嵌套对象=新OuterClass.staticNestClass();

相似之处

两个内部类甚至可以访问外部类的私有字段和方法外部类也可以访问内部类的私有字段和方法两个类都可以具有私有、受保护或公共访问修饰符

为什么使用嵌套类?

根据Oracle文档,有几个原因(完整文档):

这是一种对仅在一个地方使用的类进行逻辑分组的方法:如果一个类只对另一个类有用,那么将其嵌入该类并将两者保持在一起是合乎逻辑的。嵌套这样的“助手类”使它们的包更加精简。它增加了封装:考虑两个顶级类A和B,其中B需要访问A的成员,否则这些成员将被声明为私有。通过将类B隐藏在类A中,可以将A的成员声明为私有,B可以访问它们。此外,B本身可以对外界隐藏。它可以产生更可读和可维护的代码:将小类嵌套在顶级类中,使代码更接近使用它的地方。