kevin

奋斗的码农

java中值传递与引用传递分析

0x01

java中是传值还是传引用(英文:java called by value or called by reference?)

首先搞清楚一点,不管参数的类型是什么,一律传递参数的副本。如果java是传值,那么传递的是值的副本,如果java传引用,那么传递的是引用的副本。

0x02

java中,变量类型分为两类:

①基本类型变量 int,long,double,float,byte,boolean,char,java中是传值的副本,副本变了,自己不变,这一点与C++中相同

②对于一切对象型变量,java都是传引用的副本,其实传引用的副本就是复制指向地址的指针,而不是自己实际值的副本,原因:以为对象类型是放在堆里面的,一方面,速度相对于基本类型比较慢,另一方面,对象类型本身比较大,如果采用重新复制对象值的办法,浪费内存且速度慢。

举个例子:张三(函数)要打开仓库(地址)并且检查库里的货物,有必要新建一座仓库并放入相同货物给张三吗?没有必要,只需要把钥匙(引用)复制一份给张三就好,张三会拿复制的钥匙(引用副本,有时效性,函数结束,钥匙销毁)打开仓库。

只不过java不像C++中有显著的*和&符号(这里java与C++不同,在C++中,当参数是引用类型时,传递的是真实引用而不是引用副本)。

还要注意String,String也是对象型变量,所以它必然是传引用副本,只不过它是一个非可变类,似的传值与传引用显得没什么区别。

关于很多java书籍中说的不管是基本数据类型还是对象类型,都是传值,这种说法也是对啊,以为他们把引用副本看成了一种值。

总结:String可以理解为修改了原来的副本,StringBuffer理解为修改的就是原值。

String是final类型的,不可以继承和修改这个类。

 

 

例子如下:

 1 package com.day01;
 2 
 3 public class zhiyuyinyong {
 4 
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7 //        boolean test=true;
 8 //        System.out.println("beforetest:test="+test);
 9 //        test1(test);
10 //        System.out.println("aftertest:test="+test);
11         
12         //--------------------------
13 //        StringBuffer string=new StringBuffer("hello");
14 //        test2(string);
15 //        System.out.println(string);
16         //输出hello,world
17         //原因:StringBuffer是产生一块内存空间,相关的增删改操作都在其中进行,所以添加一句“world”仍然是在同一段内存的地址上进行,str指向的引用并没有改变
18         //--------------------------
19         String string="hello";
20         System.out.println(string);
21         System.out.println("修改后的string="+test3(string));
22         System.out.println(string);
23         
24         //输出hello  
25         //.ello  这个就是str指向了hello,str=str.replace("h", ".");就是对hello的副本进行了修改
26         //hello
27         //--------------------------
28         //--------------------------
29                 //String string="hello";
30     
31         System.out.println("修改后的string="+test4(string));
32                 System.out.println(string);
33                 
34                 //输出world  原因:str="world";这个过程是系统自动生成了新String对象,并把这个新对象的值设置为world,然后这个对象的引用是str,可以理解为str这把钥匙原来指向hello仓库,现在指向world仓库。隐含着新建了string对象后,新对象与hello没有关系,函数结束,str作用消失,原来内存地址上的内容未加改变,打印仍是hello
35                 //--------------------------
36     }
37 
38 //    public static void test1(boolean test){
39 //        boolean test22=true;
40 //        test22=!test;
41 //        //这个test就是主函数中传过来的bool值,我们对这个bool值就行修改,发现这个test仅仅是主函数中test的一个副本,修改这个test变量,主函数中的test是不变化的
42 //        
43 //        System.out.println("in test,test="+test22);
44 //    }
45     
46     public static void test2(StringBuffer str){
47         str.append(".world");
48         //这里形参传过来的就是一个StringBuffer的引用,java对于引用形式传递对象类型的变量时,实际上是将引用作为一个副本传进方法函数的,这个副本指向的是原来str对象的地址,通过引用副本找到地址,修改了地址中的值,地址中有对象,对象的值就被修改了
49     
50     }
51     public static String test3(String str){
52         //string.replaceAll(regex, replacement)=".world"; //这里string=“.world”相当于新建了一个String对象,这个与原来形参传过来的string没有任何关系了
53         str=str.replace("h", ".");
54         //这里形参传过来的就是一个String的引用
55     return str;
56     }
57     
58     public static String test4(String str){
59         //string.replaceAll(regex, replacement)=".world"; //这里string=“.world”相当于新建了一个String对象,这个与原来形参传过来的string没有任何关系了
60         str="world";
61         //这里形参传过来的就是一个String的引用
62     return str;
63     }
64 }
View Code

涉及到内容的一些解释:

final是修饰变量的,不是修饰对象本身的

举例说明:
final String s = "hello";
这里final是修饰变量s的,而不是修饰"hello"
至于说String不能修改,是因为String类是immutable的,就是不能修改的意思,immutable也是一个修饰符

final StringBuffer sb = new StringBuffer("hello");
这里final修饰了sb,sb就不能指向其它对象了
比如sb = new StringBuffer("hi");这样是不允许的
但是sb所指向的那个对象本身还是可以改变的
比如sb.append(" world");是允许的,因为sb并没有变,sb仍然指向的是那个对象,变化的是对象本身

 

posted on 2016-07-17 17:44  CUMT存在  阅读(302)  评论(0编辑  收藏  举报

导航