图解Java传递类型

图解Java传递类型

Java的传递类型究竟是值传递还是引用传递,相信大多数小伙伴们都很迷惑,也许有小伙伴知道是值传递,但是不不知道怎么说明。同时这也是一个很经典的面试题,打开文章的你也遇到过吧。

话不多说,先说结论:


Java严格按照值传递。


值传递与引用传递

值传递: 在函数调用的过程中,将实参复制一份给形参,在函数中执行的对象是形参。

引用传递: 在函数调用的过程中,将实参的地址传递到函数中,在函数中执行的对象是真实的实参。

首先,我们先弄明白什么值传递和引用传递的具体含义。

值传递,是将我们传递给函数的对象进行复制,而真正传递进函数的是复制的对象,而不是我们传递给函数的对象,所以我们在函数内对于对象的修改,不会影响我们传递给函数的对象(实参),只会影响形参。

引用传递,是将我们传递给函数的对象的地址传递到函数里面,如果我们在函数中,对对象进行了修改,是会影响我们传递给函数的对象(实参)的。

值传递 引用传递
根本区别 会创建副本 不会创建副本
表现 函数无法改变原始对象 函数可以改变原始对象

代码演示

我们可以根据传递给函数的对象的不同,分别演示基本数据类型和引用数据类型的对象

基本数据类型

基本数据类型演示代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ShowCode {
public static void main(String[] args) {
int a = 1;
int b = 2;
swap(a, b);
System.out.println("print in main, a = " + a + ", b = " + b);
}

private static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("print in swap, a = " + a + ", b = " + b);
}
}

代码的执行结果:

1
2
print in swap, a = 2, b = 1
print in main, a = 1, b = 2

对于这段代码的演示结果,相信屏幕前的你很清楚。当传递参数是基本数据类型的时候,实际传递的参数的副本,所以在swap函数中,对于对象的修改不会影响实际的对象。

引用数据类型

引用类型演示代码

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
public class Balloon {
private String name;
private String color;

public Balloon(String name, String color) {
this.name = name;
this.color = color;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}
}
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
public class PassMain {
public static void main(String[] args) {
Balloon red = new Balloon("Red Balloon", "Red");
Balloon blue = new Balloon("Blue Balloon", "Blue");
Balloon yellow = new Balloon("Yellow Balloon", "Yellow");

System.out.println("============= 1 ===============");
swap(red, blue);
System.out.println("Red Balloon color is " + red.getColor());
System.out.println("Blue Balloon color is " + blue.getColor());

System.out.println("============= 2 ===============");
swap2(red, blue);
System.out.println("Red Balloon color is " + red.getColor());
System.out.println("Blue Balloon color is " + blue.getColor());

System.out.println("============= 3 ===============");
paint(yellow);
System.out.println("Yellow Balloon color is " + yellow.getColor());
}

public static void swap(Object o1, Object o2) {
Object temp = o1;
o1 = o2;
o2 = temp;
}

public static void swap2(Balloon o1, Balloon o2) {
String temp = o1.getColor();
o1.setColor(o2.getColor());
o2.setColor(temp);
}

private static void paint(Balloon balloon) {
balloon.setColor("Black");
balloon = new Balloon("Green Balloon","Green");
balloon.setColor("White");
}
}

代码执行结果

1
2
3
4
5
6
7
8
=============  1  ===============
Red Balloon color is Red
Blue Balloon color is Blue
============= 2 ===============
Red Balloon color is Blue
Blue Balloon color is Red
============= 3 ===============
Yellow Balloon color is Black

对于这段代码的结果,屏幕面前的你,有没有感觉有点转不过来。哈哈,听我细细道来。

swap函数中,交换了两个引用数据类型的对象,实际对象没有变化。

swap2函数中,交换了两个引用数据类型的对象的颜色,实际对象却发生了变化。

paint函数中,对引用数据类型的对象的颜色,实际对象发生了变化。

底层原理

对于值传递,无论是值类型还是引用类型,都会在调用栈上创建一个副本,不同是,对于值类型而言,这个副本就是整个原始值的复制。而对于引用类型而言,由于引用类型的实例在堆中,在栈上只有它的一个引用(一般情况下是指针),其副本也只是这个引用的复制,而不是整个原始对象的复制。

基本数据类型

在基本数据类型的代码演示中,根据对象在JVM的分布情况,我们可以知道基本数据类型在栈的分布情况。在基本数据类型对象传递给函数时,会在栈内复制一份,即为形参ab,在swap函数执行之后,在栈内的分布情况,即变为形参a的值变为2,形参b的值变为1,虽然形参发生了变化,但是实参却没有变化。

Java基本数据类型传递.png

引用数据类型

在引用数据类型的代码演示中,对于swap函数,o1o2分别为redblue的副本,o1o2分别指向Red BalloonBlue Balloon对象。在swap函数执行之后o1o2分别指向Blue BalloonRed Balloon对象,即指向的对象发生了变化。


swap函数执行前

swap函数执行前.png

swap函数执行后

swap函数执行后.png


对于swap2函数,o1o2分别为redblue的副本,o1o2分别指向Red BalloonBlue Balloon对象。在swap2函数执行之后o1o2分别指向对象的颜色发生变化,即指向的对象发生了变化。


swap2函数执行前

swap2函数执行前前.png

swap2函数执行后

swap2函数执行后.png


对于paint函数,ballon分别为yellow的副本,ballonyellow都指向Yellow Balloon对象。在paint函数执行之后yellow先改变了Yellow Balloon对象的颜色,之后指向了Green Balloon对象,最后又改变Green Balloon对象的颜色为白色。

paint函数执行前后.png


最后我们可以知道,无论是基本数据类型还是引用数据类型的对象,Java都是值传递。

思考题

1
2
3
4
5
6
7
8
9
10
11
public class StringTest {
public static void main(String[] args) {
String name = "Hello Java";
passStr(name);
System.out.println("name ==" + name);
}

private static void passStr(String str) {
str = "Hello World";
}
}

读到这里,预测一下最后打印的是Hello Java还是Hello World

最后结果是Hello Java,你猜对了没有?其实想知道结果,只需要知str = "Hello World"str = new String("Hello World")是等价的。剩下的内容,根据我们前面的分析,就可以知道了。


  图解
You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.

评论