zoukankan      html  css  js  c++  java
  • 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到底是值传递还是引用传递?)

  • 相关阅读:
    数据库模式
    数据流模式、转换、格式与操作
    桥接模式=抽象层协作关系+继承体系注入
    php 中更简洁的三元运算符 ?:
    larave5.6 将Excel文件数据导入数据库代码实例
    Laravel获取所有的数据库表及结构
    Laravel框架数据库CURD操作、连贯操作总结
    insert into 语句的三种写法
    Laravel 上传excel,读取并写入数据库 (实现自动建表、存记录值
    laravel5.4将excel表格中的信息导入到数据库中
  • 原文地址:https://www.cnblogs.com/expiator/p/11128479.html
Copyright © 2011-2022 走看看