java传递类_图解Java传递类型
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")是等价的。剩下的内容,根据我们前面的分析,就可以知道了。