Java方法内交换值不生效的原因

交换地址或者值不生效

读者如果对栈和堆的概念理解较深,可以直接滑到底部看总结

传参其实是使得形参得到一份实参副本(形参收到的传参),实质是在内存中新开辟了一片空间,而并非传参获得引用,如果传递的是基本数据类型,则传递值的copy

class Student
{
    int age;
    public Student(int age)
    {
        this.age = age;
    }
}

public class JavaIsAboutPassingByValue
{    
    // 基本数据类型会直接复制一份到方法栈中
    public static void swapValue(int age1, int age2)
    {
        int temp = age1;
        age1 = age2;
        age2 = temp;
    }
    
    public static void main(String[] args)
    {
        Student stu1 = new Student(3);
        Student stu2 = new Student(7);
     
        System.out.println("stu1 " + stu1.age + " stu2 " + stu2.age);
        swapValue(stu1.age, stu2.age);
        System.out.println("stu1 " + stu1.age + " stu2 " + stu2.age);
    }// output: 
    // stu1 3 stu2 7
    // stu1 3 stu2 7
}

如果传递的是引用数据类型,则传递的是地址值的copy(新建了一个引用指向对象,并非获得实参引用或对象),所以方法内进行交换操作,在方法内的新建引用确实进行了交换,通过交换的引用看到引用的指向发生了交换,但方法出栈之后,新建的引用随即销毁。

public class JavaIsAboutPassingByValue
{    
    // 实质是交换 s1,s2 两个复制来的引用指向,而不是交换s1,s2两个对象的地址值
    public static void swapReference(Student s1, Student s2)
    {
    // 此处为了区别实参stu1,stu2,我将形参命名为s1,s2
    // 可以更清楚地看清是形参与实参是不同的引用,尽管它们指向同一个地址
        Student temp = s1;
        s1 = s2;
        s2 = temp;
   	// 方法出栈,销毁s1,s2
    }
    
    public static void main(String[] args)
    {
        Student stu1 = new Student(3);
        Student stu2 = new Student(7);
     
        System.out.println("stu1 " + stu1 + " stu2 " + stu2);
        swapReference(stu1, stu2);
        System.out.println("stu1 " + stu1 + " stu2 " + stu2);
    }// output: 
    // stu1 Student@1b6d3586 stu2 Student@4554617c
    // stu1 Student@1b6d3586 stu2 Student@4554617c
}

这个过程中,对象的地址没有发生改变,交换的一直都是引用指向(ex: 有两根电线杆,甲绑了两根绳子,甲1->杆1,甲2->杆2,然后乙通过甲也知道了电线杆位置,也绑了两根绳子,乙1->杆1,乙2->杆2,乙自己将绳子交换,乙2->杆1,乙1->杆2,不影响电线杆位置和甲的指向)
虽然很废话,但是可以让人更容易理解为什么java在方法内操作传参的值进行交换不生效。

Java本质是值传递,方法传参只能得到实参的copy版,方法内对实参进行修改不影响实参值。但通过获得引用,更改引用的引用指向(也有可能是引用的引用里的内容,即值),可以修改引用的实际值。(不修改引用地址,但因为自身也指向引用所指向的对象,所以修改对象可以使得引用的结果也发生变化)

public class JavaIsAboutPassingByValue
{    
    // 通过传参,自身获得地址值,修改对象内的内容(值)
    // 从setValue方法外看,好像也达成了修改的效果
    // 和上面swapValue方法不同的地方在于,此处传参传的是Student引用数据类型
    public static void setValue(Student s1, Student s2)
    {
    	int temp = s1.age;
        s1.age = s2.age;
        s2.age = temp;
    }
    
    public static void main(String[] args)
    {
        Student stu1 = new Student(3);
        Student stu2 = new Student(7);

        System.out.println("stu1 " + stu1.age + " stu2 " + stu2.age);
        setValue(stu1, stu2);
        System.out.println("stu1 " + stu1.age + " stu2 " + stu2.age);
    }// output: 
    // stu1 3 stu2 7
    // stu1 7 stu2 3
}

下面是交换引用的引用,即地址值

class Student
{
    String name;
    int age;
    Pet pet;

    public Student(String name, int age, Pet pet)
    {
        this.name = name;
        this.age = age;
        this.pet = pet;
    }

    @Override
    public String toString()
    {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", pet=" + pet +
                '}';
    }
}

class Pet
{
    String pet;

    public Pet(String pet)
    {
        this.pet = pet;
    }

    @Override
    public String toString()
    {
        return "Pet{" +
                "pet='" + pet + '\'' +
                '}';
    }
}

public class JavaIsAboutPassingByValue
{
    public static void swapPet(Student s1, Student s2)
    {
        Pet temp = s1.pet;
        s1.pet = s2.pet;
        s2.pet = temp;
    }
    
    public static void main(String[] args)
    {
        Student stu1 = new Student("太一", 13, new Pet("亚古兽"));
        Student stu2 = new Student("嘉儿", 11, new Pet("仙女兽"));
        
        System.out.println(stu1 + "\n" + stu2);
        swapPet(stu1, stu2);
        System.out.println("--------------------------------------------------");
        System.out.println(stu1 + "\n" + stu2);
    }// output:
   	// Student{name='太一', age=11, pet=Pet{pet='亚古兽'}}
	// Student{name='嘉儿', age=9, pet=Pet{pet='仙女兽'}}
	// --------------------------------------------------
	// Student{name='太一', age=11, pet=Pet{pet='仙女兽'}}
	// Student{name='嘉儿', age=9, pet=Pet{pet='亚古兽'}}

上述分为四种情况:

  1. 传参类型为基本数据类型(int,char,byte,等 ),方法内交换值不生效
  2. 传参类型为引用数据类型,方法内交换引用类型(传参对象的地址值)不生效
  3. 传参类型为引用数据类型,方法内交换基本数据类型(传参对象的属性)生效
  4. 传参类型为引用数据类型,方法内交换引用数据类型(传参对象的属性的地址值)生效

总结:
方法内交换,对于传参进来的基本数据类型并不生效(情况1)
如果传参为引用数据类型,交换引用本身也不会生效(情况2)
如果传参为引用数据类型,交换引用的引用,会生效(情况3,4)
传参对象的属性,或者其地址值,是归属于传参对象的数据,如果获得它的地址值,操作它的数据,是能够生效的。

如果彻底理解栈和堆的概念,实质便只有两种情况,方法进栈,传参获得的数据永远是局部变量,局部变量无论怎么修改,出栈时都会被销毁,而如果通过传参的数据,对栈外(堆上的数据)进行操作,则方法出栈时,传参数据(局部变量)即使被销毁,在堆上做过的操作依然会被保留。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hastings_Lucifer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值