![55e4d6951be5e19377c967124ba5cc74.png](https://i-blog.csdnimg.cn/blog_migrate/4e7421a4fbc40558449e44c83f82f9a5.jpeg)
值传递:指的是在方法调用时,传递的是参数是按值的拷贝传递。
特点:传递的是值的拷贝,也就是传递后就互不相关了。
引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的是引用的地址,也就是变量所对应的内存空间的地址。
特点:传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。
对于这两种传参方式,假如你写过一些java程序后,你就会发现这不就是看传的参数是基本类型的还是引用类型的吗?(其中比较特殊一点的是String类型和基本类型的包装类类型,它们也是按值传递的,但它是属于引用类型,对于还不知道包装类型的,可以看我之前的一篇博客:现世安稳:java学习基础(三)关于自动拆箱与自动装箱)
一、值传递
话不多说,我们直接分析代码运行结果:
public
![63b19f60eceab9a05e288c9dc8cba943.png](https://i-blog.csdnimg.cn/blog_migrate/5587fb21136d3fcb2f519561f56301d3.png)
对于基本类型,这里我只举了int类型的值传递例子,可以看到在main方法里的a与test1方法里面的a并没有什么关系,在test1方法里面改变a的值并不能影响main方法里的a值。
![264e35514b8c6b0fe7f7db61c6061d50.png](https://i-blog.csdnimg.cn/blog_migrate/27ac87438640c6c95de95032978386ac.jpeg)
那对于引用类型String和基本类型的包装类而言,它们也都是值传递。所以我们先来了解一波String类型。
public
![85c4b4350ba32917168c98570edbbc58.png](https://i-blog.csdnimg.cn/blog_migrate/3b821b79e5eb33ef42bcf3a2f7e44394.png)
分析上面代码,可以看出与int类型值传递的效果是一样的。
对于String类型,我们有两种常用的创建对象的方法,一种是用字符串直接量,一种是用字符数组。但java把字符串直接量也当做是String对象,因此,利用字符串直接量可以按如下两种方式去创建。
//利用字符串直接值创建对象的方法
但这两种方式却不能说完全一样,且看测试代码如下:
![4876c12b3327e62af8caed9475851603.png](https://i-blog.csdnimg.cn/blog_migrate/4fc1a22107cfb95439db0f5146f9dbd2.jpeg)
![bbfefaf9c4865a350a7a878a0b69a2ee.png](https://i-blog.csdnimg.cn/blog_migrate/4d9e71272bff6d71e1705d3fa3587be2.png)
根据==判断为false可知二者并不是指向同一个引用,但我们之前说String类型对象是不可变得,它的内容是不能改变的,既然根据equals判断为true,它们的内容是一样的,那为什么二者却不是指向同一个引用呢?
其实,很简单,正是由于字符串在程序设计中被频繁使用,当它本身又不可变,所以JVM为了提高效率并节约内存,对具有相同字节序列的字符串直接量使用同一个实例。这个实例叫做“限定(interned)的字符串”。因此两种创建对象的方式其实是不同的,s2是指向了一个限定的字符串“hello”,这个是在栈内存中的,而s1则是实实在在指向了创建的一个字符串对象,它的值为“hello”,这个则是在堆内存的。
![6b8d6b35f26d284dd88d8f612d0d9300.png](https://i-blog.csdnimg.cn/blog_migrate/fe8be92a8a0e7c5664685af04a7bf51b.jpeg)
假如你对什么是限定字符串和字符串不可变性的概念理解了,下面的一段代码你就应该能很快理解了:
public
![9150077a9e62c58e8913ccfb228f0f2f.png](https://i-blog.csdnimg.cn/blog_migrate/3f8d1abd79cce192074372da163129f5.png)
分析:首先对于s1和s4大家应该好理解,创建了两个对象,引用肯定是不相同的;其次对于限定字符串“hello”,由于s2与s3均指向它,因此二者的引用是相同的,所以s2==s3为true。至于s1和s2,上面已经解释过了。最后我再说明一下String类型的不变性。看到s3输出为helloworld,不明所以的你可能以为这个不就和不变性矛盾了吗?其实不然,且看下图:
![19b040fc6901482730f1ccb79b85f842.png](https://i-blog.csdnimg.cn/blog_migrate/cab9966e0aac5820b0c7a8c1a5f5d889.jpeg)
看完图,你就会明白,不变的是“hello”,但s3的引用关系发生了改变,指向了存放"helloworld"的内存空间。
二、引用传递
看完上面int和String类型的值传递,我们再来看下什么是引用传递。事实上,java里面参数传递只有值传递一种,因为引用传递可以相当于传递的是引用值。至于引用值是什么,我们可以类比C语言得指针变量,它存放的也是一段内存空间的地址。
引用传递事实上更好理解,尤其是你对C语言中指针概念有所了解的话,这个直接分析一遍代码,就能理解了。
public
![f4267d284af3ed9869cc69396a4f9cee.png](https://i-blog.csdnimg.cn/blog_migrate/27787f64eee410d7505e467e94a31274.png)
首先在main方法中,我们创建了A类的一个对象a,并把它的age设为2,然后我们将a作为参数传到test1方法中,并再次修改age值,然后输出test1中a.age值,执行完后返回到main函数里,再输出main中a.age,且二者结果一致。结合内存图示来理解,会很容易理解:
![9435c2f7b12ee78dac7b1159df963a23.png](https://i-blog.csdnimg.cn/blog_migrate/bf7979eb57e2906e66cf2dab0565df25.jpeg)
在不同方法之间,看似传递的是一个对象,但其实对象的位置并没有动,只是将存放对象的内存地址进行了传递,这样当改变对象的一些属性时,也会的的确确改变堆内存中对象的某些属性值,因此返回到main函数后由于a依旧指向同一块区域,所以去读取数据的时候,值就会变化了。