Java中内部类和静态嵌套类之间的主要区别是什么?设计/实施是否在选择其中之一时发挥作用?
28 回答
来自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() { ... }
是在静态上下文中定义的内部类,没有封闭实例。
Java教程说:
术语:嵌套类分为两类:静态和非静态。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。
通俗地说,术语“嵌套”和“内部”被大多数程序员互换使用,但我将使用正确的术语“嵌套类”,它涵盖了内部和静态。
类可以无限嵌套,例如 A 类可以包含 B 类,B 类包含 C 类,C 类包含 D 类等。但是,多于一级的类嵌套很少见,因为它通常是糟糕的设计。
创建嵌套类的原因有以下三个:
- 组织:有时将一个类分类到另一个类的命名空间中似乎是最明智的,尤其是当它不会在任何其他上下文中使用时
- 访问:嵌套类对其包含类的变量/字段具有特殊访问权限(确切地说,哪些变量/字段取决于嵌套类的类型,无论是内部类还是静态类)。
- 便利性:必须为每种新类型创建一个新文件很麻烦,尤其是当该类型仅在一个上下文中使用时
Java中有四种嵌套类。简而言之,它们是:
- 静态类:声明为另一个类的静态成员
- 内部类:声明为另一个类的实例成员
- 本地内部类:在另一个类的实例方法中声明
- 匿名内部类:类似于本地内部类,但写成返回一次性对象的表达式
让我详细说明一下。
静态类
静态类是最容易理解的,因为它们与包含类的实例无关。
静态类是声明为另一个类的静态成员的类。就像其他静态成员一样,这样的类实际上只是一个使用包含类作为其命名空间的衣架,例如,在包Pizza中声明为类 Rhino 的静态成员的Goat类称为Pizza.Rhino.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.Rhino.Goat,但在包含类内部,它可以通过其简单名称来识别。然而,内部类的每个实例都与它的包含类的一个特定实例相关联:上面,在jerry中创建的Goat隐式地与在jerry中的Rhino实例this相关联。否则,我们在实例化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 ,您可以使用this来引用其成员,例如 Rhino.this.barry。
本地内部类
本地内部类是在方法体中声明的类。这样的类仅在其包含方法中是已知的,因此只能在其包含方法中对其进行实例化并访问其成员。好处是本地内部类实例被绑定到并且可以访问其包含方法的最终局部变量。当实例使用其包含方法的 final 局部变量时,该变量将保留它在实例创建时所持有的值,即使该变量已超出范围(这实际上是 Java 粗略的、有限的闭包版本)。
因为本地内部类既不是类的成员也不是包的成员,所以它没有用访问级别声明。(但是要清楚,它自己的成员具有与普通类一样的访问级别。)
如果在实例方法中声明了本地内部类,则内部类的实例化与创建实例时包含方法的this所持有的实例相关联,因此包含类的实例成员可以像在实例中一样访问内部阶级。本地内部类仅通过其名称进行实例化,例如本地内部类Cat被实例化为new Cat(),而不是您可能期望的 new this.Cat() 。
匿名内部类
匿名内部类是编写本地内部类的一种语法方便的方法。最常见的是,每次运行其包含方法时,本地内部类最多只实例化一次。那么,如果我们可以将本地内部类定义和它的单个实例化组合成一种方便的语法形式,那就太好了,如果我们不必为类想一个名字(越少无用您的代码包含的名称越好)。匿名内部类允许以下两种情况:
new *ParentClassName*(*constructorArgs*) {*members*}
这是一个表达式,返回一个扩展ParentClassName的未命名类的新实例。您不能提供自己的构造函数;相反,一个是隐式提供的,它只是调用超级构造函数,所以提供的参数必须适合超级构造函数。(如果父级包含多个构造函数,则调用“最简单”的构造函数,“最简单”是由一组相当复杂的规则决定的,不值得费心去详细学习——只需注意 NetBeans 或 Eclipse 告诉你的内容。)
或者,您可以指定要实现的接口:
new *InterfaceName*() {*members*}
这样的声明创建了一个未命名类的新实例,它扩展了 Object 并实现了InterfaceName。同样,您不能提供自己的构造函数;在这种情况下,Java 隐式提供了一个无参数、什么都不做的构造函数(因此在这种情况下永远不会有构造函数参数)。
即使您不能为匿名内部类提供构造函数,您仍然可以使用初始化程序块(放置在任何方法之外的 {} 块)进行任何设置。
需要明确的是,匿名内部类只是用一个实例创建本地内部类的一种不太灵活的方式。如果您想要一个实现多个接口的本地内部类,或者在扩展Object以外的某个类或指定其自己的构造函数的同时实现接口,那么您将无法创建一个常规的命名本地内部类。
我认为上述答案中的真正区别并不明显。
首先要正确使用条款:
- 嵌套类是在源代码级别包含在另一个类中的类。
- 如果使用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 的封闭实例Container.this
。
更多硬核解释如下:
如果您查看编译器为(非静态)嵌套类生成的 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
。这是在构造函数中设置的,它有一个 Container 类型的附加参数来指定封闭实例。您在源代码中看不到此参数,但编译器为嵌套类隐式生成它。
马丁的例子
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
将被编译为类似(在字节码中)的调用
new InnerClass(outerObject)
为了完整起见:
匿名类是非静态嵌套类的完美示例,它没有与之关联的名称,以后也无法引用。
我认为以上答案都没有向您解释嵌套类和静态嵌套类在应用程序设计方面的真正区别:
概述
嵌套类可以是非静态的或静态的,并且在每种情况下都是在另一个类中定义的类。嵌套类应该只存在于服务封闭类,如果嵌套类对其他类有用(不仅是封闭类),则应将其声明为顶级类。
不同之处
非静态嵌套类:与包含类的封闭实例隐式关联,这意味着可以调用封闭实例的方法和访问变量。非静态嵌套类的一种常见用途是定义适配器类。
静态嵌套类:无法访问封闭类实例并在其上调用方法,因此应在嵌套类不需要访问封闭类实例时使用。静态嵌套类的一个常见用途是实现外部对象的组件。
结论
所以从设计的角度来看,两者的主要区别是:非静态嵌套类可以访问容器类的实例,而静态不能。
简单来说,我们需要嵌套类主要是因为 Java 不提供闭包。
嵌套类是在另一个封闭类的主体内定义的类。它们有两种类型——静态的和非静态的。
它们被视为封闭类的成员,因此您可以指定四个访问说明符中的任何一个 - private, package, protected, public
。对于只能声明public
或包私有的顶级类,我们没有这种奢侈。
内部类又名非堆栈类可以访问顶级类的其他成员,即使它们被声明为私有,而静态嵌套类无法访问顶级类的其他成员。
public class OuterClass {
public static class Inner1 {
}
public class Inner2 {
}
}
Inner1
是我们的静态内部类,并且Inner2
是我们的非静态内部类。它们之间的主要区别在于,您无法在Inner2
没有 Outer 的情况下创建实例,因为您可以独立创建Inner1
对象。
你什么时候使用内部类?
考虑一种情况,其中Class A
和Class B
相关,Class B
需要访问Class A
成员,并且Class B
仅与 相关Class A
。内部类进入图片。
要创建内部类的实例,您需要创建外部类的实例。
OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();
或者
OuterClass.Inner2 inner = new OuterClass().new Inner2();
你什么时候使用静态内部类?
当您知道静态内部类与封闭类/顶级类的实例没有任何关系时,您将定义它。如果你的内部类不使用外部类的方法或字段,那只是浪费空间,所以将其设为静态。
例如,要为静态嵌套类创建对象,请使用以下语法:
OuterClass.Inner1 nestedObject = new OuterClass.Inner1();
静态嵌套类的优点是它不需要包含类/顶级类的对象来工作。这可以帮助您减少应用程序在运行时创建的对象数量。
这是Java内部类和静态嵌套类之间的主要区别和相似之处。
希望能帮助到你!
内部类
- 可以访问外部类的实例和静态方法和字段
与封闭类的实例相关联,因此要实例化它首先需要一个外部类的实例(注意new关键字 place):
Outerclass.InnerClass innerObject = outerObject.new Innerclass();
不能定义任何静态成员本身
- 不能有类或接口声明
静态嵌套类
无法访问外部类实例方法或字段
不与封闭类的任何实例关联所以要实例化它:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
相似之处
- 两个内部类甚至可以访问外部类的私有字段和方法
- 外部类也可以访问内部类的私有字段和方法
- 两个类都可以有私有、受保护或公共访问修饰符
为什么使用嵌套类?
根据 Oracle 文档,有几个原因(完整文档):
这是一种对仅在一个地方使用的类进行逻辑分组的方法:如果一个类仅对另一个类有用,那么将其嵌入该类并将两者放在一起是合乎逻辑的。嵌套这样的“帮助类”使它们的包更加精简。
它增加了封装性:考虑两个顶级类 A 和 B,其中 B 需要访问 A 的成员,否则这些成员将被声明为私有。通过将类 B 隐藏在类 A 中,可以将 A 的成员声明为私有的,并且 B 可以访问它们。此外,B 本身可以对外界隐藏。
它可以导致代码更易读和更可维护:在顶级类中嵌套小类可以使代码更接近使用它的位置。
我认为,通常遵循的约定是这样的:
- 顶级类中的静态类是嵌套类
- 顶级类中的非静态类是内部类,它还有另外两种形式:
- 本地类- 在块内声明的命名类,如方法或构造函数主体
- 匿名类- 在表达式和语句中创建实例的未命名类
但是,要记住的其他几点是:
顶级类和静态嵌套类在语义上是相同的,只是在静态嵌套类的情况下,它可以对其外部 [父] 类的私有静态字段/方法进行静态引用,反之亦然。
内部类可以访问外部 [父] 类的封闭实例的实例变量。然而,并不是所有的内部类都有封闭的实例,例如静态上下文中的内部类,就像在静态初始化块中使用的匿名类一样,没有。
默认情况下,匿名类扩展父类或实现父接口,并且没有进一步的子句来扩展任何其他类或实现更多接口。所以,
new YourClass(){};
方法class [Anonymous] extends YourClass {}
new YourInterface(){};
方法class [Anonymous] implements YourInterface {}
我觉得更大的问题仍然悬而未决,使用哪个以及何时使用?好吧,这主要取决于您正在处理的情况,但阅读@jrudolph 给出的回复可能会帮助您做出一些决定。
嵌套类:类内类
类型:
- 静态嵌套类
- 非静态嵌套类[内部类]
不同之处:
非静态嵌套类[内部类]
在内部类的非静态嵌套类对象中存在于外部类的对象中。这样外部类的数据成员就可以被内部类访问。所以要创建内部类的对象,我们必须先创建外部类的对象。
outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass();
静态嵌套类
在内部类的静态嵌套类对象中不需要外部类的对象,因为“静态”一词表示不需要创建对象。
class outerclass A {
static class nestedclass B {
static int x = 10;
}
}
如果你想访问x,那么在里面写下面的方法
outerclass.nestedclass.x; i.e. System.out.prinltn( outerclass.nestedclass.x);
内部类的实例是在创建外部类的实例时创建的。因此,内部类的成员和方法可以访问外部类的实例(对象)的成员和方法。当外部类的实例超出范围时,内部类实例也不再存在。
静态嵌套类没有具体实例。它只是在第一次使用时加载(就像静态方法一样)。它是一个完全独立的实体,其方法和变量无法访问外部类的实例。
静态嵌套类不与外部对象耦合,它们速度更快,并且不占用堆/堆栈内存,因为不需要创建此类的实例。因此经验法则是尝试定义静态嵌套类,范围尽可能有限(private >= class >= protected >= public),然后将其转换为内部类(通过删除“静态”标识符)并放松范围,如果真的有必要的话。
嵌套静态类的使用有一个微妙之处,可能在某些情况下很有用。
虽然静态属性在类通过其构造函数实例化之前被实例化,但嵌套静态类内部的静态属性似乎直到类的构造函数被调用之后才被实例化,或者至少直到属性被首次引用之后才被实例化,即使它们被标记为“最终”。
考虑这个例子:
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");
}
}
即使 'nested' 和 'innerItem' 都被声明为 'static final'。nested.innerItem 的设置直到类被实例化之后才会发生(或者至少直到嵌套的静态项被第一次引用之后),你可以通过注释和取消注释我所指的行来亲自看到,多于。'outerItem' 的情况并非如此。
至少这是我在 Java 6.0 中看到的。
这些术语可以互换使用。如果你真的想学究气,那么你可以定义“嵌套类”来引用一个没有封闭实例的静态内部类。在代码中,你可能有这样的东西:
public class Outer {
public class Inner {}
public static class Nested {}
}
不过,这并不是一个被广泛接受的定义。
在创建实例的情况下,非静态内部类的实例是使用定义它的外部类的对象的引用来创建的。这意味着它有封闭的实例。但是静态内部类的实例是通过外部类的引用创建的,而不是外部类的对象的引用。这意味着它没有封闭实例。
例如:
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….
}
}
我认为这里没有太多要补充的,大多数答案都完美地解释了静态嵌套类和内部类之间的区别。但是,在使用嵌套类与内部类时,请考虑以下问题。正如在几个答案中提到的那样,如果没有其封闭类的实例,内部类就无法实例化,这意味着它们持有指向其封闭类实例的指针,这可能导致内存溢出或堆栈溢出异常,因为 GC即使不再使用它们,也无法对封闭类进行垃圾收集。为了清楚起见,请检查以下代码:
public class Outer {
public class Inner {
}
public Inner inner(){
return new Inner();
}
@Override
protected void finalize() throws Throwable {
// as you know finalize is called by the garbage collector due to destroying an object instance
System.out.println("I am destroyed !");
}
}
public static void main(String arg[]) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
// out instance is no more used and should be garbage collected !!!
// However this will not happen as inner instance is still alive i.e used, not null !
// and outer will be kept in memory until inner is destroyed
outer = null;
//
// inner = null;
//kick out garbage collector
System.gc();
}
如果您删除对// inner = null;
程序的评论,则会输出“我被摧毁! ”,但保留此评论不会。
原因是白色的内部实例仍然被引用,GC 无法收集它,因为它引用(有一个指向)外部实例,它也没有被收集。在您的项目中有足够的这些对象并且可能会耗尽内存。
与不指向内部类实例的静态内部类相比,因为它与实例无关,而是与类相关。上面的程序可以打印“我被摧毁了! ”如果你让内部类静态并用Outer.Inner i = new Outer.Inner();
嗯...内部类是嵌套类...您的意思是匿名类和内部类吗?
编辑:如果您实际上是指内部与匿名......内部类只是在类中定义的类,例如:
public class A {
public class B {
}
}
而匿名类是匿名定义的类的扩展,因此没有定义实际的“类”,如:
public class A {
}
A anon = new A() { /* you could change behavior of A here */ };
进一步编辑:
Wikipedia声称 Java 有所不同,但我已经使用 Java 8 年了,这是我第一次听到这样的区别......更不用说那里没有任何引用来支持这种说法......底部行,内部类是在类中定义的类(静态或非静态),而嵌套只是表示同一事物的另一个术语。
静态和非静态嵌套类之间存在细微差别......基本上非静态内部类可以隐式访问封闭类的实例字段和方法(因此它们不能在静态上下文中构造,它将是一个编译器错误)。另一方面,静态嵌套类没有对实例字段和方法的隐式访问,并且可以在静态上下文中构造。
嵌套类是一个非常笼统的术语:每个不是顶级的类都是嵌套类。内部类是非静态嵌套类。Joseph Darcy 写了一篇关于Nested、Inner、Member 和 Top-Level Classes的非常好的解释。
针对不熟悉 Java 和/或嵌套类的学习者
嵌套类可以是:
1. 静态嵌套类。
2.非静态嵌套类。(也称为内部类)=>请记住这一点
1.内部类
示例:
class OuterClass {
/* some code here...*/
class InnerClass { }
/* some code here...*/
}
内部类是嵌套类的子集:
- 内部类是特定类型的嵌套类
- 内部类是嵌套类的子集
- 你可以说一个内部类也是一个嵌套类,但你不能说一个嵌套类也是一个内部类。
内班特长:
- 内部类的实例可以访问外部类的所有成员,即使是那些标记为“私有”的成员</li>
2.静态嵌套类:
示例:
class EnclosingClass {
static class Nested {
void someMethod() { System.out.println("hello SO"); }
}
}
案例1:从非封闭类实例化静态嵌套类
class NonEnclosingClass {
public static void main(String[] args) {
/*instantiate the Nested class that is a static
member of the EnclosingClass class:
*/
EnclosingClass.Nested n = new EnclosingClass.Nested();
n.someMethod(); //prints out "hello"
}
}
案例 2:从封闭类实例化静态嵌套类
class EnclosingClass {
static class Nested {
void anotherMethod() { System.out.println("hi again"); }
}
public static void main(String[] args) {
//access enclosed class:
Nested n = new Nested();
n.anotherMethod(); //prints out "hi again"
}
}
静态类的特点:
- 静态内部类只能访问外部类的静态成员,不能访问非静态成员。
结论:
问题: Java中内部类和静态嵌套类的主要区别是什么?
答:只需浏览上述每个类的细节即可。
Java中的内部类和嵌套静态类都是在另一个类中声明的类,在Java中称为顶级类。在 Java 术语中,如果您声明一个嵌套类为静态的,则它在 Java 中将称为嵌套静态类,而非静态嵌套类简称为内部类。
Java中的内部类是什么?
任何不是顶级或在另一个类中声明的类都称为嵌套类,在这些嵌套类中,声明为非静态的类在 Java 中称为内部类。Java中有三种Inner类:
1) 本地内部类 - 在代码块或方法内声明。
2) 匿名内部类 - 是一个没有名称可以引用的类,并且在它被创建的同一位置初始化。
3) 成员内部类 - 被声明为外部类的非静态成员。
public class InnerClassTest {
public static void main(String args[]) {
//creating local inner class inside method i.e. main()
class Local {
public void name() {
System.out.println("Example of Local class in Java");
}
}
//creating instance of local inner class
Local local = new Local();
local.name(); //calling method from local inner class
//Creating anonymous inner class in Java for implementing thread
Thread anonymous = new Thread(){
@Override
public void run(){
System.out.println("Anonymous class example in java");
}
};
anonymous.start();
//example of creating instance of inner class
InnerClassTest test = new InnerClassTest();
InnerClassTest.Inner inner = test.new Inner();
inner.name(); //calling method of inner class
}
//Creating Inner class in Java
private class Inner{
public void name(){
System.out.println("Inner class example in java");
}
}
}
Java中的嵌套静态类是什么?
嵌套静态类是另一个类,它在类中声明为成员并设为静态。嵌套的静态类也被声明为外部类的成员,并且可以像任何其他成员一样设为私有、公共或受保护。嵌套静态类相对于内部类的主要好处之一是嵌套静态类的实例不附加到外部类的任何封闭实例。您也不需要任何 Outer 类的实例来在 Java 中创建嵌套静态类的实例。
1)它可以访问外部类的静态数据成员,包括私有的。
2)静态嵌套类不能访问非静态(实例)数据成员或方法。
public class NestedStaticExample {
public static void main(String args[]){
StaticNested nested = new StaticNested();
nested.name();
}
//static nested class in java
private static class StaticNested{
public void name(){
System.out.println("static nested class example in java");
}
}
}
我认为以上答案都没有为您提供嵌套类和静态嵌套类在应用程序设计方面的区别的真实示例。静态嵌套类和内部类之间的主要区别在于访问外部类实例字段的能力。
让我们看看下面的两个例子。
静态嵌套类:使用静态嵌套类的一个很好的例子是构建器模式(https://dzone.com/articles/design-patterns-the-builder-pattern)。
对于 BankAccount 我们使用静态嵌套类,主要是因为
静态嵌套类实例可以在外部类之前创建。
在构建器模式中,构建器是一个帮助类,用于创建 BankAccount。
- BankAccount.Builder 仅与 BankAccount 相关联。没有其他类与 BankAccount.Builder 相关。所以最好在不使用名称约定的情况下将它们组织在一起。
public class BankAccount {
private long accountNumber;
private String owner;
...
public static class Builder {
private long accountNumber;
private String owner;
...
static public Builder(long accountNumber) {
this.accountNumber = accountNumber;
}
public Builder withOwner(String owner){
this.owner = owner;
return this;
}
...
public BankAccount build(){
BankAccount account = new BankAccount();
account.accountNumber = this.accountNumber;
account.owner = this.owner;
...
return account;
}
}
}
内部类:内部类的一个常见用途是定义一个事件处理程序。 https://docs.oracle.com/javase/tutorial/uiswing/events/generalrules.html
对于MyClass,我们使用内部类,主要是因为:
内部类 MyAdapter 需要访问外部类成员。
在示例中,MyAdapter 仅与 MyClass 关联。没有其他类与 MyAdapter 相关。所以最好在不使用名称约定的情况下将它们组织在一起
public class MyClass extends Applet {
...
someObject.addMouseListener(new MyAdapter());
...
class MyAdapter extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
...// Event listener implementation goes here...
...// change some outer class instance property depend on the event
}
}
}
我认为这里的人们应该注意到海报:静态嵌套类只是第一个内部类。例如:
public static class A {} //ERROR
public class A {
public class B {
public static class C {} //ERROR
}
}
public class A {
public static class B {} //COMPILE !!!
}
因此,总而言之,静态类不依赖于它包含的类。所以,他们不能在正常的课堂上。(因为普通类需要一个实例)。
当我们在类内部声明静态成员类时,它被称为顶级嵌套类或静态嵌套类。可以证明如下:
class Test{
private static int x = 1;
static class A{
private static int y = 2;
public static int getZ(){
return B.z+x;
}
}
static class B{
private static int z = 3;
public static int getY(){
return A.y;
}
}
}
class TestDemo{
public static void main(String[] args){
Test t = new Test();
System.out.println(Test.A.getZ());
System.out.println(Test.B.getY());
}
}
当我们在一个类中声明非静态成员类时,它被称为内部类。内部类可以演示如下:
class Test{
private int i = 10;
class A{
private int i =20;
void display(){
int i = 30;
System.out.println(i);
System.out.println(this.i);
System.out.println(Test.this.i);
}
}
}
以下是static nested class
和的示例inner class
:
外部类.java
public class OuterClass {
private String someVariable = "Non Static";
private static String anotherStaticVariable = "Static";
OuterClass(){
}
//Nested classes are static
static class StaticNestedClass{
private static String privateStaticNestedClassVariable = "Private Static Nested Class Variable";
//can access private variables declared in the outer class
public static void getPrivateVariableofOuterClass(){
System.out.println(anotherStaticVariable);
}
}
//non static
class InnerClass{
//can access private variables of outer class
public String getPrivateNonStaticVariableOfOuterClass(){
return someVariable;
}
}
public static void accessStaticClass(){
//can access any variable declared inside the Static Nested Class
//even if it private
String var = OuterClass.StaticNestedClass.privateStaticNestedClassVariable;
System.out.println(var);
}
}
外部类测试:
public class OuterClassTest {
public static void main(String[] args) {
//access the Static Nested Class
OuterClass.StaticNestedClass.getPrivateVariableofOuterClass();
//test the private variable declared inside the static nested class
OuterClass.accessStaticClass();
/*
* Inner Class Test
* */
//Declaration
//first instantiate the outer class
OuterClass outerClass = new OuterClass();
//then instantiate the inner class
OuterClass.InnerClass innerClassExample = outerClass. new InnerClass();
//test the non static private variable
System.out.println(innerClassExample.getPrivateNonStaticVariableOfOuterClass());
}
}
首先,没有这样的类称为静态类。与内部类(称为嵌套类)一起使用的静态修饰符表示它是外部类的静态成员,这意味着我们可以像访问其他静态成员一样访问它,而无需任何外部类的实例。(这本来是静态的好处。)
使用嵌套类和常规内部类之间的区别是:
OuterClass.InnerClass inner = new OuterClass().new InnerClass();
首先我们可以实例化 Outerclass 然后我们可以访问 Inner。
但是如果 Class 是嵌套的,那么语法是:
OuterClass.InnerClass inner = new OuterClass.InnerClass();
它使用静态语法作为静态关键字的正常实现。
Java 编程语言允许您在另一个类中定义一个类。这样的类称为嵌套类,如下所示:
class OuterClass {
...
class NestedClass {
...
}
}
嵌套类分为两类:静态和非静态。声明为静态的嵌套类称为静态嵌套类。非静态嵌套类称为内部类。我们应该记住的一件事是非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为私有。静态嵌套类只能访问封闭类的其他成员(如果它们是静态的)。它不能访问外部类的非静态成员。与类方法和变量一样,静态嵌套类与其外部类相关联。例如,要为静态嵌套类创建对象,请使用以下语法:
OuterClass.StaticNestedClass nestedObject =
new OuterClass.StaticNestedClass();
要实例化内部类,您必须先实例化外部类。然后,使用以下语法在外部对象中创建内部对象:
OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();
为什么我们使用嵌套类
- 这是一种对仅在一个地方使用的类进行逻辑分组的方法。
- 它增加了封装。
- 它可以导致更可读和可维护的代码。
资料来源:Java™ 教程 - 嵌套类
除了已经提到的那些,嵌套类的另一个用例是嵌套类具有只能从外部类访问的方法。这是可能的,因为外部类可以访问嵌套类的私有构造函数、字段和方法。
在下面的示例中,Bank
可以发出具有私有构造函数的 a ,并且可以使用 的私有实例方法Bank.CreditCard
根据当前银行政策更改信用卡的限额。(在这种情况下,对实例变量的直接字段访问也可以使用)。从任何其他类只能访问公共方法。setLimit(...)
Bank.CreditCard
limit
Bank.CreditCard
public class Bank {
// maximum limit as per current bank policy
// is subject to change
private int maxLimit = 7000;
// ------- PUBLIC METHODS ---------
public CreditCard issueCard(
final String firstName,
final String lastName
) {
final String number = this.generateNumber();
final int expiryDate = this.generateExpiryDate();
final int CVV = this.generateCVV();
return new CreditCard(firstName, lastName, number, expiryDate, CVV);
}
public boolean setLimit(
final CreditCard creditCard,
final int limit
) {
if (limit <= this.maxLimit) { // check against current bank policy limit
creditCard.setLimit(limit); // access private method Bank.CreditCard.setLimit(int)
return true;
}
return false;
}
// ------- PRIVATE METHODS ---------
private String generateNumber() {
return "1234-5678-9101-1123"; // the numbers should be unique for each card
}
private int generateExpiryDate() {
return 202405; // date is YYYY=2024, MM=05
}
private int generateCVV() {
return 123; // is in real-life less predictable
}
// ------- PUBLIC STATIC NESTED CLASS ---------
public static final class CreditCard {
private final String firstName;
private final String lastName;
private final String number;
private final int expiryDate;
private final int CVV;
private int balance;
private int limit = 100; // default limit
// the constructor is final but is accessible from outer class
private CreditCard(
final String firstName,
final String lastName,
final String number,
final int expiryDate,
final int CVV
) {
this.firstName = firstName;
this.lastName = lastName;
this.number = number;
this.expiryDate = expiryDate;
this.CVV = CVV;
}
// ------- PUBLIC METHODS ---------
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public String getNumber() {
return this.number;
}
public int getExpiryDate() {
return this.expiryDate;
}
// returns true if financial transaction is successful
// otherwise false
public boolean charge(final int amount) {
final int newBalance = this.balance - amount;
if (newBalance < -this.limit) {
return false;
}
this.balance = newBalance;
return true;
}
// ------- PRIVATE METHODS ---------
private int getCVV() {
return this.CVV;
}
private int getBalance() {
return this.balance;
}
private void setBalance(final int balance) {
this.balance = balance;
}
private int getLimit() {
return limit;
}
private void setLimit(final int limit) {
this.limit = limit;
}
}
}
静态嵌套类访问它们定义的类的 PRIVATE 类级静态变量。从体系结构的角度来看,这可能是巨大的(即服务定位器模式在服务中使用嵌套静态帮助器类),并且可以帮助 OP 了解它们为什么存在内部类。
不同之处在于,同样是静态的嵌套类声明可以在封闭类之外实例化。
当您有一个非静态的嵌套类声明(也称为内部类)时,Java 不会让您实例化它,除非通过封闭类。从内部类创建的对象链接到从外部类创建的对象,因此内部类可以引用外部类的字段。
但如果它是静态的,则链接不存在,无法访问外部字段(除了像任何其他对象一样通过普通引用),因此您可以自己实例化嵌套类。
我已经说明了 Java 代码中可能发生的各种可能的正确和错误场景。
class Outter1 {
String OutStr;
Outter1(String str) {
OutStr = str;
}
public void NonStaticMethod(String st) {
String temp1 = "ashish";
final String tempFinal1 = "ashish";
// below static attribute not permitted
// static String tempStatic1 = "static";
// below static with final attribute not permitted
// static final String tempStatic1 = "ashish";
// synchronized keyword is not permitted below
class localInnerNonStatic1 {
synchronized public void innerMethod(String str11) {
str11 = temp1 +" sharma";
System.out.println("innerMethod ===> "+str11);
}
/*
// static method with final not permitted
public static void innerStaticMethod(String str11) {
str11 = temp1 +" india";
System.out.println("innerMethod ===> "+str11);
}*/
}
// static class not permitted below
// static class localInnerStatic1 { }
}
public static void StaticMethod(String st) {
String temp1 = "ashish";
final String tempFinal1 = "ashish";
// static attribute not permitted below
//static String tempStatic1 = "static";
// static with final attribute not permitted below
// static final String tempStatic1 = "ashish";
class localInnerNonStatic1 {
public void innerMethod(String str11) {
str11 = temp1 +" sharma";
System.out.println("innerMethod ===> "+str11);
}
/*
// static method with final not permitted
public static void innerStaticMethod(String str11) {
str11 = temp1 +" india";
System.out.println("innerMethod ===> "+str11);
}*/
}
// static class not permitted below
// static class localInnerStatic1 { }
}
// synchronized keyword is not permitted
static class inner1 {
static String temp1 = "ashish";
String tempNonStatic = "ashish";
// class localInner1 {
public void innerMethod(String str11) {
str11 = temp1 +" sharma";
str11 = str11+ tempNonStatic +" sharma";
System.out.println("innerMethod ===> "+str11);
}
public static void innerStaticMethod(String str11) {
// error in below step
str11 = temp1 +" india";
//str11 = str11+ tempNonStatic +" sharma";
System.out.println("innerMethod ===> "+str11);
}
//}
}
//synchronized keyword is not permitted below
class innerNonStatic1 {
//This is important we have to keep final with static modifier in non
// static innerclass below
static final String temp1 = "ashish";
String tempNonStatic = "ashish";
// class localInner1 {
synchronized public void innerMethod(String str11) {
tempNonStatic = tempNonStatic +" ...";
str11 = temp1 +" sharma";
str11 = str11+ tempNonStatic +" sharma";
System.out.println("innerMethod ===> "+str11);
}
/*
// error in below step
public static void innerStaticMethod(String str11) {
// error in below step
// str11 = tempNonStatic +" india";
str11 = temp1 +" india";
System.out.println("innerMethod ===> "+str11);
}*/
//}
}
}