一直没太明白参数传递
网上看到 记录下
假设有一段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方法是有效的.
网上看到 记录下
假设有一段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方法是有效的.