call by value or call by reference

一直没太明白参数传递

网上看到 记录下


假设有一段code内容大致如下:
----------------------------------
MAIN()
BEGIN
A[1:4]={1,2,3,4};
X = 1;
Y = 2;
CALL SUB(X, Y, A[X]);
END

SUB(A, B, C)
BEGIN
A = B;
C = 12;
END
----------------------------------


call by value 方式:
调用时子程序得到的是参数值的副本,子程序中对形参的改变其实只是影响了该副本的值,但在返回主程序后该副本会被丢弃,因此在主程序中按值调用的参数仍保持原来的值。
例如SUB(A,B,C)中,若形参A是by value的,则在MAIN中CALL SUB(X,Y,A[X])后,X仍为1。

call by address/reference 方式:
调用时子程序得到的是实际参数的内存地址,因此在子程序中改变形参的值时,实际会导致对该形参所对应的地址处的内存数据的变化,即直接修改的是主程序中的变量的值,返回主程序后该参数所对应的变量值会产生变化。
例如SUB(A,B,C)中,若形参A是by reference的,则在MAIN中CALL SUB(X,Y,A[X])后,X会变为2。

call by name 方式:
有点类似于宏扩展的方式,调用的参数并非是在调用前计算出来,而是在子程序中每个引用所对应的形参的地方都重新进行计算,因此有延迟计算的作用。例如你例子中,若主程序调用SUB时是by name的,则实际执行的情况是:
A=B --> X=Y --> X=2
C=12 --> A[X]=12 --> A[2]=12
这里就看到,因为X的值先变化过,所以在对C赋值的时候,实际影响的是A[2],而不是A[1]。


另一版本


call by value 即 值传递,call by reference 即引用传递.

值传递表示方法只是得到了调用者提供的值,对比来说,引用传递表示方法得到了调用者提供的值的位置.因此,方法可以改变由引用传递的存储在变量里的值,但不能改变由值传递的值.

有两种方法参数,一种叫简单类型,如数字 或者 boolean 值.另一种叫对象引用.可以通过方法改变简单类型,但是在对象引用这里,情形有点不一样.你可以很简单的执行一个方法用来增长一个公司里的某员工的薪水,以下举例:

public static void tripleSalary(Emloyee x)

{

x.raiseSalary(200);

}

当你调用它的时候:

bryan = new Emloyee(...);

tripleSalary(bryan);

发生了什么?下面来看看:

首先,x初始化为bryan的值的一个copy,这就是一个对象引用.然后,raiseSalary方法被应用到了那个对象引用上.Emloyee对象 不管是对于x 还是对于bryan ,他们的薪水都同时增加了200 percent;最后方法结束,参数x不再使用,因为它只在tripleSalary里有效.当然bryan确实改变了.

就如你所看到的,通过一个方法去改变一个对象引用的状态是有可能的.道理很简单,方法得到对象引用的一个copy,然后原来的那个对象和现在的copy都同时指向了一个对象.但是还是要注意一些问题,下面来看:

很多编程语言都有两种方法用来参数传递:值传递和引用传递.一些程序员声称java使用引用传递来传递对象.事实上呢?这是不正确的.这显然是一个共有的误解.下面举一个反面的例子:

让我们写一个方法来交换两个emloyee对象:

public static void swap(Employee x,Employee y) //无作用

{

Employee temp = x;

x = y;

y = temp;

}

如果java是采用了应用传递来传递参数的话,下面的方法就可以顺利地起作用.

Employee a = new Employee("Alice",...);

Employee b = new Employee("Bob",...);

swap(a,b);

//现在a指向 bob,b 指向了alice吗?

实际上,这个方法并没有改变存储在变量a和变量b中的对象引用.为什么呢?因为x和y是在swap方法里初始化的,它们初始化为它们的引用的copy.方法交换了这些copy.

// x refers to Alice,y to Bob

Employee temp = x;

x = y;

y = temp;

//now x refers to Bob,y to Alice

虽然方法交换了这些copy,但最终这是无谓的努力.当方法结束的时候,参数变量x和y被放弃了.因为它们属于局部变量.那么为什么上面的tripleSalary又是有效的呢?那是不同的,下面会继续分析.

上面的例子说明java语言没有通过引用传递来传递参数给对象.在java里.引用传递实际上也是通过值来传递的.

上面的第一个例子提到了tripleSalary方法,里面用到了raiseSalary方法.下面是原代码:

public class ParamTest

{

public static void main(String args[]) {

System.out.println("\nTesting tripleSalary:");

Employee bryan = new Employee("Bryan",50000);

System.out.println("Before : salary =" + bryan.getSalary());

tripleSalary(bryan);

System.out.println("After : salary =" + bryan.getSalary());

}

}

public static void tripleSalary(Employee x)

{

x.raiseSalary(200);

System.out.println("End of mehod : salary=" + x.getSalary());

}

class Employee

{

private String name;

private double salary;

public Employee(String n,double s)

{

name = n;

salary = s;

}

public double getSalary()

{

return salary;

}

public void raiseSalary(double byPercent)

{

double raise = salary*byPercent/100;

salary += raise;

}

最大的不同就在raiseSalary方法里,它定义了一个raise变量,并把它赋给salary;而salary是一个成员变量(也叫实例变量),它不会因为结束方法而被放弃.所以tripleSalary方法是有效的.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值