Java中的值传递和引用传递

Java中的值传递和引用传递

Java中的数据类型

Java中的数据类型我们知道分为基本数据类型和引用数据类型两种,所以对应存储他们的变量也分为基本数据类型的变量和引用数据类型的变量。

基本数据类型变量之间的赋值(传递)

对于基本数据类型,基本数据类型变量之间的传递均是值传递。那什么是值传递?

int类型来说,看个例子:

public static void main(String[] args) {
    int m = 44;
    int n = m;
    System.out.println("m = " + m + ",n = " + n);//m = 44,n = 44
    m++;
    System.out.println("m = " + m + ",n = " + n);//m = 45,n = 44
    n = n + 4;
    System.out.println("m = " + m + ",n = " + n);//m = 45,n = 48
    changeBaseType(m,n);
    System.out.println("m = " + m + ",n = " + n);//m = 45,n = 48
}

public static void changeBaseType(int a,int b) {
    a = a + 5;
    b = a + 10;
    System.out.println("a = " + a + ",b = " + b);//a = 50,b = 56
}

先分析结果:

第一个输出语句是没什么问题,将m赋值给n,所以结果是:m = 44n = 44

第二个输出语句,输出前执行了m++;,结果m的值加1,变为45,没问题,那n的值为什么还是44而不是45?

因为上面的int n = m;这句赋值代码来说,其过程为:首先m存储的值是44,等进行n = m赋值时,系统会复制一份44这个值的副本,然后将这个副本赋值给n,所以最后结果是mn虽然存储的都是44这个值,但是并不是同一个44,两者的内存地址不同,是互不相干的两个对象。因此当其中一个发生变化时,并不会影响另一个。

所以第二个输出语句前虽然执行了m++导致m由原来的的44变为45,但n的值依旧为44,所以结果为:m = 45n = 44

同理可得,n变化时,m不变,所以第三个输出语句结果为:m = 45n = 48

再来看第四个输出语句,第四个输出语句前,我们将mn传给changeBaseType(int a,int b)这个方法并执行了该方法,进入方法前我们先说方法的参数问题,changeBaseType(int a,int b)带了两个参数ab(方法中的参数我们称为形式参数),形参我们可理解为方法内另外定义的变量,所以,这里我们可理解为这方法中定义了ab这两个变量,当我们将实参mn传给方法时,系统通过a = mb = n赋值,赋值过程我们上面说过了,最终ab存储的分别是mn所存储值的副本,所以方法里面都是对副本 进行操作,并不会影响mn所存储的值。所以main方法中的第四个输出语句结果为m = 45n = 48,而不是方法中ab的值a = 50b = 56。这里注意方法中的参数名我故意写成ab而不是mn是为了更方便理解,方法中的参数名可以随意取,写成mn也可以,一般情况为了方便,习惯将形参名字和实参名字保持一致(即将a、b取名为m、n),但注意,实际并不是同一个东西。

引用数据类型变量间的赋值(引用传递)

引用数据类型变量之间的传递是引用传递(其实还是值传递,因为根本上来说,引用数据类型变量间的赋值,是内存地址的赋值,而这个内存地址的赋值是上面已经说过的值传递。

例如:

Person p1 = new Person("张三");
Person p2 = p1;

这两句代码实际上只创建了一个Person("张三")实例。Person p1 = new Person("张三");这句代码先是通过new关键字申请内存创建了一个Person("张三")实例,然后赋值给了p1这个变量,赋值结果是p1并没有直接存储Person("张三")这个实例本身,而是存储了它所在的内存地址,p1通过这个内存地址间接存储Person("张三")这个实例。就好比你要找人,你得先知道人家在哪,知道人家具体位置才能找到人。虽然变量p1存的是实例对象所在的内存地址,但我们对变量p1做的操作全都会反映到p1所指向的实例对象上。

Person p2 = p1;这个赋值语句只是将变量p1存储的内存地址复制了一个副本,然后将这个内存地址的副本赋值给了p2,结果是p1p2存储的内存地址相同,有没有似曾相识的感觉?没错,其实就是上面的值传递,既然存的内存地址相同,也就是说p1p2指向的是同一块内存,因此两者指向的都是同一个Person("张三")实例。整个过程下来只有一个实例对象,只是p1p2这两个变量都指向了这个对象实例,就相当于一个人有两个外号一样。

接下来看下面的例子:

public void test(){
    Person p1 = new Person("张三");
    Person p2 = p1;
    System.out.println("p1 = " + p1 + ",p2 = " + p2);//p1 = Person{name=张三},p2 = Person{name=张三}
    p2.setName("张三111");
    System.out.println("p1 = " + p1 + ",p2 = " + p2);//p1 = Person{name=张三111},p2 = Person{name=张三111}
    p2 = new Person("李四");//更改引用,此时p2已经指向了另外一个实例对象
    p2.setName("李四111");
    System.out.println("p1 = "+ p1 +",p2 = "+ p2);//p1 = Person{name=张三111},p2 = Person{name=李四111}
}

第一打印语句结果应该没什么疑问。

第二个打印结果,打印前,我们通过代码p2.setName("张三111");p2所指向实例的name字段的值由原来的“张三”改为了“张三111”,结果发现p1name字段也变成了“张三111”,这是为什么?这就得回到上面说的引用数据类型的变量赋值过程。上面我们说了Person p2 = p1;这句代码的结果是p1p2指向了同一个Person(“张三”)实例,因此当我们通过p2将它所指向对象中的name字段由原来的“张三”改为“张三111”后,p1自然的也会跟着变化。

第三个打印语句,打印前执行了p2 = new Person("李四");p2.setName("李四111");然后在打印p1,发现p1没有发生改变,前面我们说了p1p2指向的是同一个实例对象,p1p2只要其中一个发生改变,另外一个也会跟着变化。但这里p2发生了变化,p1为什么没跟着改变?
这里注意,p2 = new Person("李四")这句代码已经通过new关键字重新创建了一个Person("李四")实例,然后赋值给了p2。这时p2存储的是Person("李四")这个实例的内存地址而不是之前Person("张三111")这个实例的内存地址了,而p1存储的还是Person("张三111")这个实例的地址,此时p1p2已经指向了不同的实例,两者已经不相关了,所以两者互不影响,后面p2.setName("李四111");这句代码和p1就更加没有关系了,p1自然也就不会跟着变化。

至于调用带形参的方法,这个和上面的值传递一样,只要知道,方法的形参其实可以理解为方法内部另外定义的变量,传入实参时就是赋值操作,只是对基本数据类型的变量赋值是值传递,而对于引用数据类型的变量赋值只是把实例所在的内存地址赋值给变量而已,上面已经说了这个内存地址的赋值是值传递,这也是为什么大家都说Java中其实只有值传递没有引用传递的原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值