【Java面向对象】5-11 内部类
§5-11 内部类
本节内容将了解内部类的不同写法。
5-11.1 什么是内部类
内部类就是在一个类的内部定义一个类。
例如,在类 A
中定义类 B
,那么,对于类 B
而言,A
是它的外部类;对于类 A
而言,B
是它的内部类。
内部类可简单地划分为以下几种:
- 成员内部类;
- 静态内部类;
- 局部内部类;
- 匿名内部类;
内部类特点:
-
编译之后可生成独立的字节码文件;
例如:类
Outer
中的内部类Inner
会在编译后生成Outer$Inner.class
文件; -
内部类可直接访问外部类的私有成员,而不破坏其封装性;
-
可为外部类提供必要的内部功能组件;
下文将简单地介绍这些内部类。
5-11.2 成员内部类
成员内部类:在类的内部定义,与实例变量、实例方法同级别的类。
示例:
package com.zebt.oop.internal;
public class Outer {
//外部类的属性
private String name = "张三";
private int age = 20;
//成员内部类。为保证可在外部访问、实例化,将访问限定符设置为public
public class Inner {
//内部类属性
private String location = "北京";
private int tel = 100;
//内部类访问外部类的私有属性
public void show() {
System.out.println(name);
System.out.println(age);
System.out.println(location);
System.out.println(tel);
}
}
}
成员内部类属于外部类的一个实例部分,要想创建内部类对象,必须要依赖外部类对象。
package com.zebt.oop;
import com.zebt.oop.internal.Outer;
public class Main {
public static void main(String[] args) {
//实例化外部类
Outer outer = new Outer();
//通过外部类实例化内部类
Outer.Inner inner = outer.new Inner();
//若还导入了com.zebt.oop.internal.Outer.Inner; 则可以写成 Inner inner = outer.new Inner();
//调用内部类方法
inner.show();
}
}
运行,得到
内部类方法。
张三
20
北京
100
另外,还有一种 “一步到位” 的创建方法:
Outer.Inner inner = new Outer().new Inner(); //仅导入 Outer
Inner inner = new Outer().new Inner(); //导入 Inner
当内部类和外部类拥有重名变量时,优先访问内部类变量。若要访问外部类变量,应当使用 外部类类名.this.变量名
。
示例:
package com.zebt.oop.internal;
public class Outer {
private String name = "张三";
public class Inner {
private String name = "李四";
public void show() {
System.out.println(name);
System.out.println(Outer.this.name); //访问外部类的属性时,实际上就是通过 外部类类名.this.变量名访问
}
}
}
在测试类中调用 show()
:
import com.zebt.oop.internal.Outer;
import com.zebt.oop.internal.Outer.Inner;
public class Main {
public static void main(String[] args) {
Inner inner = new Outer().new Inner();
inner.show();
}
}
得到:
李四
张三
20
北京
100
注意:成员内部类不允许定义静态成员,但是可以包含静态常量。
//内部类中
private static String country = "中国"; //不允许:不支持内部类中的静态声明
private static final String country = "中国"; //允许
5-11.3 静态内部类
在内部类 Inner
前使用 static
关键字修饰,即可将该成员类声明为静态内部类,其级别和外部类相同。
静态内部类不依赖外部类对象,可以直接创建或通过类名访问,可声明静态成员。
示例:
package com.zebt.oop.internal;
public class Outer {
private String name = "张三";
private int age = 20;
public static class Inner {
private String location = "上海";
private int tel = 200;
private static int count = 100;
public void show() {
//通过实例化外部类对象访问外部类属性
Outer outer = new Outer();
System.out.println(outer.name);
System.out.println(outer.age);
//直接访问内部类属性和方法
System.out.println(this.location);
System.out.println(this.tel);
//访问静态内部类静态属性
System.out.println(Inner.count);
}
}
}
在测试类中,可以直接创建静态内部类对象
package com.zebt.oop;
import com.zebt.oop.internal.Outer;
public class Main {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.show();
}
}
得到:
张三
20
上海
200
10
注意:
- 只有成员内部类可以由
static
关键字修饰,变为静态内部类; - 静态内部类访问外部类的成员属性,需要通过外部类对象访问。
5-11.4 局部内部类
和局部变量的概念类似,定义在外部类的方法内部的类称为局部内部类,其作用范围和创建对象范围仅限于当前方法。
由于其范围限制在方法内部,局部内部类不可以由任何访问修饰符修饰。
package com.zebt.oop.internal;
public class Outer {
private String name = "张三";
private int age = 20;
//外部类中的方法
public void show() {
String location = "深圳";
//局部内部类
class Inner {
private String email = "Zhangsan@qq.com";
private String qq = "10000000";
public void show2() {
//调用外部类属性
System.out.println(Outer.this.name);
System.out.println(Outer.this.age);
//调用内部类属性
System.out.println(this.email);
System.out.println(this.qq);
//调用局部变量
//JDK 1.7 及以前,局部内部类访问局部变量,局部变量必须为常量;1.8 以后,自动为常量 final
System.out.println(location);
}
}
//在方法中创建局部内部类对象
Inner inner = new PtInner();
inner.show2();
}
}
要想实例化局部内部类,其范围仅限其所在方法中。因此,在测试类中,必须通过外部类调用该方法实现。
package com.zebt.oop;
import com.zebt.oop.internal.Outer;
public class Main {
public static void main(String[] args) {
//创建外部类对象
Outer outer = new outer();
outer.show();
}
}
运行,得到
张三
20
Zhangsan@qq.com
10000000
深圳
注意:
- 局部内部类访问外部类当前方法中的局部变量时,由于无法保证变量的生命周期与自身相同,变量必须修饰为
final
; - 局部内部类中不可包含静态变量,但可以包含静态常量(
static final
); - 若外部类方法属于静态方法,局部内部类访问外部类属性时,应当通过实例化外部类对象访问;
- 局部内部类的使用受限,仅在当前方法中可用。
5-11.5 匿名内部类
匿名内部类:没有类名的局部内部类,一切特征都与局部内部类相同。
匿名内部类必须继承一个父类或实现一个接口。
示例:
我们首先创建一个接口:
public interface Usb {
//接口中的方法
void service();
}
然后,编写一个实现类,实现 Usb
,并重写方法:
public class Mouse implements Usb {
@Override
public void service() {
System.out.println("连接成功,鼠标开始工作。");
}
}
接着,在测试类中,利用多态,创建一个接口类对象:
public class UsbTest {
public static void main(String[] args) {
//使用传统方法:多态 + 局部内部类,创建一个接口类对象
Usb usb = new Mouse(); //接口本身没有构造器,不允许创建接口类对象;但可以利用多态创建实现类对象
usb.service(); //调用实现类中的重写方法
System.out.println("==================");
}
}
运行,得到
连接成功,鼠标开始工作。
==================
改变实现方法,使用局部内部类,在 Main()
方法中使用局部内部类实现 Usb
接口,并重写方法:
public class UsbTest {
public static void main(String[] args) {
//使用局部内部类(非匿名)
class Fan implements Usb {
//重写方法
@Override
public void service() {
System.out.println("连接成功,风扇开始工作。");
}
}
Usb usb2 = new Fan();
usb2.service(); //调用实现类中的重写方法
System.out.println("==================");
}
}
运行,得到
连接成功,风扇开始工作。
==================
如果该局部内部类只使用一次,可以使用匿名内部类优化:
public class UsbTest {
public static void main(String[] args) {
//使用匿名内部类优化
Usb usb3 = new Usb() {
@Override
public void service() {
System.out.println("连接成功,USB接口开始工作。");
}
};
usb3.service();
System.out.println("==================");
}
}
运行,得到:
连接成功,USB接口开始工作。
==================
注意:
-
匿名内部类没有名字,一切特征与局部内部类相同;
-
匿名内部类必须继承一个父类,或者实现一个接口;
-
定义类、实现类、创建对象的语法合并,只能创建一个该类对象;
-
优点:减少代码量;缺点:可读性较差;
-
实际上,该匿名内部类也是有名字的(由编译器命名);
例如:上述的
usb3
所创建的匿名内部类,编译后产生字节码文件UsbTest$1.class
。