Java参数传递分析

简介: 本文详细探讨了Java中参数传递的机制,明确指出Java采用的是值传递而非引用传递。通过基本数据类型(如int)和引用类型(如Map、自定义对象People)的实例测试,证明方法内部对参数的修改不会影响原始变量。即使在涉及赋值返回的操作中,表面上看似引用传递,实际仍是值传递的结果。文中结合代码示例与执行结果,深入解析了值传递的本质及容易引起混淆的情形,帮助读者准确理解Java参数传递的核心概念。

传递类型

按值传递:就是把原来的参数的值,拷贝一份,将这个拷贝的值修改,而原来的旧的值不会修改。 引用传递:就是将参数的地址拿来使用,不去拷贝,直接在这个地址上操作,这样的话,就会把原来的值也修改了。

Java参数传递是值传递还是引用传递?

当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递? 答:是值传递。Java 语言的参数传递只有值传递。当一个实例对象作为参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法内改变,但对象的引用(不是引用的副本) 是永远不会改变的。

值传递的证明

如果是引用传递,那么我们修改操作的时候,直接是对地址进行操作,意味着我们的操作,都是直接对这个地址上的内容进行修改,所以这个修改是必然生效的,而如果是值传递,那么意味着,我传过去的参数,只是一个值,我们知道,一个对象是分配在内存地址上的,而如果只是传了一个参数的话,这就意味着,我们的参数原先的位置等信息,没有传递过去,而只是传了一个拷贝的值,这个拷贝的值,是在一个新的地址上,我们对一个对象进行更新操作,这必然意味着我们直接对这个地址上所承载和分配的内容进行了修改,而如果只是值传递,那么就是将我们的参数的值,进行了一个拷贝,而对原来的值,没有改动,没有改动原来地址上的内容。所以按照这个而言,值传递,不会修改参数的原来的内容。那么是不是这样呢?

体验AI代码助手

代码解读

复制代码

/**
 * @Author zgy
 * @Date 2020/6/15
 * @Description 总的结论: Java是按照值传递的,而不是按照引用,
 * 按值传递:  就是把原来的参数的值,拷贝一份,将这个拷贝的值修改,而原来的旧的值不会修改
 * 引用传递:就是将参数的地址拿来使用,不去拷贝,直接在这个地址上操作,这样的话,就会把原来的值也修改了
 */
public class TransferParam {

    public static void main(String[] args) {
        // 基本类型的值传递
        System.out.println("===========基本类型的值传递============");
        int age = 22;
        System.out.println("调用changeData(int data)方法前: " + age);
        changeDataBasic(age);
        System.out.println("调用changeData(int data)方法后: " + age + "\n");

        // 基本类型的值传递, String
        System.out.println("===========基本类型的值传递:String============");
        String name = "张三";
        System.out.println("调用changeString(String str)方法前: " + name);
        changeString(name);
        System.out.println("调用changeString(String str)方法后: " + name + "\n\n");

        // 对象类型的值传递
        System.out.println("+++++++++++对象类型的值传递++++++++++++++");
        Map<String, String > map = new HashMap<>();
        map.put("name", "李小龙");
        System.out.println("调用changeDataObject(Map<String, String> map)方法前: " + map);
        changeDataObject(map);
        System.out.println("调用changeDataObject(Map<String, String> map)方法后: " + map + "\n");

        // 对象类型的值传递, 普通的对象
        System.out.println("+++++++++++对象类型的值传递:People++++++++++++++");
        Map<String, String> info = new HashMap<>();
        info.put("address", "北京朝阳");
        info.put("home", "四川成都");
        People people = People.builder().age(22).name("lee").info(info).build();
        System.out.println("调用changePeople(People people)方法前: " + people);
        changePeople(people);
        System.out.println("调用changePeople(People people)方法后: " + people);


    }

    public static void changeDataBasic(int data) {
        data = 100;
        System.out.println("方法中: " + data);
    }

    public static void changeDataObject(Map<String, String> map){
        Map<String, String > testMap = new HashMap<>();
        testMap.put("name", "zhangsan");
        map = testMap;
        System.out.println("方法中: " + map);
    }

    public static void changeString(String str){
        str ="Hello world!";
        System.out.println("方法中: " + str);
    }

    public static void changePeople(People people){
        Map<String, String> info2 = new HashMap<>();
        info2.put("address", "北京");
        info2.put("home", "四川雅安");
        People people2 = People.builder().age(22).name("lee").info(info2).build();
        people = people2;
        System.out.println("方法中: " + people2);
    }
}

@Data
@Builder
class People {
    private String name;
    private Integer age;
    private Map<String, String> info;
}

我们分别对普通的基本类型,String对象,Map对象和自定义的People对象,进行了测试,都证明了是值的传递,而非引用传递,输出的结果如下:

体验AI代码助手

代码解读

复制代码

===========基本类型的值传递============
调用changeData(int data)方法前: 22
方法中: 100
调用changeData(int data)方法后: 22

===========基本类型的值传递:String============
调用changeString(String str)方法前: 张三
方法中: Hello world!
调用changeString(String str)方法后: 张三


+++++++++++对象类型的值传递++++++++++++++
调用changeDataObject(Map<String, String> map)方法前: {name=李小龙}
方法中: {name=zhangsan}
调用changeDataObject(Map<String, String> map)方法后: {name=李小龙}

+++++++++++对象类型的值传递:People++++++++++++++
调用changePeople(People people)方法前: People(name=lee, age=22, info={address=北京朝阳, home=四川成都})
方法中: People(name=lee, age=22, info={address=北京, home=四川雅安})
调用changePeople(People people)方法后: People(name=lee, age=22, info={address=北京朝阳, home=四川成都})

值传递的分析

如下作为分析,在我们对基本类型操作的时候,比如如下的age=22,在调用changeDataBasic方法的前后,其实就相当于在内存中把age复制了一份,在调用之中,传递的是拷贝的一份,这拷贝的一份,我们可以在changeDataBasic方法之中,随意操作,但是它影响不了外面的age的值,因为这已经是两份对象。

体验AI代码助手

代码解读

复制代码

    public static void main(String[] args) {
        System.out.println("===========基本类型的值传递============");
        int age = 22;
        System.out.println("调用changeData(int data)方法前: " + age);
        changeDataBasic(age);
        System.out.println("调用changeData(int data)方法后: " + age + "\n");
    }

    public static void changeDataBasic(int data) {
        data = 100;
        System.out.println("方法中: " + data);
    }

等号"="的操作含义

对于基本数据类型来说 “=”赋值操作是直接改变内存地址(存储单元)上的值。 对于引用类型来说 “=” 赋值操作是改变引用变量所指向的内存地址(上文中存储单元)。

容易导致混淆的赋值

体验AI代码助手

代码解读

复制代码

/**
 * @Author zgy
 * @Date 2020/6/15
 * @Description 总的结论: Java是按照值传递的,而不是按照引用,
 * 按值传递:  就是把原来的参数的值,拷贝一份,将这个拷贝的值修改,而原来的旧的值不会修改
 * 引用传递:就是将参数的地址拿来使用,不去拷贝,直接在这个地址上操作,这样的话,就会把原来的值也修改了
 * <p>
 * 虽然值传递和引用传递尘埃落定,在Java之中只有值传递,但是有个操作很容易造成迷惑,就是赋值,以如下基本类型和Map对象来演示
 */
