"Java 中只有按值传递",做为初学者初看到这几个字有点不敢相信,无数次经过函数改变过对象,无数次跟同事说 Java 在传对象的时候是按引用传递。后来细细想一想,之因此觉得 Java 传对象是按引用传递是由于其中有不少概念都没理清楚,与 C++ 中的搞混了。从 C++ 转 Java 的时候将 C++ 中的知识点映射到 Java 没错,这有利于 C++ 转 Java 的人更快的学习 Java。但一旦映射错误就很容易造成固定思惟。java
在 C++ 和 Java 中都有引用的概念,但他们彻底不是同一个东西。Java 中的引用更相似 C++ 的指针,C++ 的引用在 Java 中并没有对应概念。在 C++ 中有按值传递、按指针传递和按引用传递三种,而在 Java 中没有 C++ 引用和指针的概念,只保留了按值传递。程序员
为了更好的说明 Java 中只有按值传递,先来看看 Java 的数据类型,Java 的数据类型分为基本数据类型和引用类型,其中:架构
基本类型包括 byte/short/int/long/float/double/char/boolean 八种,基本类型在内存中地址中保存的即自己的值,其通常都在栈上分配。函数
引用类型指向一个对象,它与 C++ 的指针很是类似。但 C++ 的指针能够指向基本类型和类对象,而 Java 的引用只能指向类(枚举、接口等)对象。Java 中对象自己在堆上分配,而引用类型在栈上分配,其内存地址中保存的是对象在堆中的地址。两种类型在内存中的布局以下:布局
1 class MyInteger {
2 int value;
3 }
4
5 public class TestReference {
6
7 public static void changeBasic(int arg) {
8 arg = 2;
9 }
10
11 public static void changeReference(MyInteger arg) {
12 arg.value = 2;
13 }
14
15 public static void main(String[] args) {
16
17 int basicTypeA = 1;
18
19 MyInteger referenceTypeA = new MyInteger();
20 referenceTypeA.value = 1;
21
22 System.out.println("调用 changeBasic 以前 basicTypeA 的值 "+ basicTypeA);
23 changeBasic( basicTypeA);
24 System.out.println("调用 changeBasic 以后 basicTypeA 的值 "+ basicTypeA);
25
26 System.out.println("调用 changeReference 以前 referenceTypeA 的值 "+ referenceTypeA.value);
27 changeReference( referenceTypeA);
28 System.out.println("调用 changeReference 以后 referenceTypeA 的值 "+ referenceTypeA.value);
29 }
30 }
复制代码
运行结果以下:性能
其实否则,按值传递的意思想必你们都知道:传递的是值的的拷贝,好比上面代码中的调用 changeBasic(basicTypeA) 时,arg 是 basicTypeA 的一个拷贝,因此不管对 arg 作任何操做都不影响 basicTypeA 变量自己。而调用 changeReference(referenceTypeA) ,arg 也是 referenceTypeA 的一个拷贝,可是因为 arg 和 referenceTypeA 都是引用类型且他们指向同一对象,因此经过 arg 修改对象,referenceTypeA 也能看到。两种类型变量在内存中调用过程以下:学习
因此能够看出不管是基本类型仍是引用类型,都是按值传递。只是因为它们在内存中所表示的内容不一样,最后表现出来的结果也有所不一样。同理,在 C++ 中的按值传递、按指针传递和按引用传递理论上均可以归为按值传递(其实这个归类在学 C++ 的时候就概括出来了,只是后来反而忘了)。spa
Java 的引用相似于 C++ 的指针,可是 C++ 的对象(不包括基本类型)传递提供了对象自己直接传递和指针传递两种方式(引用方式不谈),而 Java 对象只有对引用进行传递这一种,不存在直接将对象自己进行传递。设计
在 C++ 中以上两种传递方式能够自行选择,而 Java 里面只有第 2 种方式,凡事有利有弊,有时候咱们并不想在函数中改变原对象的内容,这里我就踩过一个坑,咱们的项目中有个对象经过管道传递的流程以下:3d
funcA 与 funcB 是两个不一样的人负责的,一次升级后 funcB 的负责人发如今函数中获取的对象 X 内容不对,一开始还觉得是传递对象 X 的接口出现了错误即是一顿排查,知道最后才发现对象 X 是在升级后在 funcA 中被修改了,浪费了很多时间。固然这个架构的流程设计的不合理是主要缘由(只须要在分发的时候讲对象 X 作手动拷贝便可避免上述问题),可是不不影响咱们抛出 Java 只能对引用进行传递的弊端。
在调用链较长、各类 for/while 循环中很容易就犯了上述错误,解决方案固然就是手动拷贝对象,Java 中拷贝对象有如下两种方式:
其中第二种方式能避免深浅拷贝的问题,但调用比较耗时。第一种也能避免深浅拷贝可是须要本身手动去写相应的代码,若是嵌套较深,代码将很是复杂。至于深浅拷贝的问题能够自行百度,其本质仍是由于只是将对象的引用进行了传递而致使的一些问题。记得关注公众号哦,记录着一个 C++ 程序员转 Java 的学习之路。