java是值传递还是引用传递?
最近在项目中发现了类似如下的代码。
import com.alibaba.fastjson.JSONObject;
public class ValuePassParamTest {
public static void main(String[] args) {
JSONObject paramJson=new JSONObject();
paramJson.put("date","1993_12_22");
addJson(paramJson);
System.out.println("paramJson:"+paramJson.toString());
}
public static JSONObject addJson(JSONObject paramJson){
String value=paramJson.getString("date");
if(value!=null && value.contains("_")) {
value=value.replace("_","");
}
paramJson.put("date",value);
return paramJson;
}
}
修改新对象,影响了旧对象
这段代码期望原来的paramJson不改变,只改变方法参数中的paramJson。
结果却发现对形参paramJson进行替换操作,实参也跟着改变了。
除了这种传参数,类似的还有赋值。
JSONObject paramJson=new JSONObject();
paramJson.put("date","1993_12_12");
JSONObject paramJson2=paramJson;
paramJson2.put("name","lin");
同样发现,修改了paramJson2,会影响paramJson。
那么,为什么对这些非基本类型的对象进行赋值或传参,修改新对象,会影响旧对象?
值传递
java中方法参数传递方式(以及赋值)是按值传递的。
如果参数是基本类型(以及String类型),传递的是基本类型(以及String)的变量值。
如果参数是引用类型,传递的是该参数所引用的对象在堆中地址值。
值,就是指存储实际内容的内存块。
引用,就是指向带有存储值内存块的变量,自身不存储实际值。
基本类型和引用类型
Java 将内存空间分为堆和栈。
基本类型直接在栈中存储数值,而引用类型是将变量放在栈中,实际存储的值是放在堆中,通过栈中的变量指向堆中存放的数据。
JSONObject不是基本类型,而是引用类型。
原来的paramJson指向了某块内存,而方法参数中的paramJson也指向了相同的内存。
当内存块中存储的值改变时,任何指向内存块的引用都会随着改变。
示例
1.方法参数(形参)为基本类型以及String,如下。
public class ParamTransfer {
public void changeValue(int param){
param = param + 1;
System.out.println("方法内的值:"+param);
}
public static void main(String[] args) {
int value = 1;
ParamTransfer pt = new ParamTransfer();
pt.changeValue(value);
System.out.println("main方法的值:"+value);
}
}
运行结果为:
方法内的值:2
main方法的值:1
结论:
方法参数(形参)为基本类型以及String,方法参数改变时,不会改变原来的实参。
2.方法参数(形参)为引用类型,如下:
public class ParamTransfer{
public void changeValue(StringBuffer param) {
param = param.append("World!");
System.out.println("方法内的值:"+param);
}
public static void main(String[] args) {
ParamTransfer pt = new ParamTransfer();
StringBuffer value = new StringBuffer("Hello ");
pt.changeValue(value);
System.out.println("main方法的值:"+value);
}
}
运行结果为:
方法内的值:Hello World!
main方法的值:Hello World!
结论:
方法参数(形参)为引用类型,方法参法改变时,原来的实参随之改变。
因为方法参数和原来的实参指向同一内存块。
当内存块中存储的值改变时,任何指向内存块的引用都会随着改变。
3.方法参数(形参)为引用类型,新建对象,方法参数改变,不会改变原来的参数。
public class ParamTransfer{
public void changeValue(StringBuffer param) {
StringBuffer sb = new StringBuffer("Hi ");
param = sb;
param.append("World!");
System.out.println("方法内的值:"+param);
}
public static void main(String[] args) {
ParamTransfer pt = new ParamTransfer();
StringBuffer value = new StringBuffer("Hello ");
pt.changeValue(value);
System.out.println("main方法的值:"+value);
}
}
运行结果如下:
方法内的值:Hi World!
main方法的值:Hello
结论:
方法参数(形参)为引用类型,新建对象,开辟新的内存块,将新对象赋值给形参,那么形参和实参指向的就是不同的内存块,方法参数改变,不会改变原来的实参。
4.新建对象,并赋值为原来的值,如下:
public class ParamTransfer{
public void changeValue(StringBuffer param) {
StringBuffer sb = new StringBuffer("Hi");
sb=param;
sb.append("World");
System.out.println("方法内的值:"+param);
}
public static void main(String[] args) {
ParamTransfer pt = new ParamTransfer();
StringBuffer value = new StringBuffer("Hello");
pt.changeValue(value);
System.out.println("main方法的值:"+value);
}
}
运行结果:
方法内的值:HelloWorld
main方法的值:HelloWorld
结论:
方法参数(形参)为引用类型,新建对象,开辟新的内存块,那么新对象和实参指向的就是不同的内存块。
将形参赋值给新对象,赋值之后,新对象和实参又指向同一个内存块。
由于指向同一个内存块,新对象改变,实参也随之改变。
参考资料:
https://www.zhihu.com/question/31203609 (知乎的这个"值传递和引用传递"的回答,强烈推荐,值得思考)
https://blog.csdn.net/newmoons/article/details/51512481
https://blog.csdn.net/u010469514/article/details/80838678 (String到底是值传递还是引用传递?)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2017-07-03 BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource