Java的传递类型究竟是值传递还是引用传递,相信大多数小伙伴们都很迷惑,也许有小伙伴知道是值传递,但是不不知道怎么说明。同时这也是一个很经典的面试题,打开文章的你也遇到过吧。
话不多说,先说结论:
Java严格按照值传递。
值传递: 在函数调用的过程中,将实参复制一份给形参,在函数中执行的对象是形参。
引用传递: 在函数调用的过程中,将实参的地址传递到函数中,在函数中执行的对象是真实的实参。
首先,我们先弄明白什么值传递和引用传递的具体含义。
值传递,是将我们传递给函数的对象进行复制,而真正传递进函数的是复制的对象,而不是我们传递给函数的对象,所以我们在函数内对于对象的修改,不会影响我们传递给函数的对象(实参),只会影响形参。
引用传递,是将我们传递给函数的对象的地址传递到函数里面,如果我们在函数中,对对象进行了修改,是会影响我们传递给函数的对象(实参)的。
值传递 | 引用传递 | |
---|---|---|
根本区别 | 会创建副本 | 不会创建副本 |
表现 | 函数无法改变原始对象 | 函数可以改变原始对象 |
我们可以根据传递给函数的对象的不同,分别演示基本数据类型和引用数据类型的对象
基本数据类型演示代码
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); } } 复制代码
代码的执行结果:
print in swap, a = 2, b = 1 print in main, a = 1, b = 2 复制代码
对于这段代码的演示结果,相信屏幕前的你很清楚。当传递参数是基本数据类型的时候,实际传递的参数的副本,所以在swap
函数中,对于对象的修改不会影响实际的对象。
引用类型演示代码
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; } } 复制代码
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 =============== 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
的分布情况,我们可以知道基本数据类型在栈的分布情况。在基本数据类型对象传递给函数时,会在栈内复制一份,即为形参a
,b
,在swap
函数执行之后,在栈内的分布情况,即变为形参a
的值变为2,形参b
的值变为1,虽然形参发生了变化,但是实参却没有变化。
在引用数据类型的代码演示中,对于swap
函数,o1
、o2
分别为red
、blue
的副本,o1
、o2
分别指向Red Balloon
、Blue Balloon
对象。在swap
函数执行之后o1
、o2
分别指向Blue Balloon
、Red Balloon
对象,即指向的对象发生了变化。
swap
函数执行前
swap
函数执行后
对于swap2
函数,o1
、o2
分别为red
、blue
的副本,o1
、o2
分别指向Red Balloon
、Blue Balloon
对象。在swap2
函数执行之后o1
、o2
分别指向对象的颜色发生变化,即指向的对象发生了变化。
swap2
函数执行前
swap2
函数执行后
对于paint
函数,ballon
分别为yellow
的副本,ballon
、yellow
都指向Yellow Balloon
对象。在paint
函数执行之后yellow
先改变了Yellow Balloon
对象的颜色,之后指向了Green Balloon
对象,最后又改变Green Balloon
对象的颜色为白色。
最后我们可以知道,无论是基本数据类型还是引用数据类型的对象,Java都是值传递。
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")
是等价的。剩下的内容,根据我们前面的分析,就可以知道了。