java是值传递还是引用传递

简介: 所谓值传递就是一个参数被传进方法中修改,却不影响原始值,为什么原始值不受影响呢?因为jvm是将原始值复制了一份传递出去的,所以叫值传递(这句话很重要,这决定了对象数据类型到底是值传递正确,还是引用传递正确)。

一.值传递?



所谓值传递就是一个参数被传进方法中修改,却不影响原始值,为什么原始值不受影响呢?因为jvm是将原始值复制了一份传递出去的,所以叫值传递(这句话很重要,这决定了对象数据类型到底是值传递正确,还是引用传递正确)。


二.引用传递?



引用传递就是一个参数被传进方法中修改,会影响到原始的值,传递的是原始值得引用。


这便是值传递与引用传递最直观的展现,这里要特别注意的是,引用传递是你传递到方法里的值如果改变,你原始数据是必须要变得,这才能叫做引用传递(这句话也非常重要,手动敲黑板,请记住)。


三.那么java中到底是值传递?还是引用传递?



3.1有人认为基本数据类型是值传递,对象类型是引用传递


基本数据类型,应该没神马争议,是值传递。


 public static void main(String args[]){
       // testList();
       // testNull();
        test1();
    }
    //int
    public static void test1(){
        int a = 1;
        changeInt(a);
        System.out.println(a);
    }
    public static void changeInt(int a){
        a = 2;
    }


这个输出应该没有神马争议,输出是1,所以基本数据类型是值传递大家一致认可。


F:\java\bin\java.exe...
1
Process finished with exit code 0


那引用数据类型呢?举个栗子


public static void main(String args[]){
       // testList();
       // testNull();
       // test1();
        test2();
    }
    //list
    public static void test2(){
        List list = new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        changeList(list);
        list.forEach(str ->{
            System.out.println(str);
        });
    }
    public static void changeList(List list){
        list.add("d");
    }


这个输出大部分人应该也没有争议,list的值应该是a,b,c,d


F:\java\bin\java.exe...
a
b
c
d
Process finished with exit code 0


到这里大部分就认为对象类型是引用传递,因为原始值改变了传递的自然是引用了,如果你也这么认为,那我要告诉你,兄弟关于这个问题(对象类型是引用还是值传递)只验证到这,其实只是验证了一半,这一半验证成功是因为jvm将引用数据类型的地址引用放在了栈里,看过一些解释,都是验证到这里就没有下文了,如果我要不是碰到问题,下面的我也没想起来(插一句,验证对象类型不要使用final修饰的类,比如String,当你改变他时,其实是在创建一个新的对象,看起来和基本数据类型类似)。


3.2 java其实只有值传递


public static void main(String args[]){
       // testList();
       // testNull();
       // test1();
        test2();
    }
    //list
    public static void test2(){
        List list = new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        changeList(list);
        list.forEach(str ->{
            System.out.println(str);
        });
    }
    public static void changeList(List list){
        list.add("d");
        list = null;
    }


这块代码和3.1中的第二段代码只是在changList方法中加了一句:list = null;,加了这一句,大家不妨想一想会输出什么?空指针异常?还是a,b,c,d?其实是a,b,c,d,这里不展示结果,给不信得人去试试的机会。如果是引用传递,那么我将引用传递进来,输出结果也应该是空指针异常,因为我将引用置空了,不是吗。到这里有些人可能会有这样的疑问:在changList方法中为list增加一个元素,原始的list会增加一个元素,但是在changList方法中将list置空,原始的list却不变?有点懵?突然感觉还涉及到玄学了呢,其实这是因为java是值传递所以才有这种现象。在1和2中画的重点还记得吗,如果不记得可以往上看下,值传递是复制了一个值传递出去并不会影响到原始值,而引用传递才会影响到原始值。


下面说下为什么是值传递:


(**终于说到重点,前面都是铺垫 **)

java中方法传参其实无论是基本类型还是对象类型,都是先将他们在栈空间的地址复制一份然后传入进入方法中(引用传递是不会复制值进行传递的),方法体其实操作的都是这个复制的栈空间地址。所以才有这种现象:操作一个对象类型你改变他的值,原始值也会变。因为复制了的栈空间地址和原始的栈空间地址指向同一个堆内存。也有了这种现象:在方法中将传入的对象参数置空,但是原始的对象类型却并不影响,因为你置空的是是和原始引用相同的复制体,说白了。你改变得只是克隆体,所以真正的引用你改变不了。说到这里应该是明白为什么说java只有值传递了,对象类型中,你改变的其实也是一个复制体,和基本数据类型一样,你操作的都是一个原始值复制出来的值(引用传递并不会复制值传递出去,而是传递一个引用),并不是这个对象的真正的引用,所以对象类型并不是引用传递,而是值传递


