Java内部类

AI摘要:

对应《疯狂Java讲义(第5版)》6.7 章节

内部类(JDK1.1)

定义在其他类内部的类成为内部类,或称嵌套类;包含内部类的类为外部类,或宿主类

内部类作为外部类成员,可直接访问外部类私有数据,但外部类不能直接访问内部类的实现细节

内部类比外部类可多使用三个修饰符:private、protectd、static

参考链接:Java的外部类,为什么只能使用public和default进行修饰_java public default一起用-CSDN博客

Java内部类包括成员内部类(非静态内部类、静态内部类)、局部内部类、匿名内部类

成员内部类:非静态内部类

不允许在非静态内部类中定义静态成员

非静态内部类对象中,保存了一个寄生的外部类对象的引用,即{外部类类名}.this

显示创建内部类实例对象来调用访问其实例成员

DiscernVariable.java

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
public class DiscernVariable
{
private String prop = "外部类的实例变量";
private class InClass
{
private String prop = "内部类的实例变量";
public void info()
{
String prop = "局部变量";
// 通过 外部类类名.this.varName 访问外部类实例变量
System.out.println("外部类的实例变量值:"
+ DiscernVariable.this.prop);
// 通过 this.varName 访问内部类实例的变量
System.out.println("内部类的实例变量值:" + this.prop);
// 直接访问局部变量
System.out.println("局部变量的值:" + prop);
}
}
public void test()
{
InClass in = new InClass();
in.info();
}
public static void main(String[] args)
{
new DiscernVariable().test();
}
}

外部类以外调用非静态内部类

内部类完整类名 OutClass.InnerClass

必须先创建外部类对象然后outerInstance.new InnerConstructor()创建内部类实例

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
class Out
{
// 定义一个内部类,不使用访问控制符,
// 即只有同一个包中其他类可访问该内部类
class In
{
public In(String msg)
{
System.out.println(msg);
}
}
}
public class CreateInnerInstance
{
public static void main(String[] args)
{
Out.In in = new Out().new In("测试信息");
/*
上面代码可改为如下三行代码:
使用OutterClass.InnerClass的形式定义内部类变量
Out.In in;
创建外部类实例,非静态内部类实例将寄存在该实例中
Out out = new Out();
通过外部类实例和new来调用内部类构造器创建非静态内部类实例
in = out.new In("测试信息");
*/
}
}

成员内部类:static 静态内部类

属于外部类本身,只能访问外部类类成员,而不能访问外部类实例成员

静态内部类可包含静态和非静态成员,但非静态成员也无法访问外部类实例成员

静态内部类对象只持有外部类的引用,没有持有外部类对象的引用。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类对象,这将引起错误。

外部类可通过静态内部类类名访问其类成员,或通过静态内部类对象访问其实例成员

CreateStaticInnerInstance.java

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
class StaticOutS
{
// 定义一个静态内部类,不使用访问控制符,
// 即同一个包中其他类可访问该内部类
static class StaticIn
{
public StaticIn()
{
System.out.println("静态内部类的构造器");
}
}
}
public class CreateStaticInnerInstance
{
public static void main(String[] args)
{
StaticOut.StaticIn in = new StaticOut.StaticIn();
/*
上面代码可改为如下两行代码:
使用OutterClass.InnerClass的形式定义内部类变量
StaticOut.StaticIn in;
通过new来调用内部类构造器创建静态内部类实例
in = new StaticOut.StaticIn();
*/
}
}

局部内部类

局部内部类在方法中定义,仅在方法中有效,不能使用访问控制符static修饰

LocalInnerClass.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LocalInnerClass
{
public static void main(String[] args)
{
// 定义局部内部类
class InnerBase
{
int a;
}
// 定义局部内部类的子类
class InnerSub extends InnerBase
{
int b;
}
// 创建局部内部类的对象
InnerSub is = new InnerSub();
is.a = 5;
is.b = 8;
System.out.println("InnerSub对象的a和b实例变量是:"
+ is.a + "," + is.b);
}
}

生成LocalInnerClass.class,LocalInnerClass$1InnerBase.class,LocalInnerClass$1InnerSub.class

匿名内部类

匿名内部类适合创建一次性使用的类

匿名内部类创建时立即创建一个实例,类定义立即消失,不能重复使用

匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口

1
2
3
4
5
new 实现接口() | 父类构造器(实参列表)
{
//不能定义构造器(因为没有类名),但可以有初始化块
//不能定义为抽象类(因为立即创建实例)
}

实现例子:

AnonymousInner.java

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
48
49
50
51
52
53
54
55
56
57
58
abstract class Device
{
private String name;
public abstract double getPrice();
public Device(){}
public Device(String name)
{
this.name = name;
}
// 此处省略了name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
public class AnonymousInner
{
public void test(Device d)
{
System.out.println("购买了一个" + d.getName()
+ ",花掉了" + d.getPrice());
}
public static void main(String[] args)
{
AnonymousInner ai = new AnonymousInner();
// 调用有参数的构造器创建Device匿名实现类的对象
ai.test(new Device("电子示波器")
{
public double getPrice()
{
return 67.8;
}
});
// 调用无参数的构造器创建Device匿名实现类的对象
Device d = new Device()
{
// 初始化块
{
System.out.println("匿名内部类的初始化块...");
}
// 实现抽象方法
public double getPrice()
{
return 56.2;
}
// 重写父类的实例方法
public String getName()
{
return "键盘";
}
};
ai.test(d);
}
}

匿名内部类访问的局部变量必须按有final修饰的来用,如示例中的age,Java8以后会自动修饰final

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface A
{
void test();
}
public class ATest
{
public static void main(String[] args)
{
int age = 8; // ①
// 下面代码将会导致编译错误
// 由于age局部变量被匿名内部类访问了,因此age相当于被final修饰了
// age = 2;
A a = new A()
{
public void test()
{
// 在Java 8以前下面语句将提示错误:age必须使用final修饰
// 从Java 8开始,匿名内部类、局部内部类允许访问非final的局部变量
System.out.println(age);
}
};
a.test();
}
}

Java内部类
https://blog.cngo.rr.nu/posts/fe21.html
作者
cngo
发布于
2024年6月21日
许可协议