为什么java值传递时传递的是一份拷贝?

大家都知道,java里面,无论是引用传递还是值传递,实际上传递的只是他们的一份拷贝,但是我有一点想不明白,为什么要传递拷贝,而不能传递他们原生的值或引用呢?求大神门指点一下迷津,万分感谢。

回答:

大更新后,原来的回答就删了

为了回答Lance_D的提问,重新翻看了一下java虚拟机的书。

1.先看一段代码

public static void main(String args[]){

char[] c1 = {'a','b'};

System.out.println("c1:"+c1[0]+" "+c1[1]);

changer1(c1);

System.out.println("c1 change1 after:"+c1[0]+" "+c1[1]);

changer2(c1);

System.out.println("c1 change2 after:"+c1[0]+" "+c1[1]);

}

private static void changer1(char[] c1) {

char[] c2 = {'e','f'};

c1 = c2;

}

private static void changer2(char[] c1) {

char[] c2 = {'e','f'};

c1[0] = c2[0];

}

输出

c1:a b

c1 change1 after:a b

c1 change2 after:e b

看一下内存里change1的真实过程
图片描述

change2的过程
图片描述

change3的过程
图片描述

2.理解String

Java中的String类其实就是char数组的包装类,源码:

/** The value is used for character storage. */

private final char value[];

/** Cache the hash code for the string */

private int hash; // Default to 0

public String(String original) {

this.value = original.value;

this.hash = original.hash;

}

所以不管是String s1="ab";或者String s1=new ("ab");其实真实的情况是
图片描述

s1 = "cd"
图片描述

3.重新看一下java虚拟机中支持的原始数据类型与引用数据类型

原始数据类型

  • 数值类型
  • boolean类型
  • returnAddress类型

数值类型

byte类型:值为8位有符号二进制补码整数,默认为零

short类型:值为16位有符号二进制补码整数,默认为零

int类型:值为32位有符号二进制补码整数,默认为零

long类型:值为64位有符号二进制补码整数,默认为零
char类型:值为16位无符号整数表示的、指向基本多文种平面的Unicode码点,以UTF-16编码,默认值为Unicode的nell码点('\u0000')

float类型与double类型要复杂一些

boolean类型

值为布尔值truefalse,默认为false

returnAddress类型

表示一条字节码指令的操作码(opcode)

引用数据类型

  • 类类型
  • 数组类型
  • 接口类型

分别由类实例、数组实例和实现了某个接口的类实例动态创建

4.看个复杂一点点的实例

public class Test {

class Student{

public String name;

public int number;

public Student(String sName, int sNumber){

name = sName;

number = sNumber;

}

}

public void test(){

Student s1 = new Student("Jack",1);

System.out.println("s1:"+s1.name+" "+s1.number);

change1(s1);

System.out.println("s1:"+s1.name+" "+s1.number);

change2(s1);

System.out.println("s1:"+s1.name+" "+s1.number);

}

private void change1(Student s1) {

s1 = new Student("Mack",2);

}

private void change2(Student s1) {

s1.name = "Mack";

s1.number = 2;

}

public static void main(String args[]){

new Test().test();

}

}

输出:

s1:Jack 1

s1:Jack 1

s1:Mack 2

实际change1过程:

图片描述

change2过程:
图片描述

回答:

看你怎么理解引用跟值了。从某种角度上说引用也是值,所以在说我的理解前先下个定义:

Object reference= (Object)value;

上式中reference 是所谓的“引用”, value是所谓的“值”。

从这个定义上来说,java的传递全都是“值传递”。

令Object reference2 = (Object)value;

然后传递reference2 去操作value。reference2是函数范围内的局部变量,用完即收回。

至于你说的“实际上传递的只是他们的一份拷贝”,如果把“他们”理解为reference的话那么这句话是对的。为什么呢?因为java只想传递value,不想修改reference,所以传递一个reference的copy。

那么问题来了:为什么java不能传reference呢?

答:这个问题其实要问java设计者了。个人觉得这么设计牺牲了一些灵活性,但是获得的好处么。。。你想到了C++的指针了吗

回答:

是因为java里 没有指针?

java里的reference是一个抽象的概念, 里面放得也不是地址. 试想一下, 每次gc后, 对象实例有可能在不同的 代中移动, 内存地址就变了! 所以java中不会直接操作地址.

回答:

java只有值传递,这句话的意思从底层来看,就是栈上的东西永远会被复制。int存于栈上所以传参的时候会被复制。

对于object,他的引用存于栈上,但是实例在托管堆上,复制的是它的引用,实例本身并没有被复制,所以效果相当于引用传值。在子函数里面,对实例本身的任何操作,比如调用其方法,都会影响外面,但是如果你重新new了一次,不会影响到外面的。

回答:

多看看jvm内存

回答:

值传递传的是拷贝,引用传的是地址。

回答:

基本数据类型是值传递, 其他的是引用传递吧

回答:

看怎么理解,可以理解为Java只有值copy。

先看基本类型:

int a = 10;

有个方法 doWithInt(int a), 当调用doWithInt(a)的时候,实际是把a的值10传递过去了。

再看对象类型:

Object obj = new Object();

有个方法doWithObj(Object obj), 当调用doWithObj(obj)的时候,实际是把obj的引用这个值(比如是0x4123fac9)传过去了。注意这里的值,是指的obj的引用,对于一个对象来说,能标示它的唯一值的就是他的地址引用。

所以你看,理解万岁啊。

回答:

在MF的重构中有提交,java本身是值传递,如果传递常量,是传递拷贝,如果传递的是对象,则传递的是引用指向拷贝,引用本身指向是不能改变,但是能够改变引用本身的内容。

理解为传递的是引用指向的拷贝即可,指向不能更改,引用所引用的内容可以更改。

回答:

如果传入参数是对象的话,实际是对象的引用,根据按值传递的原则,又新建了一个引用副本,所以在方法体内对该引用内容进行修改,当然会直接改变所指向对象的值。而对于基本类型的参数,我们传递值后又建了这个值的副本,对这个副本的修改自然不会修改原参数的值。

以上是 为什么java值传递时传递的是一份拷贝? 的全部内容, 来源链接: utcz.com/p/172903.html

回到顶部