06-四种内部类

1.内部类通常用来解决“多重继承”的问题。
2.当希望隐藏一个类的实现,减少工程中.java文件数量,或者这个类不想被扩展时,你可以通过匿名内部类来创建一个类的对象。
3.java虽然无法直接在语法层面上支持闭包,但是可以通过内部类来模拟一个闭包的程序结构。

image-20230316135157913

概念:在一个类的内部再定义一个完整的类。

语法:

1
2
3
4
class Outer {
class Inner {
}
}

编译:Outer$Inner.class 和 Outer.class

特点:

  • 编译之后可生成独立的字节码文件

  • 内部类可直接访问外部类的私有成员,而不破坏封装

  • 可为外部类提供必要的内部功能组件

1.1 成员内部类

概念:在类的内部定义,与实例变量、实例方法同级别的类。属于外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象。

语法:

1
2
3
4
5
// 创建内部类对象
Outer out = new Outer();
Inner in = out.new Inner(); // 特殊:不具普适性
// 成员内部类访问外部类的重名属性
Outer.this.属性名

特点:

  • 当外部类、内部类存在重名属性时,会优先访问内部类属性。
  • 成员内部类不能定义静态成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 成员内部类
*/

public class TestMemberInnerClass {

public static void main(String[] args) {
Outer out = new Outer();
//Outer.Inner in = new Outer.Inner(); //Error
Outer.Inner in = out.new Inner(); // 特殊:不具备普适性
out.m1();
in.m2();
}
}

class Outer {
int a = 10; // 外部类:实例变量
public Outer() {
System.out.println("Outer()");
}
public void m1() {
System.out.println("Outer class m1()");
}
/**
* 成员内部类(实例层级) - 外部类被创建后,可再创建成员内部类
*/
class Inner {
int b = 20;
int a = 30; // 内部类:重名实例变量
public Inner() {
System.out.println("Inner()");
}
public void m2() {
System.out.println("Inner class m2()");
System.out.println("Outer a = " + Outer.this.a); // 10 (特殊:不具普适性)
System.out.println("Inner a = " + a); // 30
}
}
}

1.2 静态内部类

概念:不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员

特点:

  • 只能直接访问外部类的静态成员(实例成员需实例化外部类对象)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 静态内部类
*/
public class TestStaticInnerClass {
public static void main(String[] args) {
System.out.println(Outer.Inner.b); // 20
// 静态内部类对象创建
Outer.Inner in = new Outer.Inner();
in.m2();
}
}

class Outer {
private static int a = 10;
String s = "hello";
static class Inner {
static int b = 20;
public void m2 () {
System.out.println(Outer.a); // 可访问外部类私有对象,不影响封装
//System.out.println(s); // 静态内部类不能访问外部类的非静态成员
System.out.println("Inner m2()");
}
}
}

1.3 局部内部类

概念:定义在外部类的成员方法中,作用范围和创建对象范围仅限于当前方法

特点:

  • 不能有静态成员(属性/方法)
  • 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final
  • 隐藏类的信息,限制类的使用范围
    image-20230316135233300
    image-20230316135244012
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 局部内部类
*/
public class TestLocalInnerClass {
public static void main(String[] args) {
Outer out = new Outer();
out.m1();
System.out.println(out.p);
// com.day.t2_localinnerclass2.Outer$1Inner@7852e922
out.p.print();
}
}

interface Printable {
public void print();
}

class Outer {
int a = 10;
Printable p = null; // 接口引用 (可通过成员方法的 局部内部类 对接口引用赋值,保存对象)
public void m1() {
//Cannot refer to a non-final variable c inside an inner class defined in a different in a different method
/* final */int b = 20; // 局部内部类访问时会被自动加 final 修饰
class Inner implements Printable {
int c = 30;
@Override
public void print() {
System.out.println(a); // 10, 访问外部类的实例成员,等价于 Outer.this.a
System.out.println(b); // 20, 一旦局部内部类访问外部类的局部变量,会被自动final修饰
System.out.println(c); // 30, 访问内部类的实例成员
System.out.println("local inner m2()");
}
}

// 创建对象
Inner in = new Inner();
System.out.println(in.c);
// 接口引用指向实例对象
p = new Inner();
}
}

示例:限制类的使用范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 局部内部类 - 限制类的使用范围
*/
public class TestLocalInnerClassForApply {
public static void main(String[] args) {
// 学校一年级开设新版(班主任)
// 家长意见:我们需要经验丰富的老师
//Teacher teacher = new AdvancedTeacher(); //new Error:类定义在成员方法内 - 局部内部类
//teacher.teach();
// 校方没有那么多高级老师(6个班,3位高级老师,3位初级老师)
// 校方规则:班级奇偶编号,奇数班初级,偶数班高级
Teacher t = School.getTeacher(1);
t.teach();
}
}

class School {
public static Teacher getTeacher(int classNo) { // 封装思想:隐藏了类的信息,不能在外部new对象
// 初级老师
class BeginnerTeacher extends Teacher {
@Override
public void teach() {
System.out.println("初级老师在上课");
}
}

// 高级老师
class AdvancedTeacher extends Teacher {
@Override
public void teach() {
System.out.println("高级老师在上课");
}
}

Teacher currentTeacher = null; // 返回值
if (classNo % 2 != 0) {
currentTeacher = new BeginnerTeacher();
} else {
currentTeacher = new AdvancedTeacher();
}
return currentTeacher;
}
}

abstract class Teacher {
public abstract void teach();
}

1.4 匿名内部类【实际编码中很常见】

概念:没有类名的局部内部类(一切特征都与局部内部类相同)

特点:

  • 必须继承一个父类或者实现一个接口
  • 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象
  • 使用场景:① 显式继承父类时 ② 实现接口时
  • 优点:可以减少代码量,书写的思路流畅
  • 缺点:可读性较差
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 匿名内部类
*/
public class TestAnonymiteInnerClass {
public static void main(String[] args) {
Teacher t = School.getTeacher(1);
t.teach();
}
}

class School {
public static Teacher getTeacher(int classNo) { // 封装思想:隐藏了类的信息,不能在外部new对象
Teacher currentTeacher = null; // 返回值

if (classNo % 2 != 0) {
// 匿名内部类
currentTeacher = new Teacher() {
@Override
public void teach() {
System.out.println("初级老师在上课");
}
};
} else {
//匿名内部类 (创建了一个父类的子类对象,实现了子类中覆盖父类的方法)
currentTeacher = new Teacher() {
@Override
public void teach() {
System.out.println("高级老师在上课");
}
};
}
return currentTeacher;
}
}

abstract class Teacher {
public abstract void teach();
}

06-四种内部类
https://janycode.github.io/2016/04/28/02_编程语言/01_Java/01_JavaSE/02_面向对象/06-四种内部类/
作者
Jerry(姜源)
发布于
2016年4月28日
许可协议