Java中的值与引用的概念是初学时的一个易错点,本文总结了二者的概念出现场景,并使用snapshot图来帮助加深理解。

参考资料

  1. 理解值与引用
  2. 值和引用
  3. Java:按值传递还是按引用传递详细解说
  4. 让你彻底理解Java的值传递和引用传递
  5. 软件构造(四点五)Snapshot图的最全画法、符号原理详解(内附纯手绘图片以及详细代码、例子)
  6. 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传递的是该变量值的副本,可以理解为是重新创建了一个相同类型的变量,将传过去的参数的值赋给这个新的变量,函数内部使用的是这个新的局部变量。

描述的就是下面这两个概念:

  1. 形参:方法被调用时需要传递进来的参数,如:func(int a)中的a,它只有在func被调用期间a才有意义,也就是会被分配内存空间,在方法func执行完成后,a就会被销毁释放空间,也就是不存在了。
  2. 实参:方法被调用时是传入的实际值,它在方法被调用前就已经被初始化并且在方法被调用时传入。
1
2
3
4
5
6
7
8
9
10
public class PassTest {
private void test(int a){ // 这个a与外面的a不是同一个了,可以改名:int b
a = 5; // 修改的是函数内部局部变量a的值,与main中的a无关
}
public static void main(String[] args) {
PassTest t = new PassTest();
int a = 3;
t.test(a);
}
}

按引用传递

当传递对象、数组时,Java同样传递的是副本,不同的是,是引用的副本,即创建了一个新的局部变量,将原来的引用的内容复制到了这个变量内,可以理解为把原来引用代表的地址值赋给这个新的变量,可以通过这个地址改变地址上的内容,但若改变这个局部变量的内容,让它“引用”其他的东西,原来外部的引用不会改变。

其实与C中的传递指针类似:

1
2
3
4
void func(struct student *s) {
s->age = 18; // 实参也改变
s = {20,...} // 实参不变
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TempTest {
private void test1(A a){
a.age = 20; // 修改了外面a的内容
a = new A(); // 已经指向不同内容了
a.age = 18; // 没有修改外部内容
}
public static void main(String[] args) {
TempTest t = new TempTest();
A a = new A();
a.age = 10;
t.test1(a);
}
}
class A{
public int age = 0;
}

说法:“在Java里面参数传递都是按值传递”

这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的拷贝,所以统称按值传递。

Snapshot diagram

理解内存是如何分配的能够帮助理解程序中的值和引用。在snapshot图中,箭头代表的是引用,圆圈/箭头指向的内容代表的是值。

画法

原文:参考资料6

对于基本类型的值,使用单线箭头指向实际值,不需要表明数据类型。

对于对象的值,如果是可变对象,使用单线椭圆,椭圆内写明对象的类型及对象内的值。

如果是不可变对象,使用双线椭圆,椭圆内写明对象的类型及对象内的值。

如果是对象的不可变引用(final标记),使用双线箭头。eg:id
如果是对象的可变引用,使用单线箭头。eg:age

相关面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HelloWorld
{
public static void operate(StringBuffer x, StringBuffer y)
{
x.append(y);
y = x;
System.out.println(x + "," + y);
}


public static void main(String[] args)
{
StringBuffer a = new StringBuffer("A");
StringBuffer b = new StringBuffer("B");
operate(a, b);
System.out.println(a + "," + b);
}
}

结果:
AB,AB
AB,B

留言

⬆︎TOP