1、正确看待传值还是传引用的问题
首先说,为什么会有这样一个问题。实际上,问题来源于C,而不是Java。
C 语言中有一种数据类型叫做指针,于是将一个数据作为参数传递给某个函数的时候,就有两种方式:传值,或是传指针,它们的区别,可以用一个简单的例子说明:
void Exchg1(int x, int y) /* 传值*/ { int tmp; tmp = x; x = y; y = tmp; printf("x = %d, y = %d.\n", x, y); } void Exchg2(int *px, int *py) /* 传指针*/ { int tmp = *px; *px = *py; *py = tmp; printf("*px = %d, *py = %d.\n", *px, *py); } main() { int a = 4; int b = 6; Exchg1(a, b); printf("a = %d, b = %d.\n”, a, b); a=4;b=6; Exchg2(&a, &b); printf("a = %d, b = %d.\n", a, b); }
依次输出为:
----------------------------------------- x = 6, y = 4. a = 4, b = 6. ----------------------------------------- *px = 6, *py = 4. a = 6, b = 4. -----------------------------------------
大家可以明显的看到,按指针或者引用传递参数可以方便的修改通过参数传递进来的值,而按值传递就不行。
在Java 成长起来的时候,许多的 C 程序员开始转向学习 Java,他们发现,使用类似例子中的Exchg的方法仍然不能改变通过参数传递进来的简单数据类型的值,但是如果是一个对象,则可能将其成员随意更改。
于是他们觉得这很像是 C 语言中传值/传指针的问题。但是 Java 中没有指针,那么这个问题就演变成了传值/传引用的问题。可惜将这个问题放在 Java 中进行讨论并不恰当。
讨论这样一个问题的最终目的只是为了搞清楚载何种情况下,才能在方法函数中方便的更改参数的值并使之长期有效。
如果不弄清楚这个问题,看似不影响你写出一段美妙的代码,但却有极大的隐患。不一定什么时候你就会莫名其妙,为什么我的返回参数没有起作用,为什么我传进去的参数变成了这样?
2、基本数据类型的参数传递
Java 方法的参数是基本类型的时候,是按值传递的 (pass by value),这一点是毋庸置疑的。我们可以通过一个简单的例子来说明:
public static void switchValue (int a,int b){ int c=a; a=b; b=c; } public static void main(String[] args) throws ParseException { int x=0; int y=1; switchValue(x,y); System.out.println("x="+x); System.out.println("y="+y); }
输出结果为:
------------------------ x=0 y=1 ------------------------
不难看出,虽然在方法switchValue中交换了两个参数的值,但对参数源并没有影响。也就是说基本数据类型的参数时,是传值的。传入的参数其实是参数源的一个拷贝,在实际方法中改变参数的拷贝,并不会影响参数源的值。
3、对象做为参数的传递
Java是传值还是传引用,问题主要出在对象传递上。前边也讲到基本数据类型是按照值传递的。要讨论这个问题,必须要知道什么是引用。
简单的说,引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接是访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,我们可以把它想象为类似 C 语言中指针的东西(实际有很大的区别),它指示了对象在内存中的地址——只不过我们不能够观察到这个地址究竟是什么。(注:Java里面没有指针的概念,并不是指Java中没有指针,Java的设计者巧妙的对指针的操作进行了管理。而在懂C的Java程序员眼中,Java到处都是精美绝伦的指针。)
如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间来保存。但是它们的值是相同的,都指示同一个对象在内存的中地址。
我们看一下下面这个例子:
public static void switchValue (int a,int b){ int c=a; a=b; b=c; } public static void main(String[] args) throws ParseException { int x=0; int y=1; switchValue(x,y); System.out.println("x="+x); System.out.println("y="+y); }
从例子中可以看到tricky方法的功能是,首先改变了arg1的x,y坐标,然后将arg1赋给一个临时对象temp,然后利用temp交换arg1和arg2。在main方法中,声明两个Point对象,其x,y都是0。然后调用tricky方法,但pnt1与pnt2到底发生的什么变化。在看结果之前,大家可以自己想一想。
输出结果:
X: 0 Y: 0 X: 0 Y: 0 ------------------- X: 100 Y:100 X: 0 Y: 0
从输出结果中可以看到,tricky函数成功地改变了pnt1的值。但是pnt1和pnt2的置换失败了。这正是最令人困惑的地方。
下面来分析下这个方法到底是怎么执行的。
在main()函数当中,pnt1和pnt2仅仅是对象的引用,它的实际的值是pnt1和pin2在内存中的地址,这个地址指向的才是pnt1和pnt2在堆内存中真正存在的对象。
当向tricky函数传递参数时,与基本数据类型一样,它传递的是该对象在内存中地址的拷贝,而不是真实的对象实例。在内存中,pnt1与arg1同时指向new Point(0,0)这个对象。也就是说,当一个对象当做参数传递时,在内存中至少有两个‘引用’同时指向该对象。
在tricky函数内部,arg1.x = 100;arg1.y = 100;这个两句代码执行的操作是,找到arg1这个地址指向的对象Point,将对象Point的x、y更改为100。由于这个操作直接修改了改Point对象,也就是说如果其他‘引用’也指向该对象,也会随之改变。
而在这三句代码Point temp = arg1;arg1 = arg2;arg2 = temp;,这个过程其实是一个地址的交换过程,在上文基本数据类型传参的过程中可以发现,基本类型在方法内部的改变并不会影响参数源的值,所以对于pnt1它的值还是原来的地址,指向的对象还是原来的对象,pnt2也一样。而arg1与arg2在函数tricky中交换后,两个参数的值即它们保存的地址,确实互换了,也就是说arg1与pnt2指向同一个对象,arg2与pnt1指向同一个对象。
总结一下,当对象作为参数传递时,如果方法内对该对象的成员数据进行操作,则会真正的改变该对象;如果对该对象的地址引用进行操作,则不会改变参数源。
仔细看可以发现,方法参数与参数源是地址的拷贝,两者并没有太大关系,对象之所以发生改变是因为方法参数与参数源指向的是同一内存地址,数据在内存中改变了,指向这块内存的所有变量都会改变。但单纯的操作该地址值,并不会影响方法外部的参数源。
了解这部分内容后,大家可以把tricky方法中的执行顺序改变一下,先进行交换,然后在赋值,如下:
public static void tricky(Point arg1, Point arg2){ Point temp = arg1; arg1 = arg2; arg2 = temp; arg1.x = 100; arg1.y = 100; }
然后仍然使用原来的main方法调用,可以自己先分析一下运行过程,看自己分析的结果与程序运行的结果是否一致。
4、总结
从上面的分析我们可以得到一下结论:
(1)基本数据类型作为参数传递时,传递的值该对象值的拷贝。
(2)对象作为参数传递时,传递的是该对象的地址的拷贝。
两种传递方法,都属于值传递,所以Java的参数传递,只有值传递。
原文地址:java中到底 是传值还是传引用?。转载请注明出处,谢谢。
相关推荐
"java中传值还是传引用的认识" Java 中的参数传递是值传递还是引用传递?这是一个经常引发讨论的问题。在 Java 中,参数传递是按值传递的,也就是说,传递给方法的参数是一个副本,而不是原始值本身。 当一个对象...
### Java是传值还是传址引用 #### 一、简单类型是按值传递的 Java在处理简单数据类型(如int、boolean等)时采用的是按值传递的方式。这意味着当你将一个简单类型的值作为参数传递给一个方法时,实际上传递的是这...
在Java编程语言中,函数调用时的参数传递方式有两种:传值(Passing by Value)和传引用(Passing by Reference)。虽然Java官方文档中并未明确指出有传引用这一概念,但在实际操作中,Java的行为类似于传引用,尤其...
理解Java中的传值与传引用对于编写正确且预期的行为代码至关重要。在编写函数时,应清楚地知道参数如何被传递,以及这将如何影响函数的可读性和行为。在实际编程中,合理运用这些知识能帮助避免许多常见的错误和困惑...
本文将深入探讨Java中的传值与传引用问题,并通过具体的例子来解析其中的原理。 #### 二、基础知识回顾 在Java中,所有的数据类型可以分为两大类:基本类型(如int, double等)和引用类型(如Object, String等)。...
### Java及C++中传值传递、引用传递和指针方式的理解 在程序设计语言中,函数调用时参数的传递方式对理解程序的行为至关重要。本文将深入探讨Java与C++这两种广泛使用的编程语言中参数传递的方式,包括值传递、引用...
"Java中的传值与传引用实现过程解析" Java中的传值与传引用是Java编程语言中的一种基础概念,它们是Java函数中参数传递的两种方式。 Java中的传值是指函数参数的值被复制到函数内部,在函数内部对参数的修改不会...
首先,我们需要理解Java中的引用概念。当创建一个对象,如`StringBuffer sb = new StringBuffer("Hello ");`,我们实际上是创建了一个新的对象,并为变量`sb`分配了一个内存地址,这个地址指向了新创建的对象。这个...
JAVA传值与传引用[整理].pdf
根据标题和描述,我们将深入探讨Java中传值与引用的区别,以及它们在实际编程中的应用。 首先,Java是一种“总是按值传递”的语言。这意味着无论是基本类型还是引用类型,当作为参数传递时,都会有一个副本被创建并...
在Java中,基本数据类型(如int、float、char等)是按值传递的,而对象则是按引用传递的。但这并不意味着对象本身被复制,而是对象的引用被复制。 首先,我们来看基本类型的参数传递。当一个基本类型的变量作为参数...
本篇文章将详细探讨引用类型的传值方式。 首先,理解引用类型的概念至关重要。引用类型包括类(classes)、接口(interfaces)、数组以及委托(delegates),它们在内存中的存储方式与值类型截然不同。对于引用类型...
- **通过JSP表达式语言(EL)**:使用EL表达式,如 `${javaVariable}`,可以直接在JavaScript代码中引用JavaBean或作用域内的变量。 2. **JavaScript到Java的传值**: - **表单提交**:通过JavaScript修改表单字段的...
在Java编程语言中,引用类型变量实际上是对象的引用或指针,而非对象本身。当我们讨论“引用类型传值”时,意味着在方法调用时,传递的是对象引用的一个副本,而不是对象本身。以下是对给定代码清单的详细解释: 1....
在Java编程语言中,方法参数传递机制涉及到两个主要概念:传值和传引用。了解这两个概念对于编写高效、可靠的代码至关重要。本篇文章将深入探讨Java中的方法传值和传引用问题。 首先,让我们理解什么是传值。在Java...
Java方法参数传值是Java编程中的基础知识点,涉及变量、数据类型和方法调用的机制。在Java中,数据类型分为基本数据类型和引用数据类型。基本数据类型包括布尔型(boolean)、字节型(byte)、短整型(short)、整型...
传址和传引用是一回事。 一门编程语言的核心是数据结构,粗略来讲,可以把数据结构分成不可变类型(immutable)和可变类型(mutable)。为什么这么分呢?这涉及到内存分配问题。对于不可变类型,只要分配有限的内存...
Java只支持两种类型的参数传递:基本数据类型(如int、double等)的传值和引用类型(如类对象)的传值。以下是对给定代码清单的详细解释: 1. **基本数据类型传值**: 在Java中,基本数据类型的参数传递是按值传递...