public class TransferParam2 {
    public static void main(String[] args) {
        // 基本类型的值传递
        System.out.println("===========基本类型的值传递============");
        int age = 22;
        System.out.println("调用changeData(int data)方法前: " + age);
        changeDataBasic(age);
        System.out.println("调用changeData(int data)方法后: " + age + "\n");

        // 对象类型的值传递
        System.out.println("===========对象类型的值传递============");
        Map<String, String> map = new HashMap<>();
        map.put("name", "李小龙");
        System.out.println("调用changeDataObject(Map<String, String> map)方法前: " + map);
        changeDataObject(map);
        System.out.println("调用changeDataObject(Map<String, String> map)方法后: " + map + "\n\n");

        // 基本类型的值传递, 但是赋值行为改变了作用范围
        System.out.println("++++++++++++基本类型的值传递: 返回值赋值修改了值+++++++++++++");
        int age2 = 22;
        System.out.println("调用changeDataReturn(int data)方法前: " + age2);
        // 赋值行为修改了原先的值
        age2 = changeDataBasicReturn(age);
        System.out.println("调用changeDataReturn(int data)方法后: " + age2 + "\n");

        // 对象类型的值传递, 但是赋值行为改变了作用范围
        System.out.println("++++++++++++对象类型的值传递: 返回值赋值修改了值++++++++++++++");
        Map<String, String> map2 = new HashMap<>();
        map2.put("name", "陈真");
        System.out.println("调用changeDataObjectReturn(Map<String, String> map)方法前: " + map2);
        // 赋值行为修改了原先的值
        map2 = changeDataObjectReturn(map2);
        System.out.println("调用changeDataObjectReturn(Map<String, String> map)方法后: " + map2 + "\n\n");
    }

    // 这是空方法, 没有赋值行为, 对拷贝内容的修改, 仅仅局限于方法内, 返回是void, 对外不造成影响
    public static void changeDataBasic(int data) {
        data = 100;
        System.out.println("方法中: " + data);
    }

    public static void changeDataObject(Map<String, String> map) {
        Map<String, String> testMap = new HashMap<>();
        testMap.put("name", "zhangsan");
        map = testMap;
        System.out.println("方法中: " + map);
    }

    // 该方法有返回值, 值传递中拷贝的内容, 在修改之后, 可以通过该方法进行赋值, 所以会对外造成影响
    public static int changeDataBasicReturn(int data) {
        data = 100;
        System.out.println("方法中: " + data);
        return data;
    }

    public static Map<String, String> changeDataObjectReturn(Map<String, String> map) {
        Map<String, String> testMap = new HashMap<>();
        testMap.put("name", "zhangsan");
        map = testMap;
        System.out.println("方法中: " + map);
        return map;
    }
}

输出的结果如下

体验AI代码助手

代码解读

复制代码

===========基本类型的值传递============
调用changeData(int data)方法前: 22
方法中: 100
调用changeData(int data)方法后: 22

===========对象类型的值传递============
调用changeDataObject(Map<String, String> map)方法前: {name=李小龙}
方法中: {name=zhangsan}
调用changeDataObject(Map<String, String> map)方法后: {name=李小龙}


++++++++++++基本类型的值传递: 返回值赋值修改了值+++++++++++++
调用changeDataReturn(int data)方法前: 22
方法中: 100
调用changeDataReturn(int data)方法后: 100

++++++++++++对象类型的值传递: 返回值赋值修改了值++++++++++++++
调用changeDataObjectReturn(Map<String, String> map)方法前: {name=陈真}
方法中: {name=zhangsan}
调用changeDataObjectReturn(Map<String, String> map)方法后: {name=zhangsan}

如果单纯从上述的代码之中看,那我们很可能就会认为是引用传递起了作用,但是我们要知道,引用传递起作用是发生在一个整体的同一个步骤之中,而不像此处,先传值,然后再去通过方法的返回值,去赋值,这样就是两个步骤了,这种操作,会让人一下子有点迷惑,但是其实还是很好理解的,就是说,一步到位的就一定是引用传递,而我们只需记住,Java之中只有按值传递,所有的参数传递操作,都是传递了值的拷贝,也就是值的内容,而非对象的地址。


转载来源:https://juejin.cn/post/6844904191832522765

