内部类
在Java程序中,通常情况下,我们把不同的类组织在不同的包下面,对于一个包下面的类来说,它们是在同一层次,没有父子关系:
java.lang
├── Math
├── Runnable
├── String
└── ...
还有一种类,它被定义在另一个类的内部,所以称为内部类(Nested Class)。Java的内部类分为好几种,通常情况用得不多,但也需要了解它们是如何使用的。
Inner Class
如果一个类定义在另一个类的内部,这个类就是Inner Class:
class Outer {
class Inner {
// 定义了一个Inner Class
}
}
上述定义的Outer
是一个普通类,而Inner
是一个Inner Class,它与普通类有个最大的不同,就是Inner Class的实例不能单独存在,必须依附于一个Outer Class的实例。示例代码如下:
// inner class
----
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested"); // 实例化一个Outer
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
inner.hello();
}
}
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
class Inner {
void hello() {
System.out.println("Hello, " + Outer.this.name);
}
}
}
观察上述代码,要实例化一个Inner
,我们必须首先创建一个Outer
的实例,然后,调用Outer
实例的new
来创建Inner
实例:
Outer.Inner inner = outer.new Inner();
这是因为Inner Class除了有一个this
指向它自己,还隐含地持有一个Outer Class实例,可以用Outer.this
访问这个实例。所以,实例化一个Inner Class不能脱离Outer实例。
Inner Class和普通Class相比,除了能引用Outer实例外,还有一个额外的“特权”,就是可以修改Outer Class的private
字段,因为Inner Class的作用域在Outer Class内部,所以能访问Outer Class的private
字段和方法。
观察Java编译器编译后的.class
文件可以发现,Outer
类被编译为Outer.class
,而Inner
类被编译为Outer$Inner.class
。
Anonymous Class
还有一种定义Inner Class的方法,它不需要在Outer Class中明确地定义这个Class,而是在方法内部,通过匿名类(Anonymous Class)来定义。示例代码如下:
// Anonymous Class
----
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested");
outer.asyncHello();
}
}
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
void asyncHello() {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start();
}
}
观察asyncHello()
方法,我们在方法内部实例化了一个Runnable
。Runnable
本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了Runnable
接口的匿名类,并且通过new
实例化该匿名类,然后转型为Runnable
。在定义匿名类的时候就必须实例化它,定义匿名类的写法如下:
Runnable r = new Runnable() {
// 实现必要的抽象方法...
};
匿名类和Inner Class一样,可以访问Outer Class的private
字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心类名,比直接定义Inner Class可以少写很多代码。
观察Java编译器编译后的.class
文件可以发现,Outer
类被编译为Outer.class
,而匿名类被编译为Outer$1.class
。如果有多个匿名类,Java编译器会将每个匿名类依次命名为Outer$1
、Outer$2
、Outer$3
……
除了接口外,匿名类也完全可以继承自普通类。观察以下代码:
// Anonymous Class
----
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map1 = new HashMap<>();
HashMap<String, String> map2 = new HashMap<>() {}; // 匿名类!
HashMap<String, String> map3 = new HashMap<>() {
{
put("A", "1");
put("B", "2");
}
};
System.out.println(map3.get("A"));
}
}
map1
是一个普通的HashMap
实例,但map2
是一个匿名类实例,只是该匿名类继承自HashMap
。map3
也是一个继承自HashMap
的匿名类实例,并且添加了static
代码块来初始化数据。观察编译输出可发现Main$1.class
和Main$2.class
两个匿名类文件。
Static Nested Class
最后一种内部类和Inner Class类似,但是使用static
修饰,称为静态内部类(Static Nested Class):
// Static Nested Class
----
public class Main {
public static void main(String[] args) {
Outer.StaticNested sn = new Outer.StaticNested();
sn.hello();
}
}
class Outer {
private static String NAME = "OUTER";
private String name;
Outer(String name) {
this.name = name;
}
static class StaticNested {
void hello() {
System.out.println("Hello, " + Outer.NAME);
}
}
}
用static
修饰的内部类和Inner Class有很大的不同,它不再依附于Outer
的实例,而是一个完全独立的类,因此无法引用Outer.this
,但它可以访问Outer
的private
静态字段和静态方法。如果把StaticNested
移到Outer
之外,就失去了访问private
的权限。
小结
Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种:
-
Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有
Outer.this
实例,并拥有Outer Class的private
访问权限; -
Static Nested Class是独立类,但拥有Outer Class的
private
访问权限。
相关文章
- 视频 | ZYNQ开发板深度评测:高性能FPGA和双核ARM的强强联合!
- I²C协议官方标准文档2021最新版本下载
- 国产FPGA开发板上手体验:不足百元,集成ARM硬核处理器!
- 业内首发!感芯MC3172硬实时RISC-V芯片,还用啥RTOS!
- 从汇率转换通用解决方案到可复用设计思想
- 顶流选手专访 - 最具推广价值作品 - 背后故事
- 顶流选手专访 - 最佳展现创意作品 - 背后故事
- 【精华】顶流选手专访-最佳可视化展现作品(冠军)
- 冠军作品背后的故事会是什么呢
- IBM 人力资源综合分析案例
- 世界五百强财务高管数字化战群雄经典案例
- ZebraBI 6.0 发布,更高更快更强大
- DAX 引擎之父揭秘 DAX 引擎内部细节
- 脑图PPT - 让大脑高效流淌内容
- 融合最佳展现创意的企业智慧经营分析看板
- ArcPy将HDF格式栅格文件批量转为TIFF格式
- 用AvaSpec 2048便携式光谱仪测定地物高光谱曲线
- ArcPy依据成像时间分别批量拼接不同时相的遥感影像
- ArcMap实现栅格遥感影像监督分类
- ENVI实现基于像元的遥感影像镶嵌拼接