通过snapshot图理解值与引用
Java中的值与引用的概念是初学时的一个易错点,本文总结了二者的概念出现场景,并使用snapshot图来帮助加深理解。
参考资料
- 理解值与引用
- 值和引用
- Java:按值传递还是按引用传递详细解说
- 让你彻底理解Java的值传递和引用传递
- 软件构造(四点五)Snapshot图的最全画法、符号原理详解(内附纯手绘图片以及详细代码、例子)
- Snapshot Diagram画法小结
值与引用
概念
“面向对象分析和设计需要区分对象的值语义与引用语义。我的一块钱和你的一块钱相等,这是值语义;20岁的我和30岁的我是同一个人,这是引用语义。值对象包括2大特征:内容和运算,比如:3这个整数在计算机内部用二进制11表示,可以参与+,-,*,/等运算;引用对象包括3大特征:标识、状态和行为,比如:Person对象拥有不变的标识,并可通过行为改变状态。值对象的同一性建立在内容的基础上,而引用对象的同一性建立在标识的基础上。
struct和class是OOP语言为分别表达值语义和引用语义所提供的语法机制。值对象只能被动地参与运算,引用对象拥有主动的行为。”
简而言之,在类和对象的层面,引用可以看成是地址,而值就是这个地址上的内容。
Java中的值传递与引用传递
赋值和参数传递可以通过值复制(value-copy)或者引用复制 (reference-copy)来完成,在不同的编程语言中有不同的实现方法。
例如,在C中若要传递一个变量给一个函数并在函数中修改,可以传递这个变量的地址,即一个指向它的指针;在 C++ 中,就可以这样来声明参数int& myNum,即如果传递的变量是x,myNum就是指向x的引用,引用就像一种特殊的指针。
Java 中没有指针,有按值传递和按引用传递。
首先最好理解Java的内存分配模型,参考另一篇:https://blog.csdn.net/xx123698/article/details/99683909
按值传递
当传递的内容是基本类型时,Java传递的是该变量值的副本,可以理解为是重新创建了一个相同类型的变量,将传过去的参数的值赋给这个新的变量,函数内部使用的是这个新的局部变量。
描述的就是下面这两个概念:
- 形参:方法被调用时需要传递进来的参数,如:func(int a)中的a,它只有在func被调用期间a才有意义,也就是会被分配内存空间,在方法func执行完成后,a就会被销毁释放空间,也就是不存在了。
- 实参:方法被调用时是传入的实际值,它在方法被调用前就已经被初始化并且在方法被调用时传入。
1 | public class PassTest { |
按引用传递
当传递对象、数组时,Java同样传递的是副本,不同的是,是引用的副本,即创建了一个新的局部变量,将原来的引用的内容复制到了这个变量内,可以理解为把原来引用代表的地址值赋给这个新的变量,可以通过这个地址改变地址上的内容,但若改变这个局部变量的内容,让它“引用”其他的东西,原来外部的引用不会改变。
其实与C中的传递指针类似:
1 | void func(struct student *s) { |
1 | public class TempTest { |
说法:“在Java里面参数传递都是按值传递”
这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的拷贝,所以统称按值传递。
Snapshot diagram
理解内存是如何分配的能够帮助理解程序中的值和引用。在snapshot图中,箭头代表的是引用,圆圈/箭头指向的内容代表的是值。
画法
原文:参考资料6
对于基本类型的值,使用单线箭头指向实际值,不需要表明数据类型。
对于对象的值,如果是可变对象,使用单线椭圆,椭圆内写明对象的类型及对象内的值。
如果是不可变对象,使用双线椭圆,椭圆内写明对象的类型及对象内的值。
如果是对象的不可变引用(final标记),使用双线箭头。eg:id
如果是对象的可变引用,使用单线箭头。eg:age
相关面试题
1 | public class HelloWorld |