String是值传递还是引用传递

本文解析了Java中String作为参数时的传递行为,揭示了看似矛盾的输出结果,深入探讨了堆栈模型与方法调用中的复制原理。

本文来自慕课网文章:https://www.imooc.com/article/288336https://www.cnblogs.com/zhangliwei/p/12056692.html

今天发现了一个比较有意思的问题。把一个String类型的参数传入方法,并在方法内改变了引用的值。
然后在方法外使用这个值,发现这个String还是之前的值,并没有改变。

这里要向大家介绍一下,大家都知道java在传参时分为值 传递 和 引用传递 。参数为基本类型时是值传递,
参数为封装类型时是引用传递。例如:

基本类型参数

public class Test {
    public static void main(String[] args) {
        int num = 0 ;
        changeNum(num);
        System.out.println("num="+num);
    }

    private static void changeNum(int num) {
        num = 1;
    }
}

打印的结果是num=0。

封装类型参数

public class Test {
    public static void main(String[] args) {
        Product p = new Product();
        p.setProName("before");
        p.setNum(0);
        changeProduct(p);
        System.out.println("p.proName="+p.getProName());
        System.out.println("p.num="+p.getNum());
    }

    private static void changeProduct(Product p) {
        p.setProName("after");
        p.setNum(1);
    }
}

class Product {
    private int num;
    private String proName;

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getProName() {
        return proName;
    }

    public void setProName(String proName) {
        this.proName = proName;
    }
}

运行的结果是:p.proName=after和p.num=1 。

上面的两个例子是明显的值传递和引用传递。但是如果参数是String类型呢?我们看一下具体的例子

public class Test {
    public static void main(String[] args) {
        String str = "ab";
        changeString(str);
        System.out.println("str="+str);
    }

    private static void changeString(String str) {
        str = "cd";
    }
}

大家猜一下运行结果是什么呢?按照前面的例子,String应该是一个封装类型,它应该是引用传递,是可以改变值得,
运行的结果应该是"cd"。我们实际运行一下看看,

str=ab,这如何解释呢?难道String是基本类型?也说不通呀。

这就要从java底层的机制讲起了,java的内存模型分为 堆 和 栈 。

1.基本类型的变量放在栈里;
2.封装类型中,对象放在堆里,对象的引用放在栈里。

java在方法传递参数时,是将变量复制一份,然后传入方法体去执行。 这句话是很难理解的,也是解释这个
问题的精髓。我们先按照这句话解释一下基本类型的传递

问题的精髓。我们先按照这句话解释一下基本类型的传递

  1. 虚拟机分配给num一个内存地址,并且存了一个值0.

  2. 虚拟机复制了一个num,我们叫他num’,num’和num的内存地址不同,但存的值都是0。

  3. 虚拟机将num’传入方法,方法将num’的值改为1.

  4. 方法结束,方法外打印num的值,由于num内存中的值没有改变,还是0,所以打印是0.

我们再解释封装类型的传递:

  1. 虚拟机在堆中开辟了一个Product的内存空间,内存中包含proName和num。
  2. 虚拟机在栈中分配给p一个内存地址,这个地址中存的是1中的Product的内存地址。
  3. 虚拟机复制了一个p,我们叫他p’,p和p’的内存地址不同,但它们存的值是相同的,都是1中Product的内存地址。
  4. 将p’传入方法,方法改变了1中的proName和num。
  5. 方法结束,方法外打印p中变量的值,由于p和p’中存的都是1中Product的地址,但是1中Product里的值发生了改变,
    所以,方法外打印p的值,是方法执行以后的。我们看到的效果是封装类型的值是改变的。

最后我们再来解释String在传递过程中的步骤:

  1. 虚拟机在堆中开辟一块内存,并存值"ab"。
  2. 虚拟机在栈中分配给str一个内存,内存中存的是1中的地址。
  3. 虚拟机复制一份str,我们叫str’,str和str’内存不同,但存的值都是1的地址。
  4. 将str’传入方法体
  5. 方法体在堆中开辟一块内存,并存值"cd"
  6. 方法体将str’的值改变,存入5的内存地址
  7. 方法结束,方法外打印str,由于str存的是1的地址,所有打印结果是"ab"

这样我们理解了java在方法传参的整个过程。其实还是上面那句比较重要的话 java在方法传递参数时,是将变量复制一份,然后传入方法体去执行。

