之前学习C#的时候,是彻底在工做岗位上学习,一些底层较为深刻的道理都不是很清楚。现在学习了Java,对于Java参数传递只能传值,不能传引用(指针)感到很困惑,在C#中不是经常说把某个引用传递到函数中吗?甚至C#有至关简便的ref、out参数,明明白白的传引用。java
通过一番探索,得出的结论代表,Java中我无论你究竟是传值仍是传引用,只须要记住原生数据类型(值类型)和String做为参数传递的时候,其本来的值都不会发生改变;而引用类型在做为参数传递时,函数中对其的操做,都会反馈到引用所指向的值。程序员
这个结论不是和C#如出一辙嘛,除了C#能够方便的使用ref、out之外……编程
下面对Java中参数传递只能传值进行解释,事实证实,之前我对于C#的传值仍是传引用理解的也不够深刻:函数
先申明:java中的参数传递都是值传递,没有引用传递。
值传递的概念:值传递会为所传递的对象(这里的对象不是java里的对象object,而是通常意义上的对象)从新开辟一块空间,因而对所传对象的操做不会影响到原对象。
现象:有种说法是java中基本数据类型做参数是值传递,对象作参数是引用传递。其实是错误的,真正的引用传递,是相似C语言中的指针传递,和C#中的ref参数。
理解:为何对象作参数也是值传递呢?
分析:对象作参数传递的是对象的引用,暂且能够理解成对象的地址。因为是值传递,那么被传递的对象自己是不会有任何变化的,因此该对象的引用是不会发生任何变化的。学习
一、先举一个原生数据类型的例子:测试
测试函数:this
public static void ValueTrans(int a){ a = 2;//新产生一个值,开辟一个值类型出来,故值不会被修改。 }
测试此函数的代码:spa
int a = 1; System.out.println("a's value(before) = " + a); ValueTrans(a); System.out.println("a's value(after) = " + a);
打印出的结果:设计
a's value(before) = 1 a's value(after) = 1
安装Java值传递的思想来考虑,这个结果是显而易见的,pass by value,我只是把这个数值传给你使用。指针
二、那么Integer如何理解呢?
测试函数:
public static void ValueTrans2(Integer a){ a = new Integer(2); }
其中a也是一个对象,那么这么传入,是否是传引用呢?调用了这个函数,a的int值会不会被修改呢?
测试此函数的代码:
Integer a2 = new Integer(1); System.out.println("a2's value(before) = " + a2); ValueTrans2(a2); System.out.println("a2's value(after) = " + a2);
打印出的结果:
a2's value(before) = 1 a2's value(after) = 1
结果代表,值仍旧没有被修改。
如何解释这个现象:
理解这里的关键是区分对象和引用。这里声明的a2是一个引用,而不是一个对象(只是Java把它设计为看上去好像是对象同样)。这个引用它指向了一个对象,这个对象就是后面用new关键字生成的对象。所以,能够说a2指向了一个Integer对象。
在调用ValueTrans2方法的时候,程序将a2做为参数传递给ValueTrans2方法了。这里仍然是值传递,在ValueTrans2调用过程当中,会产生一份新的引用(不妨叫作y)。此时,a2和y指向了同一个对象。
可是此时,咱们让y指向另一个对象, y=new Integer(2); 此时a2和y就指向了不一样的对象。y修改了它指向的对象的属性,很显然不会影响到a2指向的对象。
(这里不妨说明一下引用类型的传递,如我把一个引用类型x传入函数中,这里也是值传递,在函数被调用的过程当中,产生了一个新的引用y,x和y指向同一个对象。此时修改y指向的对象,也即修改x所指向的对象!那么引用类型的传递,会形成x所指向的值被修改)
图解以下:
三、如何理解String?
若是你理解了2当中Integer的传递方式,那么我想String也不难理解。咱们新建一个String,
String string = "old String";
实际上就是
String string2 = new String("old String");
Java的机制,形成当你使用一个String如"old String"时,会新建一个对象出来。
本着负责任的态度,仍旧把相应的测试函数贴上来:
public static void StringTrans(String string){ string = "new String"; }
测试函数的代码:
String string = "old String"; System.out.println("string's value(before) = " + string); StringTrans(string); System.out.println("string's value(after) = " + string);
打印出的结果:
string's value(before) = old String string's value(after) = old String
缘由就是,实际上函数StringTrans是复制了一个引用指向原来的对象,可是函数又让这个引用指向了新的String对象。这不会对原来的引用和原来的对象形成任何影响!
四、如何理解引用类型是传值?
测试自定义类:
public class People { public int age; public String name; public People(int age, String name){ this.age = age; this.name = name; } }
测试的函数:
public static void ReferTrans(People people){ people.age = 2; people.name = "lisi"; }
测试函数的代码:
People people = new People(1, "zhangsan"); System.out.println("people's age(before) = " + people.age); System.out.println("people's name(before) = " + people.name); ReferTrans(people); System.out.println("people's age(after) = " + people.age); System.out.println("people's name(after) = " + people.name);
打印的结果:
people's age(before) = 1 people's name(before) = zhangsan people's age(after) = 2 people's name(after) = lisi
设新建的对象是PEOPLE,其引用是people,在调用ReferTrans方法是,仍然是值传递,方法赋值了一个引用people2,people2仍旧指向PEOPLE,因此操做people2对对象进行修改,即为对PEOPLE进行修改(String的话在此时已经new了一个新对象出来),和使用people的效果同样。表现出现来现象就是,当引用类型传入函数时,函数中对其的修改,都会表如今原来的引用类型上。
图解以下:
我一直认为只给分析过程,不给结论的都是耍流氓的行为。
结论:
一、在实际的使用过程当中,咱们没必要纠结那么多,原生数据类型和String,在传入函数的时候,无论函数里面作什么,他的值都不会被修改。引用类型传递到函数中,只要函数中不是让参数指向一个新建的对象,那么函数中的修改会反映到此引用类型上。
二、平时的处理上,能够把String和原生数据类型放在同一类的状况下使用,由于Java内部使用了不少的机制,让String看起来跟原生数据类型同样。可是String归根结底是一个引用类型,如在进行比较时,其余原生数据类型可使用==,而String必须使用equals方法才能比较其内部的值(这点C#作的很是好,直接使用==,不让程序员过度的了解底层,我在作C#编程的过程当中,甚至历来都没有以为string和值类型有什么不一样)。
三、Java确实是只能传值,不能传引用。所谓传引用,只在C、C++的传指针和C#的ref中存在。