目录
打赏
0
7
7
1
147
分享
相关文章
6个Java 工具,轻松分析定位 JVM 问题 !
本文介绍了如何使用 JDK 自带工具查看和分析 JVM 的运行情况。通过编写一段测试代码(启动 10 个死循环线程,分配大量内存),结合常用工具如 `jps`、`jinfo`、`jstat`、`jstack`、`jvisualvm` 和 `jcmd` 等,详细展示了 JVM 参数配置、内存使用、线程状态及 GC 情况的监控方法。同时指出了一些常见问题,例如参数设置错误导致的内存异常,并通过实例说明了如何排查和解决。最后附上了官方文档链接,方便进一步学习。
280 4
Java 大视界 -- Java 大数据在智能教育学习社区用户互动分析与社区活跃度提升中的应用(274)
本文系统阐述 Java 大数据技术在智能教育学习社区中的深度应用,涵盖数据采集架构、核心分析算法、活跃度提升策略及前沿技术探索,为教育数字化转型提供完整技术解决方案。
互联网大厂校招 JAVA 工程师笔试题解析及常见考点分析
本文深入解析互联网大厂校招Java工程师笔试题,涵盖基础知识(数据类型、流程控制)、面向对象编程(类与对象、继承与多态)、数据结构与算法(数组、链表、排序算法)、异常处理、集合框架、Java 8+新特性(Lambda表达式、Stream API)、多线程与并发、IO与NIO、数据库操作(JDBC、ORM框架MyBatis)及Spring框架基础(IoC、DI、AOP)。通过技术方案讲解与实例演示,助你掌握核心考点,提升解题能力。
96 2
Java 大视界 -- Java 大数据在智能安防入侵检测系统中的多源数据融合与分析技术(171)
本文围绕 Java 大数据在智能安防入侵检测系统中的应用展开,剖析系统现状与挑战,阐释多源数据融合及分析技术,结合案例与代码给出实操方案,提升入侵检测效能。
|
2月前
|
【高薪程序员必看】万字长文拆解Java并发编程!(3-1):并发共享问题的解决与分析
活锁:多个线程相互影响对方退出同步代码块的条件而导致线程一直运行的情况。例如,线程1的退出条件是count=5,而线程2和线程3在其代码块中不断地是count进行自增自减的操作,导致线程1永远运行。内存一致性问题:由于JIT即时编译器对缓存的优化和指令重排等造成的内存可见性和有序性问题,可以通过synchronized,volatile,并发集合类等机制来解决。这里的线程安全是指,多个线程调用它们同一个实例的方法时,是线程安全的,但仅仅能保证当前调用的方法是线程安全的,不同方法之间是线程不安全的。
58 0
|
2月前
|
【高薪程序员必看】万字长文拆解Java并发编程!(3-2):并发共享问题的解决与分析
wait方法和notify方法都是Object类的方法:让当前获取锁的线程进入waiting状态,并进入waitlist队列:让当前获取锁的线程进入waiting状态,并进入waitlist队列,等待n秒后自动唤醒:在waitlist队列中挑一个线程唤醒:唤醒所有在waitlist队列中的线程它们都是之间协作的手段,只有拥有对象锁的线程才能调用这些方法,否则会出现IllegalMonitorStateException异常park方法和unpark方法是LockSupport类中的方法。
64 0
|
3月前
|
Java 集合框架详解:系统化分析与高级应用
本文深入解析Java集合框架,涵盖List、Set、Map等核心接口及其常见实现类,如ArrayList、HashSet、HashMap等。通过对比不同集合类型的特性与应用场景,帮助开发者选择最优方案。同时介绍Iterator迭代机制、Collections工具类及Stream API等高级功能,提升代码效率与可维护性。适合初学者与进阶开发者系统学习与实践。
104 0
列表结构与树结构转换分析与工具类封装(java版)
本文介绍了将线性列表转换为树形结构的实现方法及工具类封装。核心思路是先获取所有根节点,将其余节点作为子节点,通过递归构建每个根节点的子节点。关键在于节点需包含 `id`、`parentId` 和 `children` 三个属性。文中提供了两种封装方式:一是基于基类 `BaseTree` 的通用工具类,二是使用函数式接口实现更灵活的方式。推荐使用后者,因其避免了继承限制,更具扩展性。代码示例中使用了 Jackson 库进行 JSON 格式化输出,便于结果展示。最后总结指出,理解原理是进一步优化和封装的基础。
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
本文介绍了 Spring Boot 的核心概念和使用场景,并通过一个实战项目演示了如何构建一个简单的 RESTful API。
122 5
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问