java 参数调用,是值还是引用?

java 参数调用,是值还是引用?

 

是很多人,困扰的问题,不光是新手,和老手,

我搞了 java 这么多年也是没有搞清楚,最近下了一番功夫,才了解清楚,

 

先看个例子,

 

class MyStringTest() {

 

  public static void main(String[] args) {

 

    String a = "init value";

    System.out.println(a);

   

    modifyString(a);

    System.out.println(a);

 

  }

 

  private static void modifyString(String str) {

    str = "modified value";

  }

 

看这个code, 一般人认为,应该打印结果是,

init value

modified value,

 

但是,实际结果是,

init value

modified value,

 

从这个问题出发,我就想探究下,java 中究竟是传值,还是传引用,

这个问题,众说纷纭,有能让人明白的,有听了更不清楚的,

 

最后,我发现这个问题需要结合jvm 堆,栈的理解才能清楚,当然还有一些相关的概念要清楚,

你就应该明白这个问题的,比如,基本类型,引用类型。

 

1, 先说一说 JVM 的基本结构,

 

有 class loader, 执行引擎,运行时数据区,

 

 

 

2, 然后,类的加载和实例化流程,

 

类作为一个 .class 文件,被 class loader 加载,

然后,在被引用到的时候,

class loader, 将 .class 文件,加载进 jvm,

方法被放到方法区,

new 的 instance 放到 heap,

执行时的环境,放到 stack,

 

3, 方法调用,和栈的结构

 

 

 

然后呢,

引用对象,都是产生在 heap 上,因为大小,和生存期不固定,

基本对象,因为长度,生成期固定,所以生成在 stack,

 

而我们执行方法的时候,传递参数,都是 copy value,

 

例子1和分析,

 

比如,

Class TestMethod() {

 

theMethod(int theInt , Object theObj) {

  theInt = 10;

  theObj.change();

}

 

public static void main(String[] args) {

  int n = 20;

  Object obj = new Object();

 

  theMethod(n, obj);

}

}

 

在调用方法 theMethod 之前,我们在栈中,有一个当前环境,

一个obj 引用到堆上的 object O.

当前栈中,存的是一个引用地址,

 

还有一个是 n, 他是在栈中,生成的,所以直接就是,指到的就是 10这个 value,

 

然后,调用theMethod 方法,将现在的环境压栈,

开一个新栈,在新栈中,生成

theInt, 和theObj,

他们的值从n, 和 obj copy 过来。

 

那么 n 指向的地方,存的值是20,

theObj, 指向的地方,是 heap 中的 object,

 

在执行 theInt = 10 后,

我们会在 栈中,找新地方,存一个 10, 然后,将 theInt 指过去,

老的n 指向的地方不变。

 

在执行 theObj.change() 后,

我们会到 堆中的 object 对象存储区去,做相应的改动。

因为,obj 也是指向同一个地方,

 

那么,在方法执行后,推出的时候,我们会 pop 一个栈,

回到原始栈,

 

这时,你看,

n 还是指向,10 的位置,

obj 还是指向,new object instance 位置,

而由于heap 中 object instance 部分数据改了,所以 obj 也改了,

 

所以,实际都是传值的,

对于基本类型是这样,

对于引用类型,由于中间有一层引用关系, 【栈 到 heap 的 引用】,

所以,看起来是传递引用了,

 

例子2和分析,

再看下面的例子,

 

Class TestMethod() {

 

theMethod(Object theObj) {

  theObj = new Object("B");

  theObj.change();

}

 

public static void main(String[] args) {

  Object obj = new Object("A");

 

  theMethod(obj);

}

}

 

那么返回后, obj 是A 还是B呢,

根据上面的分析,

 

栈中的 obj, 变量,存的值,指向 heap 中的 object A instance,

在调用方法的时候,在创建新栈的时候,

新 theObj 变量,我们会将 obj 变量值,复制过去,也就是,指向 heap 中的 object A,

 

但是,在执行

theObj = new Object("B");

后,

theObj 变量,就不再指向 heap 中的object A instance 了,而是指向新的 object B instance,

这是调用 change() 方法,做改动,

也只是针对 object B instance,

 

在方法退出的时候,我们 pop 一个栈,恢复到以前的环境,

obj 所指向的 heap 中的 object A instance ,没有任何改变。

 

以上,就是我的分析,

可见,网上的一些分析,没有考虑栈和堆,就事论事,总是让大家不明白,

这也从一个方面说明了这个问题,

 

写java code, 也是要明白jvm 的基本实现逻辑,否则,就会不明白。

 

JVM堆的结构。

 

值不可变类型, 

 

同时,我们也要考虑到 String 类型的特殊性,

它的值是不变的,其他同样的 box 类型也是,

Boolean, Integer,

 

还有,

String str = "abc";

String str = new String("abc");

 

这些的区别,我们都应该清楚,

这样写起代码来,才能不犯错误。

 

网上发现的相关资料。

http://pengjiaheng.javaeye.com/blog/518623

http://www.javaeye.com/wiki/jvm/2905-JVM 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值