Java中只有按值传递,没有按引用传递

简介:

最近在看Java核心技术的时候,遇到以前遇到的一个问题,就是Java除了值传递以外,到底有没有引用传递。网上众说纷纭,我看了具有代表性的10几篇文章,结合书中以及自己的举例,终于得出,Java只有按值传递,没有按引用传递。或者可以说为Java只有副本传递,为什么这么说呢?请看我的论证。

基本数据类型分析

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
/** 基本数据类型 **/
int a = 2;
p.foo(a); // 传入基本数据类型,是一个值拷贝
System.out.println(a); // 2
}

private void foo(int value) {
value = 100;
}

Java中所有的基本数据类型结果都一样,在这里用int做一下实验。
可以看出结果并没有改变a的值,因为Java对基本数据类型的传递,首先为拷贝的值开辟一个内存,让value指向拷贝的那个值,所以这是值传递。下面是内存分析图:

basicType

包装数据类型分析

1
2
3
4
5
6
7
8
9
10
11
12
/** 包装数据类型 **/
Integer integer = 2;
p.foo(integer); // 包装类作为参数传递时,仍是值传递
System.out.println(integer); // 2

Integer integer1 = new Integer(2);
p.foo(integer1); // 包装类作为参数传递时,仍是值传递
System.out.println(integer1); // 2

private void foo(Integer value) {
value = 100;
}

Java中所有的包装数据类型和基本数据类型之间都会完成隐式的互相转换,这也是为什么list可以add基本数据类型的原因。
同样这个例子和基本数据类型的结果是一样的,传递的也是值的拷贝。内存分析和上图一样。

String字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** String **/
String b = "mac";
p.fooString(b); // 没有提供改变自身方法的引用类型
System.out.println(b); // mac

String b2 = new String("mac");
p.fooString2(b2);
System.out.println(b2); // mac

String b3 = "mac";
b3.replaceAll("m", "M");
System.out.println(b3); // mac

private void fooString(String value) {
value = "windows";
}

private void fooString2(String value) {
value.concat(" computer");
}

字符串由于其特殊性(字符串是不可变的),实际上方法是对原来的对象进行了一个拷贝,之后的操作都是在拷贝对象里进行的。所以不影响原来对象的值。
b和b2对象都是在fooString方法里面进行了一次拷贝,然后value的操作不影响b和b2的值。
b3也是传递了一个拷贝值到fooString2这个方法,然后concat是对这个拷贝值进行操作。

String

系统对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** 系统对象 **/
StringBuffer sb = new StringBuffer("iPhone");
p.foo(sb); // 提供了改变自身方法的引用类型,但是方法内部拿到地址的引用指向了新的地址.原来sb指向的地址并没有改变
System.out.println(sb); // iPhone

StringBuilder sb2 = new StringBuilder("iPhone");
p.foo(sb2); // 提供了引用地址的copy,改变指向地址对象的值.
System.out.println(sb2); // iPhone5s

private void foo(StringBuffer buffer) {
buffer = new StringBuffer("iPad");
}

private void foo(StringBuilder builder) {
builder.append("5s");
}

其实前面的还好去理解传递是值传递,那么对象这块如何去理解呢?
很多人都在这里认为传递是引用传递,我们来进行一下分析。

object

上图的sb对象指向了对象StringBuffer,它有个值“iPhone”,传入方法后,buffer其实拿到的不是原来sb的引用,只不过是一个副本而已。尽管它也指向iPhone的内存地址。
第一个例子里面,buffer在方法里面重新指向了一个新的StringBuffer对象的内存地址,放着一个值”iPad”,所以原来sb指向的值并没有改变。

object2

第二个例子里面,builder拿到了原来对象的引用拷贝,但是指向的地址不变,所以可以修改内存地址中的对象的属性,并没有新的引用。

object2

所以可以看出来,我们并没有拿到引用去操作内存地址中存在的值,只不过是一个引用的拷贝,如果这个例子说的还不够信服的话,下面的例子应该会让人信服。