3.4说个关于项目中碰到的值传递与引用传递的问题(好像是我们架构师的代码)


3.4.1问题描述


将一个list放入到返回对象中,且方法return之前调用了list的clear方法。代码如下


public ResultDTO querySystemDicListByDicCode(String dicCode) {
        ResultDTO resultDTO;
        SupplierSystemDic supplierSystemDic;
        //调用数据中心查询服务
        QueryPacker queryPacker = QueryPacker.create(TableConstant.TABLE_SYSTEM_DIC);
        queryPacker.where(Criteria.field("dic_code").eq(dicCode));
        ResponseModel<PageResult> responseModel = queryPersistenceCoreDataService.queryData(CommonConstant.APPKEY, queryPacker, CommonConstant.SOURCE);
        if (responseModel.isSuccess()) {
            resultDTO = new ResultDTO(ExceptionEnum.COMMON_SUCCESS_30.getResultCode());
            List<Map<String, Object>> mapList = responseModel.getData().getResultList();
            if (mapList != null && mapList.size() > 0) {
                resultDTO.setData(mapList);
                //清空数据
                mapList.clear();
                responseModel.getData().getResultList().clear();
                supplierSystemDic = null;
            } else {
                if (resultDTO != null) {
                    resultDTO.setData(null);
                }
            }
        } else {
            log.error("方法querySystemDicByDicCod,字典代码=" + dicCode + "查询字典表数据异常,异常原因:" + responseModel.getMessage());
            resultDTO = new ResultDTO(ExceptionEnum.RPC_30.getResultCode());
        }
        //清空数据
        responseModel = null;
        queryPacker.clearColumns();
        queryPacker = null;
        return resultDTO;
    }


如果3.4之前内容都理解了,那么这里如果不用maplist.clear();而用maplist=null;他俩的区别肯定也清楚了,如果用的是maplist=null;则置空的是maplist的一个引用,而不是真正的maplist,而resultDTO里面存储的maplist依然是存在的。故使用maplist=null;不会有任何问题。


但是使用maplist.clear();则不一样,这个方法在ArrayList中的实现是这样的


public void clear() {
        modCount++;
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }


ArrayList底层是一个数据,elementData便是那个数组,这里操作不再是集合的引用,而是集合里的单个元素,并把size置为0;实际上这个集合在堆中便不存在了,所以resultDTO中设置的(复制的栈空间引用与真实的引用指向同一个堆内存)maplist也不会存在了,所以这个返回值resutDTO中无论什么时候date这个属性都不会有值。


结尾语:如果有不同意见,欢迎评论共同探讨。


相关文章
|
存储 Java 程序员
Java中相等比较与值传递
Java中相等比较与值传递
73 0
|
4月前
|
Java
java是值传递还是引用传递
本文澄清了Java中参数传递的常见误解,总结出Java采用“值传递”的方式。对于基本类型,传递其值的拷贝,方法内修改不影响原值;而对于对象类型,则传递其引用地址的拷贝,尽管是拷贝,但因指向同一对象,故方法内的修改会影响原对象状态。形参仅在方法内部有效,而实参则是调用方法时传递的具体值。通过示例和比喻(如复刻仓库钥匙),形象地解释了值传递、引用传递及Java特有的“共享对象传递”概念,帮助理解不同情况下参数传递的行为差异。
|
4月前
|
Java
java中的值传递和引用传递
【8月更文挑战第3天】在Java中,值传递用于基本数据类型,传递的是值的副本,因此方法内的修改不影响原值;而引用传递用于对象和数组,虽传递的是引用的副本,但对对象内容的修改会影响原始对象。理解这两者对于正确处理方法调用及参数至关重要。
|
4月前
|
Java
java中的值传递和引用传递
【8月更文挑战第2天】在Java中,基本数据类型如`int`、`double`等采用值传递,传递的是变量值的副本,因此方法内的修改不影响原变量。对象类型则通过引用传递,传递的是对象引用的副本,允许方法内修改原对象。例如,对`StringBuilder`对象的修改会影响原始对象。
|
7月前
|
存储 安全 Java
Java方法的值传递技术详解
Java方法的值传递技术详解
43 3
|
6月前
|
Java
Java的值传递与“引用传递”辨析
Java的值传递与“引用传递”辨析
30 0
|
7月前
|
JavaScript 前端开发 Java
【JAVA面试题】什么是引用传递?什么是值传递?
【JAVA面试题】什么是引用传递?什么是值传递?
|
7月前
|
Java
每日一道Java面试题:Java是值传递还是引用传递?
每日一道Java面试题:Java是值传递还是引用传递?
37 1
|
7月前
|
存储 Java
如何理解Java是按值传递
如何理解Java是按值传递
|
Java
JAVA参数传值机制中值传递和引用传递
JAVA参数传值机制中值传递和引用传递
98 0