目标
什么是内部类
内部类的分类和作用
内部类如何定义
如何实例化以及各自的特点
要注意区分不同类型内部类的异同
为什么需要内部类
1. 概念
在 Java 语言中,可以将一个类定义在另一个类里面或者一个方法里面,我们把这样的类称为内部类。
与之对应的,包含内部类的类被称为外部类。请阅读下面的代码:
// 外部类 Car public class Car { // 内部类 Engine class Engine { private String innerName = "发动机内部类"; } }
代码中,Engine 就是内部类,而 Sky 就是外部类。
2. 分类
Java 中的内部类可以分为 4 种:
成员内部类
静态内部类
方法内部
匿名内部类
2.1 成员内部类
2.1.1 定义
成员内部类也称为普通内部类,它是最常见的内部类。可以将其看作外部类的一个成员。在成员内部类中无法声明静态成员。
如下代码中声明了一个成员内部类:
// 外部类 Car public class Car { // 内部类 Engine private class Engine { private void run() { System.out.println("发动机启动了!"); } } }
我们在外部类 Sky 的内部定义了一个成员内部类 Engine,在 Engine 下面有一个 fly() 方法,其功能是打印输出一行字符串:“发动机启动了!”。
另外,需要注意的是,与普通的 Java 类不同,含有内部类的类被编译器编译后,会生成两个独立的字节码文件:
Car$Engine.class Car.class
内部类 Engine 会另外生成一个字节码文件,其文件名为:外部类类名 $ 内部类类名.class。
2.1.2 实例化
内部类在外部使用时,无法直接实例化,需要借助外部类才能完成实例化操作。关于成员内部类的实例化,有 3 种方法:
我们可以通过 new 外部类().new 内部类() 的方式获取内部类的实例对象:
实例演示
// 外部类 Car public class Car { // 内部类 Engine private class Engine { private void run() { System.out.println("发动机启动了!"); } } public static void main(String[] args) { // 1.实例化外部类后紧接着实例化内部类 Engine engine = new Car().new Engine(); // 2.调用内部类的方法 engine.run(); } }
运行结果:
发动机启动了!
我们可通过先实例化外部类、再实例化内部类的方法获取内部类的对象实例:
public static void main(String[] args) { // 1.实例化外部类 Car car = new Car(); // 2.通过外部类实例对象再实例化内部类 Engine engine = car.new Engine(); // 3.调用内部类的方法 engine.run(); }
编译执行,成功调用了内部类的 fly () 方法:
$javac Car.java java Car 发动机启动了!
我们也可以在外部类中定义一个获取内部类的方法 getEngine(),然后通过外部类的实例对象调用这个方法来获取内部类的实例:
实例演示
package com.caq.oop.demo10; public class Outer { private int id = 10; public void out(){ System.out.println("这是外部类的方法"); } //一个java类中可以有多个class类,但只能有一个public class public class In { public void in() { System.out.println("这是内部类的方法"); } } public static void main(String[] args) { //实例化内部类,调用内部类的方法 In In = new Outer().new In(); In.in(); } }
运行结果:
这是内部类的方法
这种设计在是非常常见的,同样可以成功调用执行 fly() 方法。
2.1.2.1 成员的访问
成员内部类可以直接访问外部类的成员,例如,可以在内部类的中访问外部类的成员属性:
实例演示
// 外部类 Sky public class Sky { String name; public Engine getEngine() { return new Engine(); } // 内部类 Engine private class Engine { // 发动机的起动方法 private void fly() { System.out.println(name + "的发动机启动了!"); } } public static void main(String[] args) { // 实例化外部类 Sky Sky = new Sky(); // 为实例属性赋值 Sky.name = "张三"; // 获取内部类实例 Engine engine = Sky.getEngine(); // 调用内部类的方法 engine.fly(); } }
观察 Engine 的 fly() 方法,调用了外部类的成员属性 name,我们在主方法实例化 Sky 后,已经为属性 name 赋值。
运行结果:
大奔奔的发动机启动了!
相同的,除了成员属性,成员方法也可以自由访问。这里不再赘述。
还存在一个同名成员的问题:如果内部类中也存在一个同名成员,那么优先访问内部类的成员。可理解为就近原则。
这种情况下如果依然希望访问外部类的属性,可以使用外部类名.this.成员的方式,例如:
实例演示
// 外部类 Sky public class Sky { String name; public Engine getEngine() { return new Engine(); } // 汽车的跑动方法 public void fly(String name) { System.out.println(name + "跑起来了!"); } // 内部类 Engine private class Engine { private String name = "引擎"; // 发动机的起动方法 private void fly() { System.out.println("Engine中的成员属性name=" + name); System.out.println(Sky.this.name + "的发动机启动了!"); Sky.this.fly(Sky.this.name); } } public static void main(String[] args) { // 实例化外部类 Sky Sky = new Sky(); // 为实例属性赋值 Sky.name = "张三"; // 获取内部类实例 Engine engine = Sky.getEngine(); // 调用内部类的方法 engine.fly(); } }
运行结果:
Engine中的成员属性name=引擎 张三的发动机启动了! 大奔奔跑起来了!
请观察内部类 fly() 方法中的语句:第一行语句调用了内部类自己的属性 name,而第二行调用了外部类 Sky 的属性 name,第三行调用了外部类的方法 fly(),并将外部类的属性 name 作为方法的参数。