自定义对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** 自定义对象 **/
Employee e1 = new Employee("tom", 7000);
Employee e2 = new Employee("jerry", 4000);
p.swap1(e1, e2);
System.out.println(e1); // tom 7000
System.out.println(e2); // jerry 4000

p.swap2(e1, e2);
System.out.println(e1); // caroline 7000
System.out.println(e2); // benjamin 4000

private void swap1(Employee x, Employee y) {
Employee temp = x;
x = y;
y = temp;
}

private void swap2(Employee x, Employee y) {
Employee temp = x;
x = y;
y = temp;
x.setName("benjamin");
y.setName("caroline");
}

Employee实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package JavaCoreTechnology;

/**
* Created by benjamin on 1/14/16.
*/

public class Employee {
private String name;
private int salary;

public Employee(String name, int salary) {
this.name = name;
this.salary = salary;
}

public String toString() {
return this.name + " " + this.salary;
}

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

o4

o4

上图是swap1的交换的实现,这也正说明了为什么输出的值并没有改变。因为原来对象的引用并没有改变,改变的只有这个引用的副本。
这也正说明了为什么swap2是改变的交换的对象的值。

总结:
在Java中并不像c++的那样通过指针操作变量,虽然在Java的最底层仍然使用的是指针,但是Java的开发者已经帮我们封装好了,让我们接触不到而已。基本数据类型的值传递的方式还是和c++的方式一样,是一个值的拷贝。操作的是拷贝的值不会对原来的值造成影响。
而对象的操作则是引用的拷贝,其实也是一个值,是内存地址的值而已。它没有完全把引用传递。所以翻转它的时候不会对原来的对象造成影响,只不过他们指向的都是同一个地址,会改变内存地址中的值罢了。

所以!Java只有按值传递!

目录
相关文章
|
3月前
|
Java
java是值传递还是引用传递
本文澄清了Java中参数传递的常见误解,总结出Java采用“值传递”的方式。对于基本类型,传递其值的拷贝,方法内修改不影响原值;而对于对象类型,则传递其引用地址的拷贝,尽管是拷贝,但因指向同一对象,故方法内的修改会影响原对象状态。形参仅在方法内部有效,而实参则是调用方法时传递的具体值。通过示例和比喻(如复刻仓库钥匙),形象地解释了值传递、引用传递及Java特有的“共享对象传递”概念,帮助理解不同情况下参数传递的行为差异。
|
3月前
|
Java
java中的值传递和引用传递
【8月更文挑战第3天】在Java中,值传递用于基本数据类型,传递的是值的副本,因此方法内的修改不影响原值;而引用传递用于对象和数组,虽传递的是引用的副本,但对对象内容的修改会影响原始对象。理解这两者对于正确处理方法调用及参数至关重要。
|
3月前
|
Java
java中的值传递和引用传递
【8月更文挑战第2天】在Java中,基本数据类型如`int`、`double`等采用值传递,传递的是变量值的副本,因此方法内的修改不影响原变量。对象类型则通过引用传递,传递的是对象引用的副本,允许方法内修改原对象。例如,对`StringBuilder`对象的修改会影响原始对象。
|
6月前
|
存储 安全 Java
Java方法的值传递技术详解
Java方法的值传递技术详解
41 3
|
5月前
|
Java
Java的值传递与“引用传递”辨析
Java的值传递与“引用传递”辨析
26 0
|
6月前
|
JavaScript 前端开发 Java
【JAVA面试题】什么是引用传递?什么是值传递?
【JAVA面试题】什么是引用传递?什么是值传递?
|
6月前
|
Java
每日一道Java面试题:Java是值传递还是引用传递?
每日一道Java面试题:Java是值传递还是引用传递?
37 1
|
6月前
|
存储 Java
如何理解Java是按值传递
如何理解Java是按值传递
|
Java
JAVA参数传值机制中值传递和引用传递
JAVA参数传值机制中值传递和引用传递
95 0
|
Java iOS开发
【Java基础】值传递和引用传递?引用传递的本质是什么?
【Java基础】值传递和引用传递?引用传递的本质是什么?
100 0
下一篇
无影云桌面