`

回顾Java值传递、引用传递

    博客分类:
  • J2SE
阅读更多
public class ParamTest {   
    // 初始值为0   
    protected int num = 0;   
  
    // 为方法参数重新赋值   
    public void change(int i) {   
        i = 5;   
    }   
  
    // 为方法参数重新赋值   
    public void change(ParamTest t) {   
        ParamTest tmp = new ParamTest();   
        tmp.num = 9;   
        t = tmp;   
    }   
  
    // 改变方法参数的值   
    public void add(int i) {   
        i += 10;   
    }   
  
    // 改变方法参数属性的值   
    public void add(ParamTest pt) {   
        pt.num += 20;   
    }   
  
    public static void main(String[] args) {   
        ParamTest t = new ParamTest();   
  
        System.out.println("参数--基本类型");   
        System.out.println("原有的值:" + t.num);   
        // 为基本类型参数重新赋值   
        t.change(t.num);   
        System.out.println("赋值之后:" + t.num);   
        // 为引用型参数重新赋值   
        t.change(t);   
        System.out.println("运算之后:" + t.num);   
  
        System.out.println();   
  
        t = new ParamTest();   
        System.out.println("参数--引用类型");   
        System.out.println("原有的值:" + t.num);   
        // 改变基本类型参数的值   
        t.add(t.num);   
        System.out.println("赋引用后:" + t.num);   
        // 改变引用类型参数所指向对象的属性值   
        t.add(t);   
        System.out.println("改属性后:" + t.num);   
    }   
}  

 这段代码的运行结果如下:

参数--基本类型
原有的值:0
赋值之后:0
运算之后:0

参数--引用类型
原有的值:0
赋引用后:0
改属性后:20

从上面这个直观的结果中我们很容易得出如下结论:

对于基本类型,在方法体内对方法参数进行重新赋值,并不会改变原有变量的值。
对于引用类型,在方法体内对方法参数进行重新赋予引用,并不会改变原有变量所持有的引用。
方法体内对参数进行运算,不影响原有变量的值。
方法体内对参数所指向对象的属性进行运算,将改变原有变量所指向对象的属性值。

上面总结出来的不过是我们所看到的表面现象。那么,为什么会出现这样的现象呢?这就要说到值传递和引用传递的概念了。这个问题向来是颇有争议的。

大家都知道,在JAVA中变量有以下两种:

基本类型变量,包括char、byte、short、int、long、float、double、boolean。
引用类型变量,包括类、接口、数组(基本类型数组和对象数组)。

当基本类型的变量被当作参数传递给方法时,JAVA虚拟机所做的工作是把这个值拷贝了一份,然后把拷贝后的值传递到了方法的内部。因此在上面的例子中,我们回头来看看这个方法:
Java代码 

// 为方法参数重新赋值    
public   void  change( int  i) {   
    i = 5 ;   
}   
Java代码 
// 为方法参数重新赋值   
public void change(int i) {   
    i = 5;   
}  

 // 为方法参数重新赋值
 public void change(int i) {
  i = 5;
 }

 在这个方法被调用时,变量i和ParamTest型对象t的属性num具有相同的值,却是两个不同变量。变量i是由JAVA虚拟机创建的作用域在 change(int i)方法内的局部变量,在这个方法执行完毕后,它的生命周期就结束了。在JAVA虚拟机中,它们是以类似如下的方式存储的:

 

很明显,在基本类型被作为参数传递给方式时,是值传递,在整个过程中根本没有牵扯到引用这个概念。这也是大家所公认的。对于布尔型变量当然也是如此,请看下面的例子:
Java代码 

public   class  BooleanTest {   
    // 布尔型值    
    boolean  bool =  true ;   
   
    // 为布尔型参数重新赋值    
    public   void  change( boolean  b) {   
        b = false ;   
    }   
   
    // 对布尔型参数进行运算    
    public   void  calculate( boolean  b) {   
        b = b && false ;   
        // 为了方便对比,将运算结果输出    
        System.out.println("b运算后的值:"  + b);   
    }   
   
    public   static   void  main(String[] args) {   
        BooleanTest t = new  BooleanTest();   
   
        System.out.println("参数--布尔型" );   
        System.out.println("原有的值:"  + t.bool);   
        // 为布尔型参数重新赋值    
        t.change(t.bool);   
        System.out.println("赋值之后:"  + t.bool);   
   
        // 改变布尔型参数的值    
        t.calculate(t.bool);   
        System.out.println("运算之后:"  + t.bool);   
    }   
}   

 Java代码

public class BooleanTest {   
    // 布尔型值   
    boolean bool = true;   
  
    // 为布尔型参数重新赋值   
    public void change(boolean b) {   
        b = false;   
    }   
  
    // 对布尔型参数进行运算   
    public void calculate(boolean b) {   
        b = b && false;   
        // 为了方便对比,将运算结果输出   
        System.out.println("b运算后的值:" + b);   
    }   
  
    public static void main(String[] args) {   
        BooleanTest t = new BooleanTest();   
  
        System.out.println("参数--布尔型");   
        System.out.println("原有的值:" + t.bool);   
        // 为布尔型参数重新赋值   
        t.change(t.bool);   
        System.out.println("赋值之后:" + t.bool);   
  
        // 改变布尔型参数的值   
        t.calculate(t.bool);   
        System.out.println("运算之后:" + t.bool);   
    }   
}  

public class BooleanTest {
 // 布尔型值
 boolean bool = true;

 // 为布尔型参数重新赋值
 public void change(boolean b) {
  b = false;
 }

 // 对布尔型参数进行运算
 public void calculate(boolean b) {
  b = b && false;
  // 为了方便对比,将运算结果输出
  System.out.println("b运算后的值:" + b);
 }

 public static void main(String[] args) {
  BooleanTest t = new BooleanTest();

  System.out.println("参数--布尔型");
  System.out.println("原有的值:" + t.bool);
  // 为布尔型参数重新赋值
  t.change(t.bool);
  System.out.println("赋值之后:" + t.bool);

  // 改变布尔型参数的值
  t.calculate(t.bool);
  System.out.println("运算之后:" + t.bool);
 }
}

 输出结果如下:

参数--布尔型
原有的值:true
赋值之后:true
b运算后的值:false
运算之后:true

那么当引用型变量被当作参数传递给方法时JAVA虚拟机又是怎样处理的呢?同样,它会拷贝一份这个变量所持有的引用,然后把它传递给JAVA虚拟机为方法创建的局部变量,从而这两个变量指向了同一个对象。在篇首所举的示例中,ParamTest类型变量t和局部变量pt在JAVA虚拟机中是以如下的方式存储的:

 

有一种说法是当一个对象或引用类型变量被当作参数传递时,也是值传递,这个值就是对象的引用,因此JAVA中只有值传递,没有引用传递。还有一种说法是引用可以看作是对象的别名,当对象被当作参数传递给方法时,传递的是对象的引用,因此是引用传递。这两种观点各有支持者,但是前一种观点被绝大多数人所接受,其中有《Core Java》一书的作者,以及JAVA的创造者James Gosling,而《Thinking in Java》一书的作者Bruce Eckel则站在了中立的立场上。

我个人认为值传递中的值指的是基本类型的数值,即使对于布尔型,虽然它的表现形式为true和false,但是在栈中,它仍然是以数值形式保存的,即0表示false,其它数值表示true。而引用是我们用来操作对象的工具,它包含了对象在堆中保存地址的信息。即使在被作为参数传递给方法时,实际上传递的是它的拷贝,但那仍是引用。因此,用引用传递来区别与值传递,概念上更加清晰。

最后我们得出如下的结论:

基本类型和基本类型变量被当作参数传递给方法时,是值传递。在方法实体中,无法给原变量重新赋值,也无法改变它的值。
对象和引用型变量被当作参数传递给方法时,在方法实体中,无法给原变量重新赋值,但是可以改变它所指向对象的属性。至于到底它是值传递还是引用传递,这并不重要,重要的是我们要清楚当一个引用被作为参数传递给一个方法时,在这个方法体内会发生什么。

分享到:
评论
1 楼 piao_bo_yi 2009-11-13  
Bruce Eckel可不是中立观点。

相关推荐

    Java参数传递PPT

    Java参数传递的规则是:**Java只使用值传递,但这种值传递对于对象参数表现为类似引用传递的行为。** 在值传递中,函数或方法接收的是原始数据类型(如int、double、boolean)参数的副本。例如,如果有一个方法...

    JNI中C和Java参数传递详细例子

    ### JNI中C和Java参数传递详解 #### 一、引言 Java Native Interface (JNI) 是Java平台标准的一部分,用于让Java代码与其他语言写的代码进行交互。本文将深入探讨在JNI环境中,C/C++和Java之间如何进行参数传递,...

    JAVA中传值与引用问题

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

    java 面向对象编程(csdn)————程序.pdf

    值传递和引用传递是Java中参数传递的两种方式。在`Demo03`中,展示了值传递的例子。当传递基本类型如`int`时,实际传递的是变量的副本,对副本的修改不会影响原始变量。因此,`change(a)`方法内部对`a`的改变不会...

    java基础知识及框架平时工作总结

    JMS(Java Message Service)API是Java中处理消息传递的标准接口,常用于实现异步通信和解耦系统组件。消息队列(Message Queue)如ActiveMQ、RabbitMQ等,可以缓存消息并确保其可靠传输。JMX(Java Management ...

    JavaThe method

    要理解这个错误,我们需要回顾Java的数据类型系统。Java有两类数据类型:原始类型(如int、char、boolean等)和引用类型(如类、接口和数组)。`setAttribute`方法只接受引用类型的值,因此需要将原始类型转换为它的...

    《HEAD-FIRST-JAVA》第一次回顾和整理.doc

    - Java 支持参数传递,即传值调用。 - 方法可以返回单一值或复杂类型(如数组、列表)。 - **封装**: - 通过 setter 和 getter 方法提供对实例变量的安全访问。 - 这种做法提高了程序的稳定性和可维护性。 ###...

    Java语言实用教程

    - **Java语言的产生**:回顾Java的历史背景和发展历程。 - **Java语言的发展**:讲述Java从最初的版本到现在的演变过程。 - **Java语言的关键特点**:详细介绍Java的面向对象、简单高效、与平台无关等核心特点。 ...

    java 编程入门思考

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    Java初学者入门教学

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    java中对 i=i++ 的 解释(反编译出java虚拟机指令的)

    在Java编程语言中,`i=i++`是一个典型的自增表达式...这种行为的根源在于Java的按值传递以及自增操作的顺序规则。为了防止这种意外,最好避免在Java中使用这样的复合赋值操作,特别是在需要依赖自增顺序的复杂逻辑中。

    java in 24 hours

    - **Java简史**:回顾Java的发展历程。 - **Java的应用场景**:探讨Java在不同领域的实际应用案例,如Food Network、NASA等。 - **商业应用**:讲解Java在企业级应用中的角色。 - **开源社区**:介绍SourceForge等...

    java联想(中文)

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

    Thinking in Java 中文第四版+习题答案

    12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使类具有克隆能力 12.2.4 成功的克隆 12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更...

    java ee 复习笔记

    Java EE,全称为Java Platform, Enterprise Edition,是Java平台针对企业级应用开发的版本。它提供了丰富的API和服务,用于构建分布式、多层架构的应用程序,包括Web服务、数据库连接、事务处理、安全性和多语言支持...

    java开发实战经典

    在描述中提到,作者推荐这本书而非观看视频教程,暗示了书籍可能更便于读者按照自己的节奏学习,并能随时回顾查阅。 Java开发是软件行业中非常重要的一个领域,Java作为一种多用途、跨平台的编程语言,广泛应用于...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    8.3 命名习惯大回顾 204 8.4 Java中的注释 205 8.4.1 使用双斜杠的单行注释 205 8.4.2 多行注释 206 8.4.3 Javadoc注释 206 8.5 小结:包让Java的类更清晰优雅 208 8.6 习题 209 第9章 再看数组、字符串和...

    JAVA白皮书(英文版)

    - **方法和消息传递**:探讨Java中方法调用和消息传递的机制。 - **终结器**:解释终结器(finalizer)的概念及其在资源释放中的作用。 - **子类**:讨论子类继承父类特性的过程及其应用场景。 - **Java语言接口**:...

    JAVA_Thinking in Java

    14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 ...

Global site tag (gtag.js) - Google Analytics