高级内存

对象 与 对象变量

数据格

1
int a = 1;

变量 是 数据格

变量的值 是 数据格里的数据

对象变量

对象变量 是 数据格

对象变量的值 是 对象*

1
Player player = new Player(); ------ ------------- 对象变量 新建 一个 对象

用来代表没有对象

1
Player player = null;

有变量,变量里没有对象

空指针异常

如果对象变量里是 null

能通过编译,但是运行时 调用 . 运算符时 会报错

1 2 3! <4
public void run(){ Player player = null; System.out.println(player.getName()); NullPointerException}

值类型 与 引用类型 的 区别

是什么

编程的 两大 内存分配和管理规则

地址

任何变量 在开辟时都会有三样东西同时存在

变量数据格
地址数据格的id

赋值

在 Java 中,赋值永远会赋值变量格里的值

值类型

是什么

采用值类型的内存分配方式,变量里存储的是数据本身

图示

1 2 3 4
Point p1 = Point(3, 4);Point p2 = Point(5, 6);p1 = p2;p1.x = 7;

Java

Java 中的 对象没有采用这种内存管理机制

C++ 中的 struct 可以采用这种内存机制管理内存

Java 中的 8大基础类型采用了这种内存管理机制

Java 的 基础类型

1 2 3 4
int a = 3;int b = 4;a = b;a = 5

指针

是什么

一个变量里的存的值,正好是另外一个变量的地址

引用

在 java 里 叫 引用

引用类型

是什么

内存被分为 堆 和 栈 两大区域。

局部变量、参数 的 内存 开辟在 栈里。

对象属性 的 内存 开辟在 堆里。

采用引用类型的内存分配方式,对象变量里 存的 是 对象在 堆里的 地址

图示

1 2 3 4
Point p1 = new Point(3, 4);Point p2 = new Point(5, 6);p1 = p2;p1.x = 7;

. 运算符

会产生地址跳跃

理解

网红的故事

网红 是 对象,是 对象在堆里开辟的空间

网红的手机号 是 对象在堆里的地址

小纸条 是 对象变量,是 指针,存储着 网红的手机号

Java

Java 中的 对象类型(非基础类型),采用这种内存管理方式

对象 与 对象变量

对象变量里存的是对象的引用

对象变量赋值, 赋值的是对象变量里的内容

1 2 3
Point p1 = new Point(3, 4);Point p2 = new Point(5, 6);Point p3 = p2;

内存管理 与 代码执行 规则

基于下面的代码

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
public class Cla { private int a = 1; public Cla(int a) { this.a = a; } public void func1(int value) { int a = 32; // 检查点 2 } public void func2() { // 检查点 4 System.out.println(a); func3(); } public void func3() { int b = 4; // 检查点 5 }}
1 2 3 4 5 6 7 8 9 10 11 12 13
public class Driver { public static void main(String[] args) { Cla obj1 = new Cla(1); Cla obj2 = new Cla(2); int value = 7; // 检查点 1 obj1.func1(2); // 检查点 3 obj2.func2(); // 检查点 6 }}

关于检查点

上面的代码的检查点 的序号 是按着执行时经过的顺序 编号的

变量开辟

规则

局部变量 / 参数 在 栈里

属性 在 堆里

案例

在 检查点 1 的时候

两个 Cla 对象开在 堆里

作为 main 函数的局部变量 obj1, obj2 和 value 开在栈里

此时的内存:

调用方法前的准备

规则

当调用方法时,需要先对引导的对象变量 和 参数 进行求值(缩合)

案例

Driver 中的 Line 8

    obj1.func1(2);

引导的对象变量 obj1 必须先缩合,才能调用 func1

参数是 值 2,无需缩合,如果是变量,则需要缩合

变量缩合

规则

变量会缩合为里面的值

一般变量 就是 里面的值

对象变量 也是 里面的值,不过这个值 是另外一个 对象的地址

案例

Driver 中的 Line 8

    obj1.func1(2);

obj1 变量会缩合为 201

    201.func1(2);

调用方法

规则
Step 1

增加 隔板

每个函数都有自己的变量空间

调用函数,会为函数开辟新的内存区域,在栈里

Step 2

传输 this

为 被调用函数 创建一个 this 变量,在栈里

谁点函数,this 就是谁

Step 3

传输 参数

为 被调用函数 创建参数变量,在栈里

传参的值 会 过去

案例

在 检查点 2 的时候 其实 方法已经调用进去了

此时根据上面规则中的操作,增加了 this 和 value 的变量

除此之外,还因为 Cla 的 Line 9 增加了 局部变量 a

此时的内存:

方法销毁

规则

删除 所有变量

删除 隔板

传输 返回值

案例

在 检查点 3 的时候 func1 函数已经调用完毕

此时曾经创建的隔板,和隔板下的变量 全部被销毁

func1 没有返回值,所以不需要考虑返回值的事情

此时的内存:

this 补充 属性

规则

如果 在栈的隔板下方 找不到 变量,则补充 this

案例

在 检查点 4 的时候,func2 函数刚刚被调用完毕

此时的内存

接下来要执行 Line 14

    System.out.println(a);

但 堆栈结构的 隔板下方 并没有 a。

所以代码会变为

    System.out.println(this.a);

从而可以根据之前的变量缩合规则变为

    System.out.println(202.a);

this 补充 方法

规则

如果调用方法前面,没有对象变量引导,则补充 this

案例

Cla 的 Line 16 时

    func3();

func3 是 省略写法,前面缺少了对象变量引导,所以补 this

    this.func3();

也就是

    202.func3();

持续进入

规则

调用方法时,如果进入新的方法,则会持续增加隔板

案例

Cla 的 Line 16 会导致 func2 执行时进入 func3

在 检查点 5 时,内存为

逐级退出

规则

每个方法调用完了,都会销毁它自己的资源和隔板

案例

Driver 的 Line 10 曾经在调用 func2 时 导致 间接调用 func3

但在 检查点 6 时, func2 已经完成了调用, func2 内部的 func3 也必定结束了调用

此时,func3 和 func2 在栈中的空间 已经接连被销毁。

在 检查点 6 的内存:

ZZAX 微信公众

文档一更新,立刻告诉你