嵌套类
Java编程语言允许你在另一个类中定义类,这样的类称为嵌套类,如下所示:
</>复制代码
class OuterClass {
...
class NestedClass {
...
}
}
</>复制代码
术语:嵌套类分为两类:静态和非静态,声明为static的嵌套类称为静态嵌套类,非静态嵌套类称为内部类。
</>复制代码
class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
嵌套类是其封闭类的成员,非静态嵌套类(内部类)可以访问封闭类的其他成员,即使它们被声明为private,静态嵌套类无权访问封闭类的其他成员。作为OuterClass的成员,可以将嵌套类声明为private、public、protected或包私有(回想一下,外部类只能声明为public或包私有)。
为什么使用嵌套类?使用嵌套类的令人信服的理由包括:
这是一种逻辑分组仅在一个地方使用的类的方法:如果一个类只对另一个类有用,那么将它嵌入该类并将两者保持在一起是合乎逻辑的,嵌套这样的“帮助类”使得它们的包更加简化。
它增加了封装:考虑两个顶级类A和B,其中B需要访问A的成员,否则这些成员将被声明为private,通过将类B隐藏在类A中,可以将A的成员声明为私有,并且B可以访问它们,另外,B本身也可以不被外界发现。
它可以使代码更具可读性和可维护性:在顶级类中嵌套小类会使代码更接近于使用它的位置。
静态嵌套类与类方法和变量一样,静态嵌套类与其外部类相关联,和静态类方法一样,静态嵌套类不能直接引用其封闭类中定义的实例变量或方法:它只能通过对象引用来使用它们。
</>复制代码
注意:静态嵌套类与其外部类(和其他类)的实例成员交互,就像任何其他顶级类一样,实际上,静态嵌套类在行为上是一个顶级类,它已嵌套在另一个顶级类中以方便打包。
使用封闭的类名访问静态嵌套类:
</>复制代码
OuterClass.StaticNestedClass
例如,要为静态嵌套类创建对象,请使用以下语法:
</>复制代码
OuterClass.StaticNestedClass nestedObject =
new OuterClass.StaticNestedClass();
内部类
与实例方法和变量一样,内部类与其封闭类的实例相关联,并且可以直接访问该对象的方法和字段,此外,由于内部类与实例相关联,因此本身无法定义任何静态成员。
作为内部类的实例的对象存在于外部类的实例中,考虑以下类:
</>复制代码
class OuterClass {
...
class InnerClass {
...
}
}
InnerClass的实例只能存在于OuterClass的实例中,并且可以直接访问其封闭实例的方法和字段。
要实例化内部类,必须首先实例化外部类,然后,使用以下语法在外部对象中创建内部对象:
</>复制代码
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
有两种特殊的内部类:局部类和匿名类。
遮蔽如果特定范围(例如内部类或方法定义)中的类型声明(例如成员变量或参数名称)与封闭范围中的另一个声明具有相同的名称,然后声明会影响封闭范围的声明,你不能仅通过其名称引用遮蔽的声明,以下示例ShadowTest演示了这一点:
</>复制代码
public class ShadowTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
System.out.println("x = " + x);
System.out.println("this.x = " + this.x);
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
}
}
public static void main(String... args) {
ShadowTest st = new ShadowTest();
ShadowTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);
}
}
以下是此示例的输出:
</>复制代码
x = 23
this.x = 1
ShadowTest.this.x = 0
此示例定义了三个名为x的变量:ShadowTest类的成员变量、内部类FirstLevel的成员变量、以及方法methodInFirstLevel中的参数。变量x定义为方法methodInFirstLevel的参数遮蔽内部类FirstLevel的变量,因此,当你在方法methodInFirstLevel中使用变量x时,它引用方法参数,要引用内部类FirstLevel的成员变量,请使用关键字this来表示封闭范围:
</>复制代码
System.out.println("this.x = " + this.x);
引用成员变量,这些成员变量根据所属的类名包含更大的作用域,例如,以下语句从方法methodInFirstLevel访问类ShadowTest的成员变量:
</>复制代码
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
序列化
强烈建议不要对内部类(包括局部类和匿名类)进行序列化,当Java编译器编译某些构造(如内部类)时,它会创建合成构造,这些是类、方法、字段和其他在源代码中没有相应构造的构造。合成构造使Java编译器能够在不更改JVM的情况下实现new的Java语言功能,但是,合成构造可以在不同的Java编译器实现之间变化,这意味着.class文件也可以在不同的实现之间变化。因此,如果序列化内部类,然后使用其他JRE实现反序列化,则可能存在兼容性问题,有关在编译内部类时生成的合成构造的更多信息,请参阅获取方法参数名称一节中的隐式和合成参数部分。
内部类示例要看内部类的使用,首先要考虑一个数组,在下面的示例中,你将创建一个数组,用整数值填充它,然后仅按升序输出数组的偶数索引值。
下面的DataStructure.java示例包括:
DataStructure外部类,它包含一个构造函数,用于创建DataStructure的实例,该实例包含一个数组,该数组填充了连续的整数值(0、1、2、3等等),以及一个方法,该方法打印具有偶数索引值的数组元素。
EvenIterator内部类,它实现了继承了Iterator< Integer>接口的DataStructureIterator接口,迭代器用于逐步执行数据结构,通常有方法来测试最后一个元素,检索当前元素,然后移动到下一个元素。
实例化DataStructure对象(ds)的main方法,然后调用printEven方法来打印具有偶数索引值的数组arrayOfInts的元素。
</>复制代码
public class DataStructure {
// Create an array
private final static int SIZE = 15;
private int[] arrayOfInts = new int[SIZE];
public DataStructure() {
// fill the array with ascending integer values
for (int i = 0; i < SIZE; i++) {
arrayOfInts[i] = i;
}
}
public void printEven() {
// Print out values of even indices of the array
DataStructureIterator iterator = this.new EvenIterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
}
interface DataStructureIterator extends java.util.Iterator { }
// Inner class implements the DataStructureIterator interface,
// which extends the Iterator interface
private class EvenIterator implements DataStructureIterator {
// Start stepping through the array from the beginning
private int nextIndex = 0;
public boolean hasNext() {
// Check if the current element is the last in the array
return (nextIndex <= SIZE - 1);
}
public Integer next() {
// Record a value of an even index of the array
Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
// Get the next even element
nextIndex += 2;
return retValue;
}
}
public static void main(String s[]) {
// Fill the array with integer values and print out only
// values of even indices
DataStructure ds = new DataStructure();
ds.printEven();
}
}
输出是:
</>复制代码
0 2 4 6 8 10 12 14
请注意,EvenIterator类直接引用DataStructure对象的arrayOfInts实例变量。
你可以使用内部类来实现辅助类,例如本示例中所示的类,要处理用户界面事件,你必须知道如何使用内部类,因为事件处理机制会广泛使用它们。
局部和匿名类还有两种类型的内部类,你可以在方法体内声明内部类,这些类称为局部类,你还可以在方法体内声明内部类,而无需命名该类,这些类称为匿名类。
修饰符你可以对用于外部类的其他成员的内部类使用相同的修饰符,例如,你可以使用访问修饰符private、public和protected来限制对内部类的访问,就像你使用它们来限制对其他类成员的访问一样。
上一篇:类的更多方面 下一篇:局部类文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/72337.html
使用包成员 组成包的类型称为包成员。 要从其包外部使用公共包成员,你必须执行以下操作之一: 通过其完全限定名称引用该成员 导入包成员 导入成员的整个包 每种情况都适用于不同的情况,如以下各节所述。 通过其完全限定名称引用包成员 到目前为止,本教程中的大多数示例都通过简单名称引用类型,例如Rectangle和StackOfInts,如果你编写的代码与该成员位于同一个包中,或者已导入该成员,则可以...
继承 在前面的课程中,你已经多次看到了继承,在Java语言中,类可以从其他类派生,从而从这些类继承字段和方法。 定义:从另一个类派生的类称为子类(也是派生类,扩展类或子类),派生子类的类称为超类(也是基类或父类)。 除了Object没有超类,每个类都有一个且只有一个直接超类(单继承),在没有任何其他显式超类的情况下,每个类都隐式地是Object的子类。 类可以从派生自类的类派生的类派生,依此类推,...
摘要:所以看出和两个的对应指针数一样,一个为一个为这就引出了变量的知识点,如手工画的图二为啥强制子类父类变量名不同阿里巴巴手册是这样写的强制避免在子父类的成员变量之间或者不同代码块的局部变量之间采用完全相同的命名方式,那会导致代码可读性降低。 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 父子...
Java™ 教程 Java教程是为JDK 8编写的,本页面中描述的示例和实践没有利用在后续版本中引入的改进。 Java教程是希望使用Java编程语言创建应用程序的程序员的实用指南,其中包括数百个完整的工作示例和数十个课程,相关课程组被组织成教程。 覆盖基础知识的路径 这些教程以书籍的形式提供,如Java教程,第六版,前往Amazon.com购买。 入门 介绍Java技术和安装Java开发软件并使用...
阅读 1011·2021-11-22 12:09
阅读 3789·2021-09-27 13:36
阅读 1482·2021-08-20 09:37
阅读 4163·2019-12-27 12:22
阅读 2432·2019-08-30 15:55
阅读 2435·2019-08-30 13:16
阅读 2910·2019-08-26 17:06
阅读 3507·2019-08-23 18:32