传值还是传引用(1) — java

发觉这篇文章讲的挺好的转过来了,顺便自己总结了一下。

By value or by reference

1. Object Copy

Copy a object in javais dangerous.  If simply using =, it isshallow copy. Means, it only copy the address of the object in the memory. Thedata in the memory is not copied. The only thing is it has an extra path thatleads to the memory location.
for example:
01	System.out.println("===== create a map with a list ====");
02	 Map<String, Collection<String>> m = new HashMap();
03	 Collection<String> ca = new ArrayList();
04	 ca.add("Apple");
05	 ca.add("Banana");
06	 ca.add("Cherry");
07	 m.put("fruit", ca);
08	 printMap(m);
09	 
10	 System.out.println("\n===== Add an intm into list ====");
11	 ca.add("Durian");
12	 printMap(m);
13	 
14	 System.out.println("\n===== delete an intm into list ====");
15	 ca.remove("Apple");
16	 printMap(m);
17	 
18	 System.out.println("\n===== assign a list to 2nd one and manipulate ====");
19	 
20	 Collection<String> cb = m.get("fruit");
21	 cb.add("Grape");
22	 printMap(m);
The result is:
01	===== create a map with a list ====
02	fruit -> [Apple,Banana,Cherry,]
03	 
04	===== Add an intm into list ====
05	fruit -> [Apple,Banana,Cherry,Durian,]
06	 
07	===== delete an intm into list ====
08	fruit -> [Banana,Cherry,Durian,]
09	 
10	===== assign a list to 2nd oe and manipulate ====
11	fruit -> [Banana,Cherry,Durian,Grape,]

Both list A and List B are referenced tothe object in the Map. Regardless to update any one, the value of the map willbe affected.

The deep copy is different. It copies thedata into another location in the memory, and then have the variable points tothe location.

If we need a deep copy of the above example:
1	System.out.println("\n===== deep copy ====");
2	 
3	 Collection<String> cc = new ArrayList();
4	 for( String s : m.get("fruit") ){
5	 cc.add( s );
6	 }
7	 cc.add ("Kiwi");
8	 cc.add ("Lemon");
9	 printMap (m );

The output:

1     =====deep copy ====

2     fruit-> [Banana,Cherry,Durian,Grape,]

The map gets nothing changed. It is notaffected by the change of list cc.

An exception:
1	Map<String, String> address = new TreeMap();
2	 address.put( "Ann", "1 Main st");
3	 String add = "5 Locker St";
4	 address.put( "Baker", add);
5	 printMap(address);
6	 System.out.println( "Change variable value");
7	 add = "38 Clifford Rd";
8	 printMap(address);

Will the address change? Answer is NO.

1     Ann- 1 Main st

2     Baker- 5 Locker St

3     Changevariable value

4     Ann- 1 Main st

5     Baker- 5 Locker St

Why? Because Stringis immutable. It could not changed after it is created. If we want it takeseffect, it needs to re-read into Map.
1	.....
2	add = "38 Clifford Rd";
3	address.put( "Baker", add);
4	.....


    1.  add="38 clifford Rd" 是定义另外一个字符常量,而不是改变然来字符常量的值,知识把add 指向了这个字符常量
         再address.put("Baker,add") 就是re-read into  Map 了。
    2. note:Map,HashMap是不允许key重复的,所以如果有key重复的话,那么前面的value会被后面的覆盖了
        想得到不被覆盖的value,可以试试hashTable

1     Ann- 1 Main st

2     Baker - 5 Locker St

3     Change variable value

4     Ann - 1 Main st

5     Baker - 38 Clifford Rd
2) Pass by value or by reference

The parameters(argument) passed in java methods are passed by value.

public class user{
    void setA(int a){
	   a=5;
   }
   
   public static void main(String[] args) {
	User user=new User();
	int b=10;
	user.setA(b);
	System.out.println(b);
   }
}
这里打印结果为10 从这可以看出java  传参为传值而非引用。
//
不好意思上面这个例子没有把传引用说明白。
我从csdn 其他地方看了一下。解说相当不错。
我粘贴如下:
对象是传引用的   
参数是传值的 
这两个能够同时成立吗?一个字:是!在java中,你从来没有传递对象,你传递的仅仅是对象的引用!一句话,java是传引用的。然而,当你传递一个参数,那么只有一种参数传递机制:传值! 

