为什么Java中只有值传递

参数传递

在我们日常编写代码的过程中,调用函数可能是最常见的操作了。那么,在调用函数时,参数是怎么样传递的呢?

值传递

相信有很多人都是学C语言入门的,刚开始写代码时,用的最多的就是值传递了。

void plus_one(int a){
  a++;
  printf("a: %d", a);
}

int main(){
  int n = 10;
  plus_one(n);
  printf("n:%d", n);
  return 0;
}

这是一个简单的值传递的例子,无需多言,plus_one函数的作用就是将传进来的数加一,然后输出。所谓值传递,就是直接将实参n的值赋给形参a,赋值完成之后,两者再无瓜葛。

因此,上面的代码可以等效为:

int main(){
  int n = 10;

  // plus_one start
  int a;
  a = n;
  a++;
  printf("a: %d", a);
  // plus_one end

  printf("n:%d", n);
  return 0;
}

可以看到,值传递简单直观,然而,调用函数并不能改变实参n的值。

指针传递

那么,当我们需要改变实参的值的时候,我们就会想到使用指针传递,也就是所谓的地址传递。

void plus_one(int* p){
  *p = *p + 1;
}

int main(){
  int n = 10;
  plus_one(&n);
  printf("The result is %d", n);
  return 0;
}

这里,我们将实参n的地址传入plus_one函数,在函数中,直接对指针p所指向的值,也就是n做操作,自然就可以改变实参n的值了。

实际上,指针传递也是值传递。我们将上面的代码改写:

int main(){
  int n = 10;

  // plus_one start
  int* p;
  p = &n;
  *p = *p + 1;
  printf("The result is %d", n);
  // plus_one end

  return 0;
}

可以看到,所谓的指针传递,也只不过是将变量n的地址值赋给指针变量p,实际上也是值传递。

所以,可以不负责任的概括为,C语言中只有值传递;

引用传递

指针固然强大,但是由于代码不易读,难以理解等问题,也是广为诟病。C++作为C语言的超大杯,引入了引用传递来简化指针传递的写法。

void plus_one(int& a){
  a++;
}

int main(){
  int n;
  plus_one(n);
  printf("The result is %d", n);
  return 0;
}

C++中,对&运算符进行了重载,实现了引用传递。具体实现为,在调用plus_one函数时,在函数调用栈中存变量n的地址,而不是n的值。因此,plus_one中的变量a就相当于是n的"别名",对a操作时,自然会改变n的值。

可见,引用传递的底层也是赋值操作。

Java中的参数传递

那么,在Java中,究竟是引用传递,还是值传递呢?

Java中变量分为基本变量和对象,我们不妨分别讨论。

基本变量类型

首先,对于int、char等基本类型,Java是使用值传递的,很容易验证。

static void plusOne(int a){
  a++;
  System.out.println("a: " + a);
}

public static void main(String[] args){
  int n = 10;
  plusOne(n);
	System.out.println("n: " + n);
}

显然,与C语言中一样,这里n的值是不会改变的。

对象

public class PassObject {
  public static void main(String[] args) {
    Dog myDog = new Dog("Test");
    foo(myDog);
    System.out.println(myDog.getName());// TestPlus
  }

  public static void foo(Dog dog) {
    dog.setName("TestPlus");
  }
}

class Dog{

  private String name;

  public Dog(String name) {
    this.name = name;
  }

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

  public String getName() {
    return name;
  }
}

通过上面的例子可以看到,传入对象的引用时,是可以改变对象的属性变量的。那么Java在传递对象作为参数时,是引用传递吗?

实际上并非如此,Java中,对象的引用,实际上相当于对象的指针。在Java中操作对象,只有通过引用操作这一种途径。某种意义上,Java中是不能直接操作对象的。

也就是说,在上例中传参时,没有对myDog对象实例做任何操作,只是把myDog引用值赋给了foo函数中的本地变量dog。并没有像引用传递一样,传入对象实体,但是只在栈中保存对象引用的操作。所以,Java中传递对象时,也是值传递。

所以,Java中只有值传递。

值得一提

然而,还是会有一些特殊情况,会让人怀疑上述结论。

数组

上面只分析了基本变量类型和对象,数组呢?

实际上,Java中的数组也是一种对象,数组类也是继承自Object类。在将数组作为参数时,也是传递的数组的引用,并没有传递数组的实体。

public static void changeContent(int[] arr) {
  arr[0] = 10;
}

public static void changeRef(int[] arr) {
  arr = new int[2];
  arr[0] = 15;
}

public static void main(String[] args) {
  int [] arr = new int[2];
  arr[0] = 4;
  arr[1] = 5;

  changeContent(arr);
  System.out.println(arr[0]); // 10
  changeRef(arr);
  System.out.println(arr[0]); // 10
}

在上例中可以看到,将传入的数组引用赋给一个新的数组后,这个引用就不能操作之前的数组了。

