`
laowan
  • 浏览: 4671 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

java语言深入(java中是传值还是引用)

阅读更多
    熟悉C的程序员都用过指针,对指针可谓爱之深恨之切。指针是指向一块内存地址的内存数据(有些拗口),也就是说指针本身是一个占用4字节内存的 int(32 位系统内),而这个int值恰恰又是另一块内存的地址。比如"hello"这个字串,存放在@0x0000F000这个地址到 @0x0000F005这段内存区域内(包括0x00的结束字节)。而在@0x0000FFF0到@0x0000FFF03这四个字节内存放着一个 int,这个int的值是 @0x0000F000。这样就形成了一个指向"hello"字串的指针。

  在Java中,很多人说没有指针,事实上,在Java更深层次里,到处都是大师封装好的精美绝伦的指针。为了更容易的讲解Java中关于类和类型的调用,Java中出现了值与引用的说法。浅显的来说,我们可以认为 Java中的引用与C中的指针等效(其实差别非常非常大,但是为了说明我们今天的问题,把他们理解为等效是没有任何问题的)。

  所谓传引用的说法是为了更好的讲解调用方式。基于上面对指针的理解,我们不难看出,指针其实也是一个int值,所谓传引用,我们是复制了复制了指针的 int值进行传递。为了便于理解,我们可以姑且把指针看作一种数据类型,透明化指针的int特性,从而提出传引用的概念。

  重申一遍:Java中只有传值。

  1所谓传值和传引用

  传值和传引用的问题一直是Java里争论的话题。与C++不同的,Java里面没有指针的概念,Java的设计者巧妙的对指针的操作进行了管理。事实上,在懂C++的Java程序员眼中,Java到处都是精美绝伦的指针。
  下面举个简单的例子,说明什么是传值,什么是传引用。
  //例1
  void method1(){
  int x=0;
  this.change(x);
  System.out.println(x);
  }

  void int change(int i){
  i=1;
  }

  很显然的,在mothod1中执行了change(x)后,x的值并不会因为change方法中将输入参数赋值为1而变成1,也就是说在执行change(x)后,x的值z依然是0。这是因为x传递给change(int i)的是值。这就是最简单的传值。
  同样的,进行一点简单的变化。
  //例2
  void method1(){
  StringBuffer x=new StringBuffer("Hello");
  this.change(x);
  System.out.println(x);
  }

  void int change(StringBuffer i){
  i.append(" world!");
  }
  看起来没什么变化,但是这次mothed1中执行了change (x)后,x的值不再是"Hello"了,而是变成了"Hello world!"。这是因为x传递给change(i)的是x的引用。这是最经典的传引用。
  似乎有些奇怪了,两段程序没有特别的不同,可是为什么一个传的是值而另一个传的是引用呢?......

  2非要搞清楚传值还是传引用的问题吗?

  搞清楚这自然是有必要的,不然我也不需要写这么多了,不过的确没有到"非要"的地步。
  首先,如果我们不太关心什么是传值什么是传引用,我们一样能写出漂亮的代码,但是这些代码在运行过程中可能会存在着极大的隐患。
  全局变量是让大家深恶痛绝(又难以割舍)的东西,原因就是使用全局变量要特别注意数据的保护。如果在多线程的程序里使用全局变量简直就等于跟自己过不去。不了解传值和传引用的问题,跟使用全局变量不考虑数据保护的罪过是不相上下下的,甚至有时候比它还要糟。你会莫名其妙,为什么我的返回参数没有起作用,为什么我传进去的参数变成了这样......?
  一个例子:
  //例3
  void mothed1(){
  int x=0;
  int y=1;
  switchValue(x,y);
  System.out.println("x="+x);
  System.out.println("y="+y);
  }
  void switchValue(int a,int b){

  int c=a;
  a=b;
  b=c;
  }
  上面是一个交换a,b值的函数,看起来似乎蛮正确的,但是这个函数永远也不会完成你想要的工作。
  还有一个例子:
  //例4
  StringBuffer a=new StringBuffer("I am a ");
  StringBuffer b=a;
  a.append("after append");
  a=b;
  System.out.println("a="+a);
  在编程过程中,经常会遇到这种情况,一个变量的值要被临时改变一下,等用完之后再恢复到开始的值。就好像上面的例子,a为了保持它的值,使用b=a做赋值,之后a被改变,再之后a把暂存在b里面的值取回来。这是我们一厢情愿的想法,而事实上,这段代码执行后,你会发现a的值已经改变了。
  以上是两个最简单的例子,真正的程序开发过程中,比这要复杂的情况每天都会遇到。

3类型和类

  Java 提出的思想,在Java里面任何东西都是类。但是Java里面同时还有简单数据类型:int,byte,char,boolean,与这些数据类型相对应的类是Integer,Byte,Character,Boolean,这样做依然不会破坏Java关于任何东西都是类的提法。
  这里提到数据类型和类似乎和我们要说的传值和传引用的问题无关,但这是我们分辨传值和传引用的基础。

  4试图分辨传值还是传引用

  为什么是"试图分辨"呢?很简单,传值和传引用的问题无处不在,但是似乎还没有人能正统的给出标准,怎样的就是值拷贝调用,怎样的就是引用调用。面对这个问题,我们更多的应该是来自平时积累对Java的理解。
  回过头来,我们分析一下上面的几个例子:
  先看例1,即使你不明白为什么,但是你应该知道这样做肯定不会改变x的值。为了方便说明,我们给例子都加上行号。
  //例1
  1 void method1(){
  2 int x=0;
  3 this.change(x);
  4 }
  5
  6 void int change(int i){
  7 i=7;
  8}
  让我们从内存的存储方式看一下x和I之间到底是什么关系。
  在执行到第2行的时候,变量x指向一个存放着int 0的内存地址。

  变量x---->[存放值0]

  执行第3行调用change(x)方法的时候,内存中是这样的情形:x把自己值在内存中复制一份,然后变量i指向这个被复制出来的0。

  变量x---->[存放值0]
  ↓进行了一次值复制
  变量x---->[存放值0]

  这时候再执行到第7行的时候,变量i的被赋值为7,而这一步的操作已经跟x没有任何关系了。

  变量x---->[存放值0]
  
  变量x---->[存放值7]

  说到这里应该已经理解为什么change(x)不能改变x的值了吧?因为这个例子是传值的。
  那么,试着分析一下为什么例三中的switchValue()方法不能完成变量值交换的工作?
  再看例2。
  //例2
  1void method1(){
  2 StringBuffer x=new StringBuffer("Hello");
  3 this.change(x);
  4}
  5
  6void int change(StringBuffer i){
  7 i.append(" world!");
  8}
  例2似乎和例1从代码上看不出什么差别,但是执行结果却是change(x)能改变x的值。依然才从内存的存储角度来看看例2的蹊跷在哪里。
  在执行到第2行时候,同例1一样,x指向一个存放"Hello"的内存空间。

  变量x---->[存放值"Hello"]

  接下来执行第三行change(x),注意,这里就与例1有了本质的不同:调用change(x)时,变量i也指向了x指向的内存空间,而不是指向x的一个拷贝。

  变量x \
  -->[存放值"Hello"]
  变量x /

  于是,第7行对i调用append方法,改变i指向的内存空间的值,x的值也就随之改变了。

  变量x \
  -->[追加为"Hello World!"]
  变量x /

  为什么x值能改变呢?因为这个例子是传引用的。
  这几个例子是明白了,可是很多人会开始有另一个疑问了:这样看来,到底什么时候是传的值什么时候是传得引用呢?于是,我们前面讲到的类型和类在这里就派上了用场:对于参数传递,如果是简单数据类型,那么它传递的是值拷贝,对于类的实例它传递的是类的引用。需要注意的是,这条规则只适用于参数传递。为什么这么说呢?我们看看这样一个例子:
  //例5
  String str="abcdefghijk";
  str.replaceAll("b","B");
  这两句执行后,str的内容依然是"abcdefghijk",但是我们明明是对str操作的,为什么是这样的呢?因为str的值究竟会不会被改变完全取决于replaceAll这个方法是怎么实现的。类似的,有这样一个例子:
  //例6
  1 void method1() {
  2 StringBuffer x = new StringBuffer("Hello");
  3 change1(x);
  4 System.out.println(x);
  5 }
  6
  7 void method2() {
  8 StringBuffer x = new StringBuffer("Hello");
  9 change2(x);
  10 System.out.println(x);
  11 }
  12
  13 void change1(StringBuffer sb) {
  14 sb.append(" world!");
  15 }
  16
  17 void change2(StringBuffer sb) {
  18 sb = new StringBuffer("hi");
  19 sb.append(" world!");
  20 }
  调用method1(),屏幕打印结果为:"Hello world!"
  调用method2(),我们认为结果应该是"hi world",因为sb传进来的是引用。可是实际执行的结果是"Hello"!
  难道change2()又变成传值了?!其实change1()和change2()的确都是通过参数传入引用,但是在方法内部因为处理方法的不同而使结果大相径庭。我们还是从内存的角度分析:
  执行method1()和change1()不用再多说了,上面的例子已经讲解过,这里我们分析一下method2()和change2()。
  程序执行到第8行,x指向一个存放着"Hello"的内存空间。

变量x---->[存放值"Hello"]

  第9行调用change2,将sb指向x指向的内存空间,也就是传入x的引用。

  变量x \
  -->[存放值"Hello"]
  变量x /

  到这里为止还没有什么异样,接下来执行18行,这里就出现了类似传入值拷贝的变化:new 方法并没有改变sb指向内存的内容,而是在内从中开辟了一块新的空间存放串"hi",同时sb指向了这块空间。

  变量x---->[存放值"Hello"]
  ×原有的引用被切断
  变量x---->[另一块存放"hi"的空间]

  接下来再对sb进行append已经和x没有任何关系了。
  所以,还有一条不成规则的规则:对于函数调用,最终效果是什么完全看函数内部的实现。比较标准的做法是如果会改变引用的内容,则使用void作为方法返回值,而不会改变引用内容的则在返回值中返回新的值。
  虽然已经说了这么多,但是感觉传值还是传引用的问题依然没有完全说清楚。因为这个问题本身就是很难归纳总结的问题,所以更多的理解要靠平时的积累和形成。下面几个例子,给大家尝试进行分析。
  //例7,打印结果是什么?
  public static void main(String[] args) {
  int a;
  int b;
  StringBuffer c;
  StringBuffer d;
  a = 0;
  b = a;
  c = new StringBuffer("This is c");
  d = c;

  a = 2;
  c.append("!!");

  System.out.println("a=" + a);
  System.out.println("b=" + b);
  System.out.println("c=" + c);
  System.out.println("d=" + d);
  }

  //例8,打印结果是什么?
  public class Test{

  public static void main(String[] args) {

  StringBuffer sb = new StringBuffer("Hello ");

  System.out.println("Before change, sb = " + sb);

  changeData(sb);

  System.out.println("After changeData(n), sb = " + sb);

  }

  public static void changeData(StringBuffer strBuf) {

  StringBuffer sb2 = new StringBuffer("Hi ");

  strBuf = sb2;

  sb2.append("World!");

  }

  }

    (无聊,发点东西,与君共勉) 
分享到:
评论

相关推荐

    JAVA中传值与引用问题

    本文将深入探讨Java中的传值与传引用问题,并通过具体的例子来解析其中的原理。 #### 二、基础知识回顾 在Java中,所有的数据类型可以分为两大类:基本类型(如int, double等)和引用类型(如Object, String等)。...

    Java传值还是引用

    根据标题和描述,我们将深入探讨Java中传值与引用的区别,以及它们在实际编程中的应用。 首先,Java是一种“总是按值传递”的语言。这意味着无论是基本类型还是引用类型,当作为参数传递时,都会有一个副本被创建并...

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

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

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

    虽然Java语言不直接支持引用传递,但由于所有对象都是通过引用访问的,所以在某种意义上,Java中的对象参数传递可以被认为是一种“引用传递”。当我们将对象传递给一个方法时,实际上是将该对象引用的副本传递给了...

    分析java的传值问题

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

    Android NDK底层和java代码相互传值调用(实用经典Demo)

    在Android开发中,Java语言是主要的编程工具,但有时为了追求性能优化或者利用已有的C/C++库,我们会使用Android Native Development Kit (NDK)。NDK允许开发者在Android应用中集成原生代码,实现Java与C/C++之间的...

    Java的引用和函数参数传递

    在Java中,对象的引用扮演着类似于其他编程语言(如C/C++)中指针的角色,但是Java的引用比传统的指针更加安全且易于管理。这是因为Java的设计者们刻意避免了一些容易引发错误的操作,比如不允许对引用进行算术操作...

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

    在Java编程语言中,方法是实现特定功能的代码块,它们可以接受输入(参数)并返回结果。本文将深入探讨Java中方法参数的传递机制,帮助开发者更好地理解这一核心概念。 1. 参数传递方式 Java中,方法参数的传递主要...

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

    在Java编程语言中,面向对象特性是其核心概念之一,其中引用来传递对象是一个非常重要的知识点。本教程将深入探讨Java中的引用传递机制,并通过视频教程的形式帮助学习者更好地理解和应用这一概念。 首先,理解...

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

    在Java编程语言中,方法调用和参数传递是核心概念,理解它们的工作原理对于编写高效、可靠的代码至关重要。本文将深入探讨Java中的方法调用、值传递与引用传递,并通过字节码分析来进一步理解这些概念。 首先,我们...

    浅析Java方法传值和传引用问题

    在Java编程语言中,方法参数传递机制涉及到两个主要概念:传值和传引用。了解这两个概念对于编写高效、可靠的代码至关重要。本篇文章将深入探讨Java中的方法传值和传引用问题。 首先,让我们理解什么是传值。在Java...

    java类

    ### Java 类:传值还是传引用? 在Java编程语言中,理解变量的传递方式对于编写高效、可维护的代码至关重要。本文将通过一个具体的例子来深入探讨Java中的传值与传引用的区别,并解释如何利用这一特性来更好地管理...

    JAVA语言程序设计PPT学习教案.pptx

    这个学习教案深入浅出地介绍了Java语言的基础知识,对于初学者来说是非常好的学习材料,有助于理解面向对象编程的基本原理和Java语言的特性。通过学习这些概念,开发者可以更好地设计和实现复杂的软件系统。

    对Java中传值调用的理解分析

    在Java编程语言中,"传值调用"是指在函数或方法调用时,参数的值被复制并传递到函数内部。这意味着,当一个变量作为参数传递时,函数内部的操作不会影响到函数外部的原始变量。这个概念与C++中的"传引用调用"有所...

    经典Java基础面试题集锦

    通过对这些经典Java基础面试题的深入分析,我们不仅了解了Java语言的基础知识,还掌握了如何正确使用这些知识解决实际问题的能力。这对于任何希望成为优秀Java开发者的求职者来说都是非常宝贵的。希望本文能帮助读者...

    传值与传地址[文].pdf

    在编程语言中,传值与传地址是两种不同的参数传递方式。Java 作为一门面向对象的语言,对于简单类型和对象类型的处理方式有所不同。本文将详细解释这两种类型在Java中的传递机制。 1. 简单类型(按值传递) Java中...

    面向对象经典讲解 有助于更进一步理解java对象

    - Java中的方法调用可以传值或传引用。基本类型数据是按值传递,而对象是按引用传递,这意味着传递的是对象在内存中的地址,而不是对象本身。 **static关键字**: - `static`修饰的成员属于类,不依赖于类的实例。...

    Java计算机语言函数应用

    Java计算机语言函数应用是编程学习中的重要组成部分,它涉及到如何在Java程序中高效地组织和执行特定任务。函数,也称为方法,在Java中扮演着核心角色,它们是代码的可重用模块,允许我们通过调用一个名字来执行一...

    Java学习笔记一

    本文将深入探讨Java的引用类型分析、传值内存解析以及继承和多态等核心概念,旨在帮助初学者和进阶者巩固基础,提升技能。 首先,让我们来谈谈Java的引用类型。在Java中,引用类型包括类(Class)、接口(Interface...

Global site tag (gtag.js) - Google Analytics