java中值传递和引用传递

值传递

package com.github.hcsp;

public class Main {
  public static void main(String[] args) {
    int i = 0;
    addOne(i);
    System.out.println(i);
  }

  static void addOne(int i) {
    i++;
  }
}


//输出结果为 0

因为 addOne 方法中传递的 i 只是 main 方法中的 i 的值的拷贝,所以不会对其产生任何影响。在执行完 addOne 方法以后,该方法空间会被销毁。

引用传递

package com.github.hcsp;

public class Main {
  public static void main(String[] args) {
    Cat cat = new Cat();
    cat.name = "haha";

    renameCat(cat);

    System.out.println(cat.name);
  }

  static void renameCat(Cat cat){
    cat.name = "mimi";
  }
}

//输出结果为mimi

为什么该程序就会改变 cat 的名字呢?因为方法中传递的是 Cat 变量引用 (地址) 的拷贝,所以,在 rename 方法中的 cat 指向的也是内存中同一只 “猫”。

Java 中有两种数据类型:

基本数据类型: int char byte boolean float double short long

引用数据类型 类 接口 数组

1 、从概念方面来说
1.1, 基本数据类型 : 变量名指向具体的数值
1.2, 引用数据类型 : 变量名不是指向具体的数值 , 而是指向存数据的内存地址 ,. 也及时 hash 值
2 、从内存的构建方面来说 ( 内存中 , 有堆内存和栈内存两者 )
2.1, 基本数据类型 : 被创建时 , 在栈内存中会被划分出一定的内存 , 并将数值存储在该内存中 .
2.2, 引用数据类型 : 被创建时 , 首先会在栈内存中分配一块空间 , 然后在堆内存中也会分配一块具体的空间用来存储数据的具体信息 , 即 hash 值 , 然后由栈中引用指向堆中的对象地址 .基本数据类型中会存在两个相同的 1 ,而引用型类型就不会存在相同的数据。
假如 “hello” 的引用地址是 xxxxx1 ,声明 str 变量并其赋值 “hello” 实际上就是让 str 变量引用了 “hello” 的内
存地址,这个内存地址就存储在堆内存中,是不会改变的,当再次声明变量 str1 也是赋值为 “hello” 时,
此时就会在堆内存中查询是否有 “hello” 这个地址,如果堆内存中已经存在这个地址了,就不会再次创建
了,而是让 str1 变量也指向 xxxxx1 这个地址,如果没有的话,就会重新创建一个地址给 str1 变量。

请谈一下值传递与引用传递?Java 中只有值传递么?

我们通过上面的程序简单了解了什么叫 “值”,什么叫 “引用”,接下来我们重点介绍一下值传递与引用传递。

请不要忽视这个问题,它也许没有你想的那么简单。绝大部分初学者很难搞懂究竟什么是值传递,什么是引用传递。

很多博客中,作者不仅没有解释清楚 “值传递” 与 “引用传递”,还混淆了很多错误的引导。

所以,在回答这个问题之前,我们先进行一次排雷检查,看一看你是否对值传递与引用传递有着错误的理解:

【观点 1】Java 中既有值传递也有引用传递
【观点 2】Java 中只有值传递,因为引用的本质就是指向堆区的一个地址,也是一个值。
如果你的观点符合上述两种观点的其中一种,那么你多半没有理解值传递和引用传递的概念。话不多说,接下来我来详细说明究竟什么是值传递,什么是引用传递,以及 Java 中为什么只有值传递。

值传递(Pass by value)与引用传递(Pass by reference)属于函数调用时,参数的求值策略(Evaluation Strategy)。求值策略的关注点在于,求值的时间以及传值方式:

求值策略 求值时间 传值方式
Pass by value 函数调用前 原值的副本
Pass by reference 函数调用前 原值(原始对象)
所以,区别值传递与引用传递的实质并不是传递的类型是值还是引用,而是传值方式,传递的是原值还是原值的副本。

如果传递的是原值(原对象),就是引用传递;如果传递的是一个副本(拷贝),就是值传递。再次强调一遍,值传递和引用传递的区别在于传值方式,和你传递的类型是值还是引用没有一毛钱关系!

Java 语言之所以只有值传递,是因为:传递的类型无论是值类型还是引用类型,Java 都会在调用栈上创建一个副本,不同的是,对于值类型而言,这个副本就是整个原始值的复制;而对于引用类型而言,由于引用类型的实例存储在堆中,在栈上只有它的一个引用,指向堆的实例,其副本也只是这个引用的复制,而不是整个原始对象的复制。

我们通过两个程序来理解下:

程序一:

public class Test {
    public static void setNum1(int num){
        num = 1;
    }
    public static void main(String[] args) {
        int a = 2;
        setNum1(a);
        System.out.println(a);
    }
}

程序二

public class Test2 {
    public static void setArr1(int[] arr){
        Arrays.fill(arr,1);
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        setArr1(arr);
        System.out.println(Arrays.toString(arr));
    }
}

程序一输出的结果为:2
程序二输出的结果为:[1,1,1,1,1]

程序一中,Java 会将原值复制一份放在栈区,并将这个拷贝传递到方法参数中,方法里面仅仅是对这个拷贝进行了修改,并没有影响到原值,所以程序一的输出结果为 2。

程序二中,Java 会将引用的地址复制一份放在栈区,复制的拷贝和原始引用都指向堆区的同一个对象。方法通过拷贝地址找到堆区的实例,对堆区的实例进行修改,而此时,原始引用仍然指向着堆区的实例,程序二的输出结果为:[1,1,1,1,1]

所以,在 Java 中,对于方法的参数传递,无论是原生数据类型,还是引用数据类型,本质上是一样的。

如果是传值,那就将值复制一份,如果是传引用(地址),就将引用(地址)复制一份。因为传入方法中的都是原始值的复制,所以说,Java 中只有 “值传递”。

参考链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值