内部类 | 我的日常分享

内部类

内部类

一、内部类

1.1 内部类的分类

image-20220226170928564

1.2 什么是内部类

  • 概念:在一个类的内部再定义一个完整的类。
  • 特点:
    1. 编译之后可生成独立的字节码文件。
    2. 内部类可直接访问外部类的私有成员,而不破坏封装。
    3. 可为外部类提供必要的内部功能组件。

案例1:编译之后生成独立的字节码文件

1
2
3
4
5
public class Outer {
class Inner{

}
}

内部类Inner生成了字节码文件Outer$Inner.class

image-20220226172621700

二、成员内部类

  • 在类的内部定义,与实例变量、实例方法同级别的类。

  • 成员内部类是外部类实例的一个部分,创建内部类对象时,必须依赖外部类对象。

    1
    2
    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
  • 当外部类、内部类存在重名属性时,会优先访问内部类属性。

  • 成员内部类不能定义静态成员(但可以定义静态常量)

案例1:内部类的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Outer {
class Inner{

}
}
class TestOuter{
public static void main(String[] args) {
/*
内部类创建方法1
*/
Outer outer1 = new Outer();
Outer.Inner inner1 = outer1.new Inner();
/*
内部类创建方法2
*/
Outer.Inner inner2 = new Outer().new Inner();
}
}

案例2:当外部类、内部类存在重名属性时,会优先访问内部类属性。

如需访问外部类的属性则需要使用外部类.this.属性名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Outer {
public String name="张三";
public int age;
class Inner{
public String name="李四";
void test(){
System.out.println(name);//优先访问内部类中的nama
System.out.println(Outer.this.name);//指定访问外部类中的属性
}
}
}
class TestOuter{
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.test();
}
}

运行结果:

image-20220226173527604

案例3:成员内部类不能定义静态成员(但可以定义静态常量)

静态内部类可以定义静态成员。

1
2
3
4
5
6
7
8
9
public class Outer {
public int age;
class Inner{
// public static final String habit;
public static String name;//错误
public static final String name1;//错误
public static final String name2="";//正确
}
}

image-20220226174630517

image-20220226174645276

三、静态内部类

  • 不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员。
  • 只能直接访问外部类的静态成员(实例成员需实例化外部类对象)
  • 声明方法:成员内部类前添加static关键字
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
public class Outer {
public String name="aaa";
public int age = 10;
//静态内部类
static class Inner{
public String address="武汉";
public String num = "123";
//静态成员 静态内部类可以定义静态变量
public static String count;
public static String count1="123";
public void test(){
/**
* 如何调用外部类的属性方法呢?
* 由于内部类是静态的,当外部类没有被创建的时候,
* 内部类可以被创建,所有外部内的属性有可能还没有被
* 创建,所有不能通过System.out.println(Outer.this.name);
* 进行访问,必须创建外部类后才能够进行访问。
*/
Outer outer = new Outer();//创建外部类对象
System.out.println(outer.name);//调用外部类的属性
/**
* 调用静态内部类的属性方法
*/
System.out.println("方法1:"+count);
System.out.println("方法2:"+Outer.Inner.count1);
}
public static void test1(){
System.out.println("内部类中的静态方法");
}
}
}
class TestOuter{
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.test();
System.out.println("***********************");
/**
* 静态内部类中的静态属性方法与普通类中的
* 静态属性方法一致,可直接通过类名进行调用
*/
Outer.Inner.test1();
System.out.println(Outer.Inner.count);
Outer.Inner.count="abcdefg";
System.out.println(Outer.Inner.count);
}
}

运行结果:

image-20220226234957622

注意:只有内部类能够加static修饰,普通类无法使用static修饰。

四、局部内部类

  • 定义在外部类的方法当中,作用范围和作用对象范围仅限于当前方法。
  • 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Outer {
private String name="xiaoming";
private int age=18;
public void test(){
/**
* 定义局部变量
* 方法中的变量为局部变量
* 是不能使用public private protected修饰的
*/
String phone = "12345";
/**
* 定义局部内部类
* 相当于局部变量或方法
* 也是不能使用public private protected修饰的
*/
class Inner{
//局部内部类中定义属性
private String a = "aaa";
private String b = "bbb";
//局部内部类中定义方法
public void test1(){
//访问外部内的属性(如果内部类中无同名)
System.out.println(name);
//访问外部内的属性(如果内部类中有同名)
System.out.println(Outer.this.name);
//访问内部类的属性
System.out.println(this.a);
System.out.println(b);
}
}
//创建内部类对象
Inner inner = new Inner();
//使用内部类的成员方法
inner.test1();
}
}

class TestOuter{
public static void main(String[] args) {
Outer outer = new Outer();
//调用外部类的方法
outer.test();
}
}

运行结果:

image-20220227121242582

案例1:

局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final,JDK1.7要求,JDK1.8及以后自动隐式添加final

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Outer {
public void test(){
//定义的局部变量
String variable = "我是局部变量";
final String variable1 = "我是局部变量1";
class Inner{
void mytest(){
System.out.println(variable);
System.out.println(variable1);
}
}
Inner inner = new Inner();
inner.mytest();
}
}

class TestOuter{
public static void main(String[] args) {
Outer outer = new Outer();
//调用外部类的方法
outer.test();
}
}

运行结果:

为什么要被final修饰成常量呢?这是因为局部变量variablevariable1在当前的test方法结束后就被销毁了,而创建的Inner类不会被销毁,如果这个时候再次调用局部变量variablevariable1,程序将会出现异常。

image-20220227122629183

案例2:局部内部类中不能声明静态成员,但是可以声明静态常量。

1
2
3
4
5
6
7
public class Outer {
public void test(){
class Inner{
private static String str;
}
}
}

运行结果:

image-20220227123443634

1
2
3
4
5
6
7
8
public class Outer {
public void test(){
class Inner{
// private static String str;
private static final String str = "123";//编译通过
}
}
}

五、匿名内部类

  • 没有类名的局部内部类(一切特征都与局部内部类相同)。
  • 必须继承一个父类或者实现一个接口。
  • 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象。
  • 优点:减少代码量
  • 缺点:可读性较差

案例1:

电脑有两个USB接口,键盘插入电脑,可以发现我们需要声明KeyBord的类,然后再去创建对象,但是我们仅仅就使用一次,这样就显得过于麻烦了,那如果要插入鼠标,就需要再去声明鼠标类,去实现Usb的方法。

Computer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Computer {
//电脑有两个USB接口
public Usb usb1;
public Usb usb2;
void run(){
System.out.println("电脑开始运行");
if(usb1 != null){
usb1.service();
}
if(usb2 != null){
usb2.service();
}
}
}

Usb.java

1
2
3
public interface Usb {
void service();
}

KeyBoard.java

1
2
3
4
5
6
7
public class KeyBord implements Usb{

@Override
public void service() {
System.out.println("键盘开始工作了");
}
}

TestComputer.java

1
2
3
4
5
6
7
public class TestComputer {
public static void main(String[] args) {
Computer computer = new Computer();
computer.usb1 = new KeyBoard();
computer.run();
}
}

运行结果:

image-20220227192418444

案例2:使用局部内部类创建键盘对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestComputer {
public static void main(String[] args) {
Computer computer = new Computer();
// computer.usb1 = new KeyBoard();
class KeyBoard implements Usb{
@Override
public void service() {
System.out.println("局部内部类 键盘开始运行了");
}
}
computer.usb1 = new KeyBoard();
computer.run();
}
}

运行结果:

image-20220227193046826

案例3:使用匿名内部类创建键盘对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestComputer {
public static void main(String[] args) {
Computer computer = new Computer();
// computer.usb1 = new KeyBoard();
computer.usb1 = new Usb() {
@Override
public void service() {
System.out.println("匿名内部类 键盘开始运行了");
}
};
computer.run();
}
}

运行结果:

image-20220227192551642

其实匿名内部类是用名字的,只是这个名字是由java编译器为我们取的,打开编译完成的字节码文件可以看到TestComputer$1KeyBoard.class

image-20220227193230791