写在前面
1、本文解决的问题:Java是按照值传递还是引用传递?
2、写作背景:QT项目刚刚结束,开发过程中被 C++ 的引用传递教育了好几回,同时,作为一名热爱Java领域的“打工人”,Java中方法参数的传递是怎样的呢?
3、写作心情:涕笑三声
预备知识
1、什么是形参和实参?
形参: 形式参数,用于定义方法的时候使用的参数,是用来接收调用者传递的参数的。
实参: 实际参数,用于调用时传递给方法的参数。
例如:
public class Test {
public static void main(String[] args) {
test("涕笑三声"); //"涕笑三声" 为实参
}
public static void test(String s){
//s 为形参
}
}
2、什么是值传递和引用传递(也可以说是值调用和引用调用)?
值传递: 是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对 参数进行修改,将不会影响到实际参数。
引用传递: 是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
3、掌握Java基础知识。
证明
在Java中只有两种类型的参数传递:基本数据类型和引用数据类型。
所以,只有确定这两种类型的传递方式,才能确定Java方法参数传递的方式。
定义: 可以确定的是,Java 是按照值传递。
定理: 值传递和引用传递的定义与特征。
下面为Java是按照值传递的求证过程:
向方法传递一个基本类型的参数值,然后在方法中对参数值加1,观察方法调用前的值是否发生变化。
代码如下:
public class Test {
public static void main(String[] args) {
int n = 1;
System.out.println("调用前原数值:" + n);
basicTypeTest(n);
System.out.println("调用后原数值:" + n);
}
public static void basicTypeTest(int n){
n = n+1;
System.out.println("方法中改变后:" + n);
}
}
运行结果:
调用前原数值:1
方法中改变后:2
调用后原数值:1
可以看到,主函数中的数值没有发生变化。
所以求证1为真。
拷贝过程如下图:
求证1结论:
1、基本数据类型是按照值传递
2、一个方法不可能修改一个基本数据类型的参数。
3、方法接收的是数值的一个拷贝。
向方法传递一个引用数据类型参数,修改对象参数状态,观察方法调用前所对应的值是否发生变化。
代码如下:
public class Test {
public static void main(String[] args) {
//创建一个实例
ReferenceTypeTest refer = new ReferenceTypeTest("我是张三");
//打印对象名字
System.out.println("调用前名字:" + refer.getName());
//修改对象名字
updateName(refer);
//修改后打印对象名字
System.out.println("调用后名字:" + refer.getName());
}
//修改参数方法
public static void updateName(ReferenceTypeTest refer){
refer.setName("涕笑三声");
System.out.println("方法中改变后:" + refer.getName());
}
}
class ReferenceTypeTest{
private String name;
public ReferenceTypeTest(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
}
运行结果:
调用前名字:我是张三
方法中改变后:涕笑三声
调用后名字:涕笑三声
可以看到,主函数中的数值发生了变化。
参考基本类型的传递的逻辑,这里好像可以判定引用数据类型的传递是按照是引用传递的方式。
但有一个问题!!!
引用传递有一个重要特征:传递的是值的引用,也就是说传递前和传递后都指向同一个引用。 这里只能证明引用数据类型的传递可以改变堆中的值,无法判断传递前后是否为同一个引用。
所以,如果传递前后是同一个引用,则求证2不成立
向方法传递两个引用数据类型参数,然后在方法中交换两个参数引用,观察方法调用前的两个对象引用是否交换
代码如下:
public class Test {
public static void main(String[] args) {
//创建实例 refer1
ReferenceTypeTest refer1 = new ReferenceTypeTest("我是张三");
//创建实例 refer2
ReferenceTypeTest refer2 = new ReferenceTypeTest("涕笑三声");
//打印 refer1,refer2 的名字
System.out.println("调用前名字 refer1:" + refer1.getName() + "\t" + "refer2:" + refer2.getName());
//把引用传递到方法中,方法中接收的引用进行交换
changeReference(refer1,refer2);
//方法执行后打印 refer1 ,refer2 的名字
System.out.println("调用后名字 refer1:" + refer1.getName() + "\t" + "refer2:" + refer2.getName());
}
//交换引用方法
public static void changeReference(ReferenceTypeTest refer1,ReferenceTypeTest refer2){
ReferenceTypeTest temp = refer1;
refer1 = refer2;
refer2 = temp;
System.out.println("方法中交换后 refer1:" + refer1.getName() + "\t" + "refer2:" + refer2.getName());
}
}
class ReferenceTypeTest{
private String name;
public ReferenceTypeTest(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
}
运行结果:
调用前名字 refer1:我是张三 refer2:涕笑三声
方法中交换后 refer1:涕笑三声 refer2:我是张三
调用后名字 refer1:我是张三 refer2:涕笑三声
可以看到,方法参数引用交换,并没有影响到原对象引用。而 传递后的引用是原引用的拷贝(地址值) 。
所以,引用传递前后不是一个引用,再次求证不成立,求证2为成立。
求证2对象参数修改过程如下图:
再次求证 引用交换过程如下图:
证明2结论:
1、引用数据类型按照值传递
2、一个方法可以可以修改对象参数的状态
3、方法接收的是一个引用地址(地址值)的拷贝
结论
1、Java 程序设计语言总是采用按值传递。
2、一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)
3、一个方法可以改变一个对象参数的状态。
4、一个方法不能让对象参数引用一个新的对象。
从代码逻辑上来看
if("求证1" && "求证2"){
System.out.println("Java 程序设计语言总是采用按值传递。")
}else{
System.out.println("并非Java语言");
}
——THE END——
最后,在新的一年,祝各位道友身体健康,万事如意。