关于引用,英文是reference,实际上,我自认为,翻译为句柄是更为贴切的,引用就像是一个柄,一个Handler,你可以用它操作实体,但他并不是实体本身。就像手柄可以操控游戏机,但不是游戏机本身,当你将这个手柄连接到另一个游戏机的时候, 它就不能操控之前的游戏机了。

包装类和String

public static void main(String[] args) {
  Integer n = 1;
  plusOne(n);
  System.out.println(n); // 1
}

private static void plusOne(Integer n) {
  n = n + 1;
  System.out.println(n);// 2
}

在这段代码中,n作为Integer类型实例的句柄,却并没有成功改变对象的值,这是为什么呢?

在Integer类中,存对应值的属性是value,其声明如下:

private final int value;

可见,value值是不能改的,那加的操作是怎么实现的呢?

在上述加一的过程中,会重新new一个Integer对象,让后将这个对象赋给引用n。这样以来,之前的对象自然是不会改变的。

实际上,包装类以及String类的值,都是final的,所以在执行+的过程中,都会重新生成一个对象,然后对它赋值。

以上就是为什么Java中只有值传递的详细内容,更多关于Java 值传递的资料请关注我们其它相关文章!

(0)

相关推荐

  • JAVA参数传递方式实例浅析【按值传递与引用传递区别】

    本文实例讲述了JAVA参数传递方式.分享给大家供大家参考,具体如下: 首先要明确的是JAVA中没有引用传递, 全部是按值调用 令大家所费解的 当对象引用作为参数时  函数为什么能修改真实的对象呢?这不是引用传递的特征吗? 尤其先学习C++再学习JAVA的同学(比如说我自己)会这样认为, 用白话解释就是: 引用传递指针时, 连函数操作的指针都是原来的指针,比如C++传入对象时直接用传入的指针操作 而JAVA的引用传进函数时 , 会复制一个引用与实参指向同一对象的引用, 操作的不是同一个引用, 如果

  • 深入了解为什么Java中只有值传递?

    1.为什么 Java 中只有值传递? 首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语.按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址.一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值. 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式. Java程序设计语言总是采用按值调用.也就是说,方法得到的是所有参数值的一个拷贝,也就

  • Java按值传递和按址传递(面试常见)

    先复制一个面试/笔试的题: 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答案: 是值传递.Java语言的方法调用只支持参数的值传递.当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用.对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的.C++和C#中可以通过传引用或传输出参数来改变传入的参数的值.在C#中可以编写如下所示的代码,但是在Java中却做不到. java中的按值传

  • java通过实例了解值传递和引用传递

    这篇文章主要介绍了java通过实例了解值传递和引用传递,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.java中的值传递的问题 指的是在方法调用时,传递的参数是按值的拷贝传递.示例如下: public static void main(String[] args) { int a=1; change(a); System.out.println("交换a后的值:"+a); } private static void change(

  • 一文秒懂java到底是值传递还是引用传递

    首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语.按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址.一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值. 它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式. Java 程序设计语言总是采用按值调用.也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参

  • 探讨Java中函数是值传递还是引用传递问题

    相信有些同学跟我一样,曾经对这个问题很疑惑.在网上也看了一些别人说的观点,评论不一.有说有值传递和引用传递两种,也有说只有值传递的,这里只说下个人见解 先给大家介绍下概念 值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值. 引用传递:(形式参数类型是引用数据类型参数):也称为传地址.方法调用时,实际参数是对象(或数组),这时实际参数与

  • Java值传递之swap()方法不能交换的解决

    自己写了一个Swap测试类,代码如下: swap不能交换原生数据类型以及字符串类型. public class Swap5 { public static void main(String[] args) { // String x = "x111"; // String y = "y111"; String x = new String("x111"); String y = new String("y111"); swap

  • 详解java的值传递、地址传递、引用传递

    详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前的理解,java中基本数据类型是值传递,对象是地址(引用)传递.给大家看个例子: public class ObjectTrans { public static void main(String[] args) { String name = "123"; SChange(name);

  • Java中值传递和引用传递的区别

    在Java中参数的传递主要有两种:值传递和参数传递: 下面是对两种传递方式在内存上的分析: 一:值传递 解释:实参传递给形参的是值  形参和实参在内存上是两个独立的变量 对形参做任何修改不会影响实参 代码示例如下: package arrayDemo; public class Demo1 { public static void main(String[] args) { int b =20; change(b);// 实参 实际上的参数 System.out.println(b); } pu

  • 解析Java按值传递还是按引用传递

    1:按值传递是什么 指的是在方法调用时,传递的参数是按值的拷贝传递.示例如下: public class TempTest { private void test1(int a){ //做点事情 } public static void main(String[] args) { TempTest t = new TempTest(); int a = 3; t.test1(a);//这里传递的参数a就是按值传递 } } 按值传递重要特点:传递的是值的拷贝,也就是说传递后就互不相关了. 示例如下

随机推荐