THIS IS B3c0me

记录生活中的点点滴滴

0%

内部类详解

1.内部类的定义

1
内部类就是在一个类的内部定义的类

2.内部类的分类

2.1成员内部类

  • 成员内部类格式如下:
1
2
3
4
5
6
7
* class Outer {
* class Inner{}
* }
*
* 编译上述代码会产生两个文件:
* Outer.classOuter$Inner.class

  • 示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Outer{
    private String name;
    public void innerPrint(){
    Inner inner = new Inner();
    inner.print();
    }
    //私有成员内部类
    private class Inner{
    public void print(){
    System.out.println("inner");
    }
    }
    }

2.2方法内部类

内部类可以作为一个类的成员外,还可以把类放在方法内定义

注意:1.方法内部类只能定义该内部类的方法内实例化,不可以在此方法外对其实例化
2.方法内部类对象不能使用该内部类所在方法的非final局部变量

  • 示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public void show(){
    int x = 10;
    final int y = 20;
    x*=2;
    class Inner2{
    public void print(){
    System.out.println("inner2");
    System.out.println();
    System.out.println(y);
    }
    }
    //在方法内部调用内部类的方法
    Inner2 inner2 = new Inner2();
    inner2.print();
    }
  • ​ 当我们想要在方法内部更改非final的变量值时,提示我们从内部类引用的本地变量必须是最终变量或者实际上的最终变量。

    • 也就是说,只要内部类访问一个变量,那这个变量必须是该方法的常量

2.3静态内部类

在一个类内部定义一个静态的内部类:
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够方法它。

静态类嵌套仅能访问外部类的静态成员和方法。

  • 示例代码:

    1
    2
    3
    4
    5
    6
    //静态内部类
    static class Inner3{
    public void print(){
    System.out.println("静态内部类");
    }
    }

2.4 匿名内部类

匿名内部类就是没有名字的内部类

内名内部类的三种情况:

​ (1)继承式的匿名内部类

​ (2)接口式的匿名内部类

​ (3)参数式的匿名内部类

在使用匿名内部类时,有以下几个原则

  • 不能有构造方法,只能有一个实例
  • 不能定义任何静态成员,静态方法
  • 不能是public,protected,private,static
  • 一定是在new的后面,用其隐含实现一个接口或实现一个类
  • 匿名内部类为局部的,所以局部内部类的所有限制都对其生效

3.在外部创建内部类对象

内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化

那么,根据内部类生成的*.class文件:Outer$Inner.class

$符号在程序运行时将被替换成”.”

所以内部类的访问:通过“外部类·内部类”的形式表示

1
2
3
Outer out = new Outer(); // 产生外部类实例
Outer.Inner in = null; //声明内部类对象
in = out.new Inner(); //实例化内部类对象

4.问题:

局部内部类访问局部变量必须用final修饰,为什么?

当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法被调用时会入栈,方法结束后即弹栈,这个局部变量也会消失,如果局部内部类对象还没有马上消失,想继续使用这个局部变量,显然已经无法使用了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了。

注意:在jdk1.8中取消了在局部内部类中使用的变量必须是显式地使用的final修饰,也就是说编译器默认会自动为变量加上final

5.内部类的作用

每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经集成了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程的问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。

具体实现:在一个外部类中写多个内部类,不同的内部类继承不同的其他类,这样就可以实现多继承。

6.内部类小结

从成员属性来看,内部类可以分为两类:

成员

  • 成员内部类
  • 静态内部类

局部

  • 方法内部类
  • 匿名内部类

从依赖外部类对象的角度来看:

  • 成员内部类,方法内部类,匿名内部类都依赖外部类对象

  • 静态内部类不依赖外部类的对象

开发中如何选择

我们在项目中优先考虑使用静态内部类。

7.附:示例代码

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package junior;

/**
* @Description :
* @Author: zcwww
* 2023/9/9 15:37
*/

/**
* 内部类就是在一个类的内部定义的类
*1.成员内部类
* 成员内部类格式如下:
* class Outer {
* class Inner{}
* }
*
* 编译上述代码会产生两个文件:
* Outer.class 和 Outer$Inner.class
*
*/
public class InnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
//在外部创建成员内部类的实例
//通常情况下不建议这样实例化内部类的对象
// Outer.Inner inner = outer.new Inner();
// inner.print();

//建议使用外部类调用内部类的方法
outer.innerPrint();

//调用方法内部类
outer.show();

//调用静态内部类
new Outer.Inner3().print();

//继承式内部类匿名类
outer.print1();

//接口式匿名类
outer.print2();

//
outer.print3(new Eat() {
@Override
public void eat() {
System.out.println("参数式匿名内部类");
}
});
}
}

class Outer{
private String name;
public void innerPrint(){
Inner inner = new Inner();
inner.print();
}
//私有成员内部类
private class Inner{
public void print(){
System.out.println("inner");
}
}

//方法内部类
public void show(){
int x = 10;
final int y = 20;
x*=2;
class Inner2{
public void print(){
System.out.println("inner2");
System.out.println();
System.out.println(y);
}
}
//在方法内部调用内部类的方法
Inner2 inner2 = new Inner2();
inner2.print();
}

//静态内部类
static class Inner3{
public void print(){
System.out.println("静态内部类");
}
}

//匿名内部类
public void print1(){
//继承式内部类
Cat cat = new Cat() {
@Override
void eat() {
System.out.println("cat inner继承式匿名内部类");
}
};
cat.eat();
}

//接口式内部类
public void print2(){
Eat eat = new Eat() {
@Override
public void eat() {
System.out.println("接口式匿名类");
}
};
eat.eat();
}

//参数式内部类
public void print3(Eat eat){
eat.eat();
}
}

abstract class Cat{
abstract void eat();
}

interface Eat{
void eat();
}

欢迎关注我的其它发布渠道