Java 内部类和静态嵌套类
Java中的内部类和静态嵌套类之间的主要区别是什么?设计/实现在选择其中之一时是否发挥作用?
Java中的内部类和静态嵌套类之间的主要区别是什么?设计/实现在选择其中之一时是否发挥作用?
从 Java 教程中:
嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。
静态嵌套类使用封闭类名进行访问:
OuterClass.StaticNestedClass
例如,若要为静态嵌套类创建对象,请使用以下语法:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
作为内部类实例的对象存在于外部类的实例中。请考虑以下类:
class OuterClass {
...
class InnerClass {
...
}
}
InnerClass 的实例只能存在于 OuterClass 的实例中,并且可以直接访问其封闭实例的方法和字段。
若要实例化内部类,必须首先实例化外部类。然后,使用以下语法在外部对象内创建内部对象:
OuterClass outerObject = new 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() { ... }
术语:嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。
在通常的说法中,术语“嵌套”和“内部”被大多数程序员互换使用,但我将使用正确的术语“嵌套类”,它涵盖了内部和静态。
类可以无限嵌套,例如.class A可以包含B类,B类包含包含D类的C类等。但是,多个级别的类嵌套很少见,因为它通常是糟糕的设计。
创建嵌套类有三个原因:
Java 中有四种嵌套类。简而言之,它们是:
让我更详细地阐述一下。
静态类是最容易理解的类,因为它们与包含类的实例无关。
静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其命名空间的衣架,例如,在包中声明为类 Rhino 的静态成员的类 Goat 以名称 pizza 而闻名。犀牛.山羊.
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 来限定。Rhino.Goat,但在包含类中,可以通过其简单名称来知道它。但是,内部类的每个实例都绑定到其包含类的特定实例:上面,在jerry中创建的Goat隐式地与jerry中的Rhino实例相关联。否则,我们在实例化 Goat 时显式显示关联的 Rhino 实例:
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(请注意,在奇怪的新语法中,您将内部类型称为Goat:Java从rhino部分推断出包含类型。而且,是的,新犀牛。Goat()对我来说也更有意义。
那么,这对我们有什么好处呢?好吧,内部类实例可以访问包含类实例的实例成员。这些封闭的实例成员在内部类中仅通过其简单名称引用,而不是通过 this(内部类中的这是指内部类实例,而不是关联的包含类实例):
public class Rhino {
private String barry;
public class Goat {
public void colin() {
System.out.println(barry);
}
}
}
在内部类中,您可以将包含类的这个引用为 Rhino.this,并且您可以使用它来引用其成员,例如 Rhino.this.barry。
本地内部类是在方法主体中声明的类。这样的类仅在其包含方法中是已知的,因此它只能被实例化并在其包含方法中访问其成员。好处是本地内部类实例绑定并可以访问其包含方法的最终局部变量。当实例使用其包含方法的最终局部变量时,该变量将保留它在创建实例时所持有的值,即使该变量已超出范围(这实际上是 Java 的粗略的有限版本的闭包)。
由于本地内部类既不是类或包的成员,因此不会使用访问级别声明它。(但是,要清楚的是,它自己的成员具有与普通类相同的访问级别。
如果在实例方法中声明了本地内部类,则内部类的实例化将绑定到创建实例时由包含方法的 this 所持有的实例,因此可以像在实例内部类中一样访问包含类的实例成员。本地内部类只需通过其名称实例化,例如,本地内部类 Cat 被实例化为新的 Cat(),而不是新的 Cat()。而不是新的。猫() 如你所料。
匿名内部类是编写本地内部类的一种语法上方便的方式。最常见的是,每次运行本地内部类的包含方法时,最多只实例化一次。那么,如果我们能够将本地内部类定义及其单个实例化组合成一种方便的语法形式,那就太好了,如果我们不必为类想出一个名称(代码包含的无用名称越少越好)。那就太好了。匿名内部类允许这两种情况:
new *ParentClassName*(*constructorArgs*) {*members*}
这是一个表达式,返回扩展 ParentClassName 的未命名类的新实例。您不能提供自己的构造函数;相反,一个是隐式提供的,它只是调用超构造函数,因此提供的参数必须适合超级构造函数。(如果父级包含多个构造函数,则“最简单”的构造函数称为“最简单”构造函数,由一组相当复杂的规则确定,不值得费心去详细学习 - 只需注意 NetBeans 或 Eclipse 告诉您的内容即可。
或者,您可以指定要实现的接口:
new *InterfaceName*() {*members*}
这样的声明创建了一个未命名类的新实例,该实例扩展了 Object 并实现了 InterfaceName。同样,您无法提供自己的构造函数;在这种情况下,Java 隐式提供了一个无 arg、无所事事的构造函数(因此在这种情况下永远不会有构造函数参数)。
即使不能为匿名内部类提供构造函数,您仍然可以使用初始值设定项块(放置在任何方法外部的 {} 块)执行所需的任何设置。
请注意,匿名内部类只是创建具有一个实例的本地内部类的一种不太灵活的方式。如果你想要一个实现多个接口的本地内部类,或者在扩展Object以外的某个类的同时实现接口,或者指定自己的构造函数,那么你就很难创建一个常规的命名的本地内部类。