<think>首先,用户的问题是基于提供的引用材料,询问Java中是值传递还是引用传递引用材料来自三个来源:[^1], [^2], [^3]。 关键点总结自参考资料: - 引用[1]:指出Java值传递,但提到一个例子显示对象属性被改变,导致一些人认为基本类型值传递引用类型引用传递。 - 引用[2]:标题是“Java中是引用传递还是值传递?”,内容提到Java总是通过值传递引用传递,并提供了一个例子。 - 引用[3]:明确说Java方法值传递,无论是基本类型还是引用类型传递的都是的副本。并区分了值传递引用传递值传递接收实参的副本,影响原始量;引用传递接收引用本身,可能改变外部对象绑定。 我的回答必须符合系统级指令: - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠(基于提供的引用)。 核心事实:从引用来看,Java值传递引用[3]尤其明确:Java值传递传递的是的副本。 回答结构: 1. **介绍问题**:简要说明Java传递机制。 2. **解释值传递 vs ���用传递**:清晰定义两者区别。 3. **Java的具体实现**:使用例子说明Java如何是值传递。 - 对于基本类型传递的副本,影响原始。 - 对于引用类型传递对象引用的副本,副本指向同一个对象,因此修改对象属性会影响原始对象,但重新赋引用会影响原始引用。 4. **澄清常见误解**:解释为什么有人误以为引用类型引用传递。 5. **结论**:重申Java值传递。 6. **生成相关问题**:在末尾添加相关问题。 生成相关问题: - 基于主题,如: 1. Java引用类型是如何传递的? 2. 如何避免在方法中意外修改对象属性? 3. Java与其他语言(如C++)在传递机制上的区别? 4. 值传递Java中的实际应用场景? 5. 为什么Java选择值传递机制? 回答中引用材料: - 在解释时,自然地添加引用标识。例如:"正如引用[3]所述,Java方法调用是值传递管是基本类型还是引用类型传递的都是‘的副本’[^3]。" 完整回答框架: 1. 开头直接回答问题:Java值传递。 2. 逐步解释: - 值传递的定义。 - 基本类型例子。 - 引用类型例子。 - 为什么属性被改变影响值传递的本质。 3. 结尾总结。 4. 添加相关问题。 确保LaTeX格式: - 行内数学表达式用$...$,但我这里没有数学公式,所以可能需要。 - 如果有公式,要用正确格式,但此主题涉及数学。 最终回答: - 用中文。 - 结构清晰。 </think>### Java中的值传递引用传递解析 在Java中,**方法参数的传递方式是值传递**,无论是基本类型(如int、boolean)还是引用类型(如对象)。这意味着调用方法时,传递的是实参量的副本,而是原始量本身或引用别名。这一机制容易引起误解,尤其是当传递引用类型时,对象属性可能被修改,但本质仍是值传递。下面我将逐步解释,并结合例子澄清核心概念。 #### 1. **值传递 vs 引用传递的区别** - **值传递**:方法接收的是实参量的副本(的拷贝)。在方法内部修改参数会影响原始量。例如,传递一个整数量时,方法内的修改只作用于副本。 - **引用传递**:方法接收的是实参引用本身(如指针的别名),在方法内部修改参数可以直接改变原始量和对象绑定。 两者的关键区别在于传递的“内容”:值传递是拷贝引用传递是共享引用地址(类似“钥匙”的传递)。Java只支持值传递,正如引用[3]所述:“Java 方法调用是值传递管是基本类型还是引用类型传递的都是‘的副本’[^3]。” #### 2. **Java中的值传递实例** - **基本类型传递**:传递的是的副本,方法内部修改影响原始。 例如,引用[3]的代码: ```java void changeInt(int x) { x = 10; // 修改副本,影响原始量 } int a = 5; changeInt(a); System.out.println(a); // 输出:5(原始)[^3] ``` 这里,`a`传递的是`5`的副本,方法内`x=10`只改变副本,`a`保持原。 - **引用类型传递**:传递的是对象引用的副本(而非对象本身)。副本指向原始对象的内存地址,因此方法内修改对象属性会影响原始对象,但重新赋引用会影响原始引用引用[2]的Balloon例子: ```java public class Balloon { private String color; public void setColor(String color) { this.color = color; } public String getColor() { return color; } } void changeColor(Balloon balloon) { balloon.setColor("Red"); // 修改对象属性:影响原始对象 balloon = new Balloon("Green"); // 重新赋引用:只影响副本,原始引用 } Balloon myBalloon = new Balloon("Blue"); changeColor(myBalloon); System.out.println(myBalloon.getColor()); // 输出:Red(属性改变),但myBalloon引用[^2] ``` - 为什么属性被改变引用副本指向同一个对象,属性修改是“间接”操作对象内容。 - 为什么重新赋无效?`balloon = new Balloon("Green")` 只改变副本的指向,原始引用`myBalloon`仍指向原对象。 #### 3. **常见误解澄清** 许多人误认为引用类型引用传递,因为对象属性可被修改(如引用[1]提到的例子)。但这源于混淆: - **误解根源**:引用类型传递的是引用的副本,而非引用本身。副本共享对象访问权,因此属性改变是合理的(值传递的“副作用”),但代表传递机制引用传递引用[1]指出:“当传递是基本类型时是值传递、当传的是引用类型时是引用传递”是一种错误总结[^1]。 - **本质**:Java始终拷贝(基本类型拷贝引用类型拷贝引用地址)。重新赋引用副本会“泄露”到外部量。 #### 4. **结论** Java是严格的值传递语言。这一设计确保了方法隔离性和安全性:方法内部操作会意外改变原始量(除非通过对象属性)。如果你在开发中遇到“属性被修改”的混淆,记住它源于副本共享对象,而非Java支持引用传递。 ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值