`
Josh_Persistence
  • 浏览: 1645834 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类

Java变量之间传值 - 值传递还是引用传递的讨论

    博客分类:
  • Java
阅读更多

Java变量之间传值时可分为值传递和引用传递, 那么它们有何区别?

 

1. 简单类型是按值传递的

    Java 方法的参数是简单类型的时候,是按值传递的 (pass by value),需要注意的是,对于基本类型的包装类型,因为JVM的自动拆箱操作,包装类型变成基本类型后也会按基本类型来进行操作,那么也是按值进行传递。这一点我们可以通过一个简单的例子来说明:

 

public class PrimitiveValuePass {
	
	public static void main(String[] args) {
		boolean test = true;
		
		System.out.println(" ======= Test Pass1 ======= ");
		System.out.println("Before pass : test =  " + test);
		pass1(test);
		System.out.println("After pass : test = " + test);
		
		
		System.out.println(" ======= Test Pass2 ======= ");
		Boolean test2 = true;
		System.out.println("Before pass : test =  " + test);
		pass2(test2);
		System.out.println("After pass : test = " + test);
		
	}
	
	// Primitive value pass
	public static void pass1(boolean test) {
		test = !test;
		System.out.println("in pass : test = " + test);
	}
	
	// 会自动拆箱为基本类型
	public static void pass2(Boolean test) {
		test = !test;
		System.out.println("in pass : test = " + test);
	}
	
	

}

 

输出结果:

 ======= Test Pass1 ======= 

Before pass : test =  true

in pass : test = false

After pass : test = true

 ======= Test Pass2 ======= 

Before pass : test =  true

in pass : test = false

After pass : test = true 

 

         不难看出,虽然在 pass1(boolean) 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对 main(String[]) 方法里的 test 变量没有影响。那说明,参数类型是简单类型的时候,是按值传递的。以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。

 

2. 什么是引用

        Java 是传值还是传引用,问题主要出在对象的传递上,因为 Java 中简单类型没有引用。既然争论中提到了引用这个东西,为了搞清楚这个问题,我们必须要知道引用是什么?

 

        简单的说,引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,我们可以把它想象为类似 C 语言中指针的东西,它指示了对象在内存中的地址——只不过我们不能够观察到这个地址究竟是什么。

        如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间来保存。但是它们的值是相同的,都指示同一个对象在内存的中位置。比如

 

String a = "Hello";
String b = a;
 

 

        这里,a 和 b 是不同的两个引用,我们使用了两个定义语句来定义它们。但它们的值是一样的,都指向同一个对象 "Hello"。也许你还觉得不够直观,因为 String 对象的值本身是不可更改的 (像 b = "World"; b = a; 这种情况不是改变了 "World" 这一对象的值,而是改变了它的引用 b 的值使之指向了另一个 String 对象 a)。那么我们用 StringBuffer 来举一个例子:

 

public class Test {
    public static void main(String[] args) {
        StringBuffer a = new StringBuffer("Hello");
        StringBuffer b = a;
        b.append(", World");
        System.out.println("a is " + a);
    }
}
 

 

运行结果:
a is Hello, World

 

         这个例子中 a 和 b 都是引用,当改变了 b 指示的对象的值的时候,从输出结果来看,a 所指示的对象的值也改变了。所以,a 和 b 都指向同一个对象即包含 "Hello" 的一个StringBuffer 对象。

       这里描述了两个要点:

 

      1. 引用是一种数据类型,保存了对象在内存中的地址,这种类型即不是我们平时所说的简单数据类型也不是对象(类实例);

      2. 不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类类型的变量。

 

3. 按引用传递是什么?

       指的是在方法调用时,传递的参数是按引用进行传递,按前面的表述,传递引用其实就是传递引用的地址,也就是变量所对应的内存空间的地址。

事例:

 

public class ObjPass {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ObjPass pass = new ObjPass();
		Person person = pass.new Person();
		person.age = 10;
		
		System.out.println("Before pass : main 方法中的age = " + person.age);
		pass.passTest(person);
		
		System.out.println("After pass : main 方法中的age = " + person.age);
		

	}
	
	public void passTest (Person person) {
		person.age = 20;
		System.out.println("passTest中的age = " + person.age);
	}
	
	class Person {
		public int age = 0;
	}

}
 输出结果:

 

Before pass : main 方法中的age = 10

passTest中的age = 20

After pass : main 方法中的age = 20

 

从这个例子可以看出,对象类型确实是按引用进行传递的,而不是按值的拷贝进行传递的。

     

4、一定要注意String对象的传递 

      从3的例子的输出结果可以看出,对象的传递是引用传递,当引用指向的值发生改变后,传递的那个引用所指向的值当然就发生了改变。但是赋值方式的String的传递相当于值传递,而不是引用传递,即

String str = "Hello world"的传递不是引用传递,对String有一定了解的童鞋相信都知道为啥,因为String类很特殊,对于:

 

String str = "123";
str = "hello world";
 Java将会创建两个String对象,既然是两个String对象,其中一个对象的内容变了,当然不会影响另一个对象。看一个具体的例子

 

 

public class StringValuePass {

	public static void passTest(String str) {
		System.out.println(" === before append: hash code: " + str.hashCode());
		str = " hello : " + str;
		System.out.println(" === After append: hash code: " + str.hashCode());
		System.out.println("in pass : str = " + str);
	}
	
	public static void main(String[] args) {
		System.out.println(" ======= Test Pass3 ======= ");
		String str = "wangsheng";
		System.out.println("Before pass : str =  " + str + " hash code: " + str.hashCode());
		passTest(str);
		System.out.println("After pass : str = " + str);
	}
}
输出结果:

 

 ======= Test Pass3 ======= 

Before pass : str =  wangsheng hash code: -1185442234

 === before append: hash code: -1185442234

 === After append: hash code: -414738574

in pass : str =  hello : wangsheng

After pass : str = wangsheng

 

从输出结果的hash code值以及str的值,结果一目了然。

 

5、 结论:

从上面执行的结果,我们不难得出下面的结论:

(1):“在Java里面参数传递都是按值传递” 这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值所以统称按值传递。

(2):在Java里面只有基本类型和按照下面这种定义方式的String是按值传递,其它的都是按引用传递。就是直接使用双引号定义字符串方式:String str = "Hello World";

 

所以你可以说是按值传递,如结论1, 也可以严格的说大多对象是按引用传递,如结论2。

 

6、番外篇:

       就像光到底是波还是粒子的问题一样,Java 方法的参数是按什么传递的问题,其答案就只能是:即是按值传递也是按引用传递,只是看问题的角度不同,结果也就不同。

        正确看待传值还是传引用的问题

          要正确的看待这个问题必须要搞清楚为什么会有这样一个问题。

          实际上,问题来源于 C,而不是 Java。

          C 语言中有一种数据类型叫做指针,于是将一个数据作为参数传递给某个函数的时候,就有两种方式:传值,或是传指针,它们的区别,可以用一个简单的例子说明:

void SwapValue(int a, int b) {
    int t = a;
    a = b;
    b = t;
}

 

void SwapPointer(int * a, int * b) {
    int t = * a;
    * a = * b;
    * b = t;
}

 

void main() {
    int a = 0, b = 1;
    printf("1 : a = %d, b = %d\n", a, b);
    SwapValue(a, b);
    printf("2 : a = %d, b = %d\n", a, b);
    SwapPointer(&a, &b);
    printf("3 : a = %d, b = %d\n", a, b);
}

 

运行结果:
1 : a = 0, b = 1
2 : a = 0, b = 1
3 : a = 1, b = 0

 

      大家可以明显的看到,按指针传递参数可以方便的修改通过参数传递进来的值,而按值传递就不行。

当 Java 成长起来的时候,许多的 C 程序员开始转向学习 Java,他们发现,使用类似SwapValue 的方法仍然不能改变通过参数传递进来的简单数据类型的值,但是如果是一个对象,则可能将其成员随意更改。于是他们觉得这很像是 C 语言中传值/传指针的问题。但是 Java 中没有指针,那么这个问题就演变成了传值/传引用的问题。可惜将这个问题放在 Java 中进行讨论并不恰当。

讨论这样一个问题的最终目的只是为了搞清楚何种情况才能在方法函数中方便的更改参数的值并使之长期有效。

 

Java 中,改变参数的值有两种情况:

第一种,使用赋值号“=”直接进行赋值使其改变,如PrimitiveValuePass ;

第二种,对于某些对象的引用,通过一定途径对其成员数据进行改变,如ObjPass。对于第一种情况,其改变不会影响到方法该方法以外的数据,或者直接说源数据。而第二种方法,则相反,会影响到源数据——因为引用指示的对象没有变,对其成员数据进行改变则实质上是改变了该对象。

7
8
分享到:
评论
6 楼 princekiki 2014-05-16  
String类本来final的,用来举例总感觉哪里有点问题没理清
5 楼 暨林瀚 2014-01-24  
非常棒,受益很大,终于弄明白了,谢谢.
4 楼 huangwen3300 2013-06-17  
受教了,虽然明白这个,这下更清楚了
3 楼 红桃貳 2013-06-17  
2 楼 Josh_Persistence 2013-06-16  
不用谢,多多讨论。
1 楼 宋建勇 2013-06-16  
讲解的不错,理解上又进了一层!多谢!

相关推荐

    Java是传值还是传址引用

    ### Java是传值还是传址引用 #### 一、简单类型是按值传递的 Java在处理简单数据类型(如int、boolean等)时采用的是按值传递的方式。这意味着当你将一个简单类型的值作为参数传递给一个方法时,实际上传递的是这...

    java及C++中传值传递、引用传递和指针方式的理解.docx

    ### Java及C++中传值传递、引用传递和指针方式的理解 在程序设计语言中,函数调用时参数的传递方式对理解程序的行为至关重要。本文将深入探讨Java与C++这两种广泛使用的编程语言中参数传递的方式,包括值传递、引用...

    JSP中java代码与js之间的传值

    - **通过JSP脚本元素**:在JSP中,可以使用 `<script>` 标签直接输出JavaScript代码,将Java变量的值传递给JavaScript。例如: ```jsp var myValue = "<%= javaVariable %>"; ``` - **通过JSP表达式语言...

    java 之方法调用 方法传参 值传递还是引用传递字节码

    本文将深入探讨Java中的方法调用、值传递与引用传递,并通过字节码分析来进一步理解这些概念。 首先,我们来看方法调用。在Java中,方法是一组完成特定任务的代码块,可以通过方法名来调用执行。方法调用的基本语法...

    java中传值还是传引用的的认识

    "java中传值还是传引用的认识" Java 中的参数传递是值传递还是引用传递?这是一个经常引发讨论的问题。在 Java 中,参数传递是按值传递的,也就是说,传递给方法的参数是一个副本,而不是原始值本身。 当一个对象...

    Java传值还是引用

    值传递和引用传递的概念对于理解Java中的方法调用至关重要。基本类型参数的值在方法内部的任何修改都不会影响到方法外部的原始变量。而引用类型参数虽然看起来似乎可以修改外部变量,但实际上是通过修改对象的状态来...

    java的传值与传值后的改变

    在Java编程语言中,"传值与传值后的改变"是一个关键的概念,涉及到函数调用时参数的传递方式。在Java中,有两种基本的数据类型:原始类型(如int, double, char等)和引用类型(如类实例、数组)。它们在函数调用时...

    JAVA中传值与引用问题

    在计算机编程领域,特别是对于面向对象语言如Java而言,“传值”与“传引用”的概念是理解数据传递机制的关键所在。Java作为一种广泛使用的高级编程语言,在设计之初就考虑到了如何避免C/C++等语言中的复杂性,特别...

    引用类型传值方法

    值类型如整型、浮点型、布尔型等,它们在赋值或作为参数传递时会进行副本复制,而引用类型则有所不同。本篇文章将详细探讨引用类型的传值方式。 首先,理解引用类型的概念至关重要。引用类型包括类(classes)、...

    java中传值与传引用

    在Java编程语言中,函数调用时的参数传递方式有两种:传值(Passing by Value)和传引用(Passing by Reference)。虽然Java官方文档中并未明确指出有传引用这一概念,但在实际操作中,Java的行为类似于传引用,尤其...

    Java-Java面向对象中引用传递教程

    - **传值**:基本数据类型(如int、char等)的参数传递是值传递,也就是说,方法内部对参数的修改不会影响到方法外部的变量。 - **传引用**:对于对象,Java总是采用引用传递。这意味着,即使在方法中改变对象的...

    java及C++中传值传递、引用传递和指针方式的理解

    Java和C++对待参数传递有着不同的处理机制,这主要体现在值传递、引用传递和指针方式上。 首先,我们来看Java的值传递。Java中,所有的参数传递都是基于值的。这意味着当一个对象作为参数传递给方法时,实际上是...

    23.Java对象作为参数传递是传值还是传引用1

    许多人认为对象是按引用传递,而实际上,Java总是按值传递,包括对象。这里的“值”指的是对象的引用,而不是对象本身。这与C++或C#等其他语言中对象的传递方式有所不同。 首先,我们需要理解Java中的引用概念。当...

    Java参数传递PPT

    当讨论方法参数时,有两种术语经常被提及:值传递(Call by Value)和引用传递(Call by Reference)。Java参数传递的规则是:**Java只使用值传递,但这种值传递对于对象参数表现为类似引用传递的行为。** 在值传递...

    Java中参数传值的代码清单.pdf

    Java只支持两种类型的参数传递:基本数据类型(如int、double等)的传值和引用类型(如类对象)的传值。以下是对给定代码清单的详细解释: 1. **基本数据类型传值**: 在Java中,基本数据类型的参数传递是按值传递...

    Java到底是传引用还是传值Java开发Java经验技巧共

    Java编程语言在处理参数传递时遵循一种特殊的方式,它既不是纯粹的按值传递,也不是纯粹的按引用传递。理解这一点对于深入学习Java至关重要。在Java中,基本数据类型(如int、float、char等)是按值传递的,而对象则...

    Java的引用和函数参数传递

    ### Java的引用与函数参数传递详解 #### 一、Java中的引用基础 在Java中,对象的引用扮演着类似于其他编程语言(如C/C++)中指针的角色,但是Java的引用比传统的指针更加安全且易于管理。这是因为Java的设计者们...

    浅谈Java中方法的参数传值.pdf

    综上所述,通过Java方法参数传递的知识点,我们可以更深入地理解Java程序的运行机制以及变量、数据类型和方法调用之间的关系。这不仅帮助编程者更好地组织代码结构,还能够在调试过程中快速定位问题所在,提高开发...

    分析java的传值问题

    本文将深入探讨Java中基本类型与引用类型的数据传递机制,并通过具体的示例代码来阐述这两者之间的区别。 #### 基本类型传值 Java中的基本数据类型包括`int`、`char`、`boolean`等。当我们将这些类型的变量作为...

Global site tag (gtag.js) - Google Analytics