Java子函数是否会直接改变主函数中的值,Integer等包装类为什么搞特殊?

Java子函数是否会直接改变主函数中的值,Integer等包装类为什么搞特殊?

在读《剑指Offer》时,看到一个字符串操作的讲解,引发一个思考:
Java语言中,子函数是否会直接改变主函数中的值?
答:分情况。

两种情况:

  • 基本数据类型
    简单的值传递,不会改变主函数中的值。
  • 引用类型(对象的引用):
    引用的值传递(相当于指针的地址传递)可以改变对象属性的值。

数组也是一种引用。

实际上是引用的值传递(两个引用指向同一个对象,可以理解为对象的地址传递)
这个时候只能改变引用所指向对象的属性,而不是对对象本身的修改
(不能把一只狗变成猫,只能修改狗的名字)。

回顾

在大一学习C语言的时候,大家在学习函数这一章节的时候,所有老师都会特意强调:

函数调用时是值的传递,子函数形参的改变,并不会影响主函数的实参值。

很典型的例子就是两个数做交换,这里就不再列举了。
如果想在子函数里修改实参的值,在C语言中有三种方法:

  • 用数组存储待修改参数
  • 利用指针
  • 用返回值返回待修改的参数

第一种方法是利用C语言中地址的传递(指针),数组名本身就是个不可修改的指针,指向数组中第一个元素的地址。
因此,前两种方法本质上是一样的。

在C++中,也可用引用类型操作符号&来绑定实参和形参。

问题

那么问题来了,在Java中,并不能直接操控指针,也没有&这种引用类型,怎么办呢?

解决方案

Java虽然不能显式操作指针,但是可以操作对象的引用
利用对象的引用,可以直接修改对象的属性(如果访问权限允许的话),如:


public class Test {
    public static void main(String[] args){
        Person person = new Person("ZhanSan",22);
        fun(person);
        System.out.println("主函数:"+person);
    }


    private static void fun(Person p) {
        p.age++;
        System.out.println("子函数:"+p);
    }
}

class Person{
    String name;
    int age;
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return this.name+" "+this.age;
    }
}

输出:

子函数:ZhanSan 23
主函数:ZhanSan 23

这样是没问题的。
虽然主函数的person和子函数的p都是指向的同一个对象,但是他俩是完全不同的两个引用(可以理解为C语言中的两个不同的指针存着同一个地址,从而指向同一个变量)


但是如果我们想直接传输一个基本数据类型的值进行修改怎么办?
先直接传传看看:

public static void main(String[] args){
		int i1 = 1;
		fun(i1);
		System.out.println("主函数:"+i1);
}
public static void fun(int i2){
		i2++;
		System.out.println("子函数:"+i2);
}

输出:

子函数:2
主函数:1

显然不行!


那么,上面提到,可以中对象来操作,而Java语言又对基本数据类型提供了包装类,那么是否可以用包装类来试试呢?

public static void main(String[] args){
		Integer i1 = new Integer(1);
		fun(i1);
		System.out.println("主函数:"+i1);
}
public static void fun(Integer i2){
		i2++;
		System.out.println("子函数:"+i2);
}

输出:

子函数:2
主函数:1

???
为什么还是不行,前面不是说用对象就可以传的嘛!

开头就提到,可以对对象的属性进行操作,但是并不支持对对象本身进行操作。

我打断点调试发现,在进行i++操作之后,对象引用的值就发生了改变,换句话说,创建了一个新对象
我们来分析一下,上述代码中:

i2++;//实际上进行了自动拆装箱操作,这是Java5的特性

//实际上,它等同于下面三行
int i = i2.intValue();//拆箱
i++;
i2= Integer.valueOf(i);//装箱

而在装箱的过程中,发生了如下操作(看源码):

// Integer源码 
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

简单解释一下,如果-128<i<127,直接从Integer自带缓存中拿一个Integer对象出来(效率会更高)
否则,直接返回一个新对象

因此,无论如何,都会返回一个新对象。

切记,我们通过对象的引用,能改变的,只是对象的属性,而不是对象本身
因此,由于Integer本身就是个对象,所以是没法改变的。

本质上,值的改变,只是自动拆箱,变成基本数据类型了之后,才进行了运算,
运算完成后,再自动装箱,包装成一个对象。

结论

Java中,值的传递分两种:

  • 基本数据类型
    简单的值传递,不会改变主函数中的值。
  • 引用类型(对象的引用):
    引用的值传递(相当于指针的地址传递)可以改变对象属性的值。

数组也是一种引用。

包装类看似特殊,实则不是,只是包装类没有属性供我们修改罢了(这个属性被私有化了,且被final修饰,不可修改)

// Integer源码 
/**	
     * The value of the {@code Integer}.
     *
     * @serial
     */
private final int value;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值