通常,当程序员讨论传值和传引用时,他们是指语言的参数传递机制,c++同时支持这两种机制,因此,以前使用过c++的程序员开始好像不能确定的java是如何传参数的。java语言为了事情变得简单只支持参数传值的机制。   

★java中的变量有两种类型:引用类型和原始类型。当他们被作为参数传递给方法时,他们都是传值的。这是一个非常重要的差别,下面的代码范例将说明这一点。在继续前,我们有必要定义一下传值和传引用。 

传值意味着当参数被传递给一个方法或者函数时,方法或者函数接收到的是原始值的副本。因此,如果方法或者函数修改了参数,受影响的只是副本,原始值保持不变。 

关于java中的参数传递的混乱是因为很多java程序员是从c++转变过来的。c++有引用和非引用类型的变量,并且分别是通过传引用和传值的。java语言有原始类型和对象引用,那么,按照逻辑,java对于原始类型使用传值而对引用是传引用的,就像c++一样(错)。毕竟,你会想到如果你正在传递一个引用,那么它一定是传引用的。这是一个很诱惑人的想法,但是是错误的! 

在c++和java中,当函数的参数不是引用时,你传递的是值得副本(传值)。但是对于引用类型就不同了。在c++中,当参数是引用类型,你传递的是引用或者内存地址(传引用),而在java中,传递一个引用类型的参数的结果。
只是传递引用的副本(传值)而非引用自身  

这是一个非常重要的区别!java不考虑参数的类型,一律传递参数的副本。仍然不信?如果java中是传引用,那么下面的范例中的swap方法将交换他们的参数。因为是传值,因此这个方法不是像期望的那样正常工作。
代码 #1
package com;
class   Swap 
{ 
public   static   void   main(String   args[])   
{   
Integer   a,   b;   
int   i,j; 
a   =   new   Integer(10); 
b   =   new   Integer(50);   
i   =   5; 
j   =   9;   

System.out.println( "Before   Swap,   a   is   "   +   a);   

System.out.println( "Before   Swap,   b   is   "   +   b);   

swap(a,   b);   

System.out.println( "After   Swap   a   is   "   +   a);   

System.out.println( "After   Swap   b   is   "   +   b);   

System.out.println( "Before   Swap   i   is   "   +   i);   

System.out.println( "Before   Swap   j   is   "   +   j);   

swap(i,j);   

System.out.println( "After   Swap   i   is   "   +   i); 

System.out.println( "After   Swap   j   is   "   +   j);   

} 

public   static   void   swap(Integer   ia,   Integer   ib) 
{ 
Integer   temp   =   ia;   
ia   =   ib;   
ib   =   temp;   
} 

public   static   void   swap(int   li,   int   lj)   

{   
int   temp   =   li;   
li   =   lj;   
lj   =   temp;   
}   
}   
执行结果:
Before   Swap,   a   is   10
Before   Swap,   b   is   50
After   Swap   a   is   10
After   Swap   b   is   50
Before   Swap   i   is   5
Before   Swap   j   is   9
After   Swap   i   is   5
After   Swap   j   is   9
因为swap方法接收到的是引用参数的副本(传值),对他们的修改不会反射到调用代码。
所以java 里直接写一个原始类型的swap 函数还不行,要通过对象或者传数组地址才行。
如对一个对象 Object.value, Object1.value 交换或者传过来数组地址在swap 才可以。

见附录。
代码#2
package com;

class   Change 

{ 

public   static   void   main(String   args[])   

{   
StringBuffer   a=new   StringBuffer( "ok ");   
int   i; 
i   =   5; 
System.out.println( "Before   change,   a   is   "   +   a);   
change(a);   
System.out.println( "After   change   a   is   "   +   a);   
System.out.println( "Before   change   i   is   "   +   i);   
change(i);   
System.out.println( "After   change   i   is   "   +   i); 

String strUP="ABCDEFG";
System.out.println("Before change strUO is "+strUP);
change(strUP);
System.out.println("After change strUO is "+strUP);


} 

public   static   void   change(StringBuffer   ia) 

{ 
ia.append( "   ok? "); 
} 

public   static   void   change(int   li)   
{   
li   =   10;   
} 

public   static   void   change(String   ia) 

{ 
 ia="abcdef";
}

} 

执行结果:
Before   change,   a   is   ok 
After   change   a   is   ok    ok? 
Before   change   i   is   5
After   change   i   is   5
Before change strUO is ABCDEFG
After change strUO is ABCDEFG

分析代码#1和#2 为什么产生不同效果。因为我们swap(Integer ia,Integer  ib)时 里面的代码是对引用的值进行操作。
就像假设 交换前 Integer a 的引用地址是 100,b 的引用地址是200。
由于是传值,会把 100的 副本传给ia, 200的 副本传给ib 。
这时候我们ia 和a 指向同一堆内存。
我们对ia 的引用(堆地址值)改变 不会改变a 的引用(堆地址的值)。
但我们对指的那一段内存区域进行改变的话,比如在swap 函数里改变ia 所指对象的属性 会影响到a 所指对象的属性值。
这就是为什么代码#2 里为什么变化了,就是因为
public   static   void   change(StringBuffer   ia) 

{ 
ia.append( "   ok? "); 
} 
这个把对象堆内存区域的值改变了。而代码#1 如下面的
public   static   void   swap(Integer   ia,   Integer   ib) 
{ 
Integer   temp   =   ia;   
ia   =   ib;   
ib   =   temp;   
} 
是把引用的值给交换了。不是把他们所指的堆里内容改变了,只是把ia ,ib引用地址交换了肯定不影响 a,b 的引用。(因为没有把a,b 所指堆里内容改变)。
所以执行结果为:
Before   change,   a   is   ok 
After   change   a   is   ok    ok? 
而改变字符串时结果没变,我们同样可以分析代码:
public   static   void   change(String   ia) 

{ 
 ia="abcdef";
}
这里只是把 ia 的引用引用本来为strUp 而我们执行 ia=“abcdef” 时只是把ia 指向了另外一个内存区域(此区域放的是“abcdef”字符串)。
Before change strUO is ABCDEFG
After change strUO is ABCDEFG
总结:
大家看执行结果会怎么样要理解两点:
1.java 里什么都是传值(传的是副本,基本类型是值的副本,引用类型是引用的副本)
2. 参数是引用类型时有两种情况
   1)如果函数里的代码只是对引用值进行修改的话,不影响外面变量。 如 上面代码#1 的swap(Integer ia,Integer ib)。
   2)如果函数里的代码是对引用所指的堆区域的内容进行修改的话就会影响外面变量。 毕竟外面变量和函数参数所指向的是同一堆内存区域。

参考网页: http://topic.csdn.net/t/20031214/11/2561764.html 。
有任何意见欢迎大家评论。
附录:java 实现swap 函数
方法1:
利用实例变量(成员变量)
class JavaSwap1
{
int x;int y;
JavaSwap1(int x,int y)
{
   this.x=x;
   this.y=y;
}
void swap()
{
   int temp;
         temp=x;
   x=y;
   y=temp;
}
public static void main(String []args)
{
   JavaSwap1 jsp = new JavaSwap1(3,4);
   System.out.println(jsp.x+" "+jsp.y);
   jsp.swap();
   System.out.println("Swapped");
   System.out.println(jsp.x+" "+jsp.y);
}
}
方法2:(利用数组)
class JavaSwap2
{
public static void main(String []args)
{
   int []num=new int[]{1,2};
   System.out.println(num[0]+" "+num[1]);
   swap(num);
   System.out.println("Swap Successfully! Congratulate!");
   System.out.println(num[0]+" "+num[1]);
}
public static void swap(int []n)
{
   if(n.length==2)
   {
    int temp=n[0];
    n[0]=n[1];
    n[1]=temp;
   }
   else
   {
    System.out.println("Error!");
    return;
   }
}
}
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值