(重要)java都是值传递,与对象形参所指向的对象改变,其实参所指向的对象也相应改变并不矛盾(2011年9.30日一天写的两个程序的总结结果)...

1.0建立二叉树的代码,在java中必须创建二叉树的方法必须用返回值,因为不存在c语言中的引用传递,在java中只有值传递 代码1为正确,代码2(没有使用返回值)为错误。那么为什么之前的例子中将对象作为参数时,对对象内容的更改还是正确的,也没有使用返回值,二者看似矛盾,其实并不矛盾,以前都没有理解到这个本质,本质就是参数中,确实是有一个临时变量,交换形式对象参数,实际的参数不会改变,但是改变形式参数的引用所指向的内容,即这个对象本身改变,其实参所引用的对象也是同一个对象,当然该对象也会发生变化,因为实参和形参引用的都是同一个对象,只不过是两份地址的拷贝。

即便是c语言如果不用引用传递-&,就是说用指针,也需要用返回值的方法建立二叉树,才能将已建立好的二叉树头指针返回给打印函数的参数。当然java中可以采用对私有变量等操作,即不不使用参数传递,而仅仅是创建函数和打印函数共同操作的是同一个变量也可以吧,估计递归就不好用了,这个怎么做没去想!~~~

错误代码:这个时候打印出来的树的节点为空,printInOrder(tree1)输出节点为空:

ContractedBlock.gif ExpandedBlockStart.gif View Code
import java.util.*;

class TNode{
int data;
TNode lchild;
TNode rchild;
TNode(int a)
{
data=a;
lchild=null;
rchild=null;
}
}

public class BTree {

static TNode root=new TNode(1);

static void creatTree(TNode root)
{
Scanner in=new Scanner(System.in);
int a=in.nextInt();
if(a==0){
root=null;
return;

}

else{
root=new TNode(a);
creatTree(root.lchild);
creatTree(root.rchild);
}
}
static void printInOrder(TNode root){
if(root==null)
return;
else{

System.out.print(root.data);
printInOrder(root.lchild);
printInOrder(root.rchild);
}

}



public static void main(String[] args) {
// TODO Auto-generated method stub

creatTree(root);
printInOrder(root);




}

}



正确代码:

ContractedBlock.gif ExpandedBlockStart.gif View Code
import java.util.Scanner;

class Tode{

int data;

Tode lchild;

Tode rchild;

Tode(int a)

{

data=a;

lchild=null;

rchild=null;

}

}

public class TREE{

static Tode root=null;

static Tode creatTree(Tode root)

{

Scanner in=new Scanner(System.in);

int a=in.nextInt();

if(a==0){

return null ;

}

else{

root=new Tode(a);

root.lchild=creatTree(root.lchild);

root.rchild=creatTree(root.rchild);

return root;

}

}

static void printInOrder(Tode root){

if(root==null)

return;

else{

System.out.print(root.data);

printInOrder(root.lchild);

printInOrder(root.rchild);

}

}

public static void main(String[] args) {

// TODO Auto-generated method stub

Tode tree1=creatTree(root);

printInOrder(tree1);

}

}



1.1、疑惑解决了,原来认为方法中传入一个对象的话(数组也行),如果方法内部对这个对象的内部参数进行了更改,则其实就是对外部的这个静态对象也进行了修改,相当于c++中的真正的引用传递吧,但是传入的是整型等普通类型,则还是不能够更改,可以见下面的实例。如果传入的对象是数组的话,也可以更改,因为java中数组也是对象~,其实也不是 因为是对引用所指向的内容进行了修改,引用可以有多个,但是引用所指向的内容只有一份!!!!!!

以下程序的输出结果是0  1  2

ContractedBlock.gif ExpandedBlockStart.gif View Code
import java.util.*;

class node{
int data;
node lchild;
node rchild;
public node(int a){
data=a;
lchild=null;
rchild=null;
}
}

public class testclass2 {

public void changeroot(node root){
node newnode1=new node(1);
node newnode2=new node(2);
root.lchild=newnode1;
root.rchild=newnode2;
}

public void print(node root){

System.out.println(root.data);
System.out.print(root.lchild.data);
System.out.print(root.rchild.data);

}
public static void main(String[] args){
node root=new node(0);
testclass2 dd=new testclass2();
dd.changeroot(root);
dd.print(root);

}

}



1.2、下面是另一个例子,在算法例子中也常用到因为是 对引用所指向的内容进行了修改,引用可以有多个,但是引用所指向的内容只有一份!!!!!!

以下程序输出结果是2 4 3 4

ContractedBlock.gif ExpandedBlockStart.gif View Code
void diguiInorderTraverse(node root){
root.lchild
}
解释说的通了,传入root,对root修改,其实就相当于


class A{
int b=2;
}
public class Testclass {
static A a=new A();
static void changeA(A a){
a.b=3;
}
static int b=4;
static void changeB(int b){
b=5;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(a.b);
System.out.println(b);
changeA(a);
changeB(b);
System.out.println(a.b);
System.out.println(b);

}
}

1.3、也就是说root对象确实分配了一个临时对象,传入的都是地址的引用,所以对这个参数的改变,对引用所指向的内容进行了修改,引用可以有多个,但是引用所指向的内容只有一份!!!!!!

 我看了一些引用调用和值调用的定义,很多人都把是传递值,还是传递地址?是改变参数自身内容,还是改变参数所指向的地址中的内容作为区别这两种调用的标志。这很不恰当,这些说法很容易让我们联想到Java的对象参数传递是引用调用,实际上,Java的对象传递仍然是值调用。

        引用调用:在参数传递的过程中,形参并不是实参的副本,而是实参本身。这种调用实参和形参在内存中实际上都是同样的一个区域,只是这个区域的表示(参数名)不同而已。

   

        值调用:在参数传递过程中,创建了一个实参的副本——形参。形参和实参在内存中是两个完全不同的区域。因此形参内容的改变并不能影响到实参。

   方法调用(call by) 是一个标准的计算机科学术语。方法调用根据参数传递的情况又分为值调用( call by reference ) 引用调用( call by value ) 。江湖上有很多关于这两种调用的定义 ,最通常的说法是传递值的是值调用,传递地址的是引用调用。这其实很不恰当,这种 这些说法很容易让我们联想到Java的对象参数传递是引用调用,实际上,Java的对象参数传递仍然是值调用  

 

以下摘自该段落:

http://hxraid.iteye.com/blog/428856

     
我们首先用一段代码来证实一下为什么Java的对象参数传递 是值调用。

Java代码  

1.   public class Employee {  

2.     

3.       public String name=null;  

4.         

5.       public Employee(String n){  

6.           this.name=n;  

7.       }  

8.       //将两个Employee对象交换  

9.       public static void swap(Employee e1,Employee e2){  

10.          Employee temp=e1;  

11.          e1=e2;  

12.          e2=temp;  

13.                  System.out.println(e1.name+" "+e2.name); //打印结果:李四 张三  

14.      }  

15.      //主函数  

16.      public static void main(String[] args) {  

17.          Employee worker=new Employee("张三");  

18.          Employee manager=new Employee("李四");  

19.          swap(worker,manager);  

20.          System.out.println(worker.name+" "+manager.name); //打印结果仍然是: 张三 李四  

21.      }  

22.  }  

 

      上面的结果让人很失望,虽然形参对象e1,e2的内容交换了,但实参对象worker,manager并没有互换内容。这里面最重要的原因就在于形参e1,e2是实参worker,manager的地址拷贝。

      大家都知道,在Java中对象变量名实际上代表的是对象在堆中的地址(专业术语叫做对象引用 )。在Java方法调用的时候,参数传递的是对象的引用。重要的是,形参和实参所占的内存地址并不一样,形参中的内容只是实参中存储的对象引用的一份拷贝。

       如果大家对JVM内存管理中Java 局部变量区 有所了解的话(可以参见《 Java 虚拟机体系结构 ),就很好理解上面这句话。在JVM运行上面的程序时,运行main方法和swap方法,会在Java栈中先后push两个叫做栈帧 的内存空间。main栈帧中有一块叫局部变量区的内存用来存储实参对象workermanager的引用。而swap栈帧中的局部变量区则存储了形参对象e1e2的引用。虽然e1e2的引用值分别与workermanager相同,但是它们占用了不同的内存空间。当e1e2的引用发生交换时,下面的图很清晰的看出完全不会影响workermanager的引用值。

             

      Java对象参数传递虽然传递的是地址(引用),但仍然是值调用。是时候需要给引用调用和值调用一个准确的定义了。

 

      值调用(call by value)  在参数传递过程中,形参和实参占用了两个完全不同的内存空间。形参所存储的内容是实参存储内容的一份拷贝。实际上,Java对象的传递就符合这个定义,只不过形参和实参所储存的内容并不是常规意义上的变量值,而是变量的地址。咳,回过头想想:变量的地址不也是一种值吗!

      引用调用(call by reference)  在参数传递的过程中,形参和实参完全是同一块内存空间,两者不分彼此。 实际上,形参名和实参名只是编程中的不同符号,在程序运行过程中,内存中存储的空间才是最重要的。不同的变量名并不能说明占用的内存存储空间不同。

 

      大体上说,两种调用的根本并不在于传递的是值还是地址(毕竟地址也是一个值),而是在于形参和实参是否占用同一块内存空间。事实上,C/C++的指针参数传递也是值调用,不信试试下面的C代码吧!

C代码  

1.  #include<stdio.h>  

2.  void swap(int *a1,int *b1){  

3.      int *t=a1;  

4.      a1=b1;  

5.      b1=t;  

6.  }  

7.  int main(){  

8.      int x1=100;  

9.      int x2=200;  

10.         int *a=&x1;  

11.     int *b=&x2;  

12.     printf("%d %d\n",*a,*b);  

13.     swap(a,b);  

14.     printf("%d %d\n",*a,*b);  

15.     return 0;  

16. }  

         C/C++是有引用调用的,这就是C/C++一种叫做引用的变量声明方法: int a; int &ra=a; 其中raa的别名,两者在内存中没有区别,占用了同一个内存空间。而通过引用(别名)的参数传递就符合引用调用的特点了。大家可以去试试

void swap(int &a1,int &b1);的运行结果。

 

转载于:https://www.cnblogs.com/wangzhewang/archive/2011/09/30/2196744.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值