Java内存管理-Stackoverflow问答-Java是传值还是传引用?(十一)

勿在流沙筑高台,出来混迟早要还的。

做一个积极的人

编码、改bug、提升自己

我有一个乐园,面向编程,春暖花开!

本文导图:

一、由一个提问引发的思考

Stack Overflow 看到这样一个问题:

Is Java “pass-by-reference” or “pass-by-value”?

翻译成中文:

Java是传值还是传引用?

请先不要看下面的内容,思考10秒后,在继续阅读!!!

为什么建议先思考,在阅读内容呢?

我们每天可能会利用碎片化的时间阅读很多内容,有很多信息和知识其实在大脑过一下,然后就忘记了!如何才能高效的利用碎片化时间掌握或者记住更多的内容和知识,我自己碎片化阅读的理解和技巧:阅读一篇自己感兴趣技术文章,在时间允许的时间下,一定是一次性阅读完,在阅读中带着自己的问题,阅读后有自己的简单总结。 千万不要阅读 一段内容,看到微信有人发消息给你,就切换聊天框回复消息,然后回复完再切换回来阅读技术文章。这种 大脑 的切换是需要耗费资源的,也影响阅读的效果和效率(大脑在多个任务切换类似cpu多线程调度,线程的频繁切换。就如多线程不一定能提供效率,频繁的线程/任务切换耗费cpu大量资源)!

扯远 了,切换回到本文正题,Java是传值还是传引用?

我相信你阅读完本篇后一定能够回答上面的问题,并且在工作在写类似传参的代码也会有更深入的理解。开启探索之旅,Let‘s go!

二、为什么有传值还是传引用的说法

在Java程序中会包含方法,方法会分为方法声明和方法实现,在方法声明中又有参数列表,参数根据调用后的效果不同,也就是是否改变参数的原始值,可以划分为两种: 按值传递参数和按引用传递参数。

  • 按值传递参数 == 传值
  • 按引用传递参数 == 传引用

也就是之前介绍过的Java的基本类型和引用类型,如果方法参数中传递的基本类型就认为是 按值传递(传值),方法参数中传递的是引用类型,就称之为按引用传递(传引用)。

三、图解传值和传引用过程

一段简单的代码:

public class PrettyGirl {
    /**
     * 芳龄几何
     */
    int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {

        // 引用类型
        PrettyGirl prettyGirl = new PrettyGirl();
        prettyGirl.setAge(28);
        // 基本类型
        int num = 50;
        // 数组arrs也引用类型
        int[] arrs = new int[]{2,0,1,9};

        System.out.println("mian 中 num  = " + num);
        System.out.println("mian 中 arrs[3] = " + arrs[3]);
        System.out.println("mian 中 prettyGirl.getAge() = " + prettyGirl.getAge());
        System.out.println("-----------------------------------------");

        // 调用 change方法
        prettyGirl.change(num, arrs, prettyGirl);

        System.out.println("调用change 后 mian 中 num  = " + num);
        System.out.println("调用change 后 mian 中 arrs[3] = " + arrs[3]);
        System.out.println("调用change 后 mian 中 prettyGirl.getAge() = " + prettyGirl.getAge());

    }

    public void change(int pnum, int[] parrs, PrettyGirl ppg) {
        //在change中 改变值类型pnum的值
        pnum = pnum + 50;
        //在change中 改变引用类型 parrs,ppg 的值
        parrs[3] = 8;
        // 从28变18
        ppg.setAge(18);

        System.out.println("change 中 pnum  = " + pnum);
        System.out.println("change 中 parrs[3] = " + parrs[3]);
        System.out.println("change 中 ppg.getAge() = " + ppg.getAge());
        System.out.println("-----------------------------------------");
    }

}

思考一下,打印的结果是什么?



打印结果如下

mian 中 num  = 50
mian 中 arrs[3] = 9
mian 中 prettyGirl.getAge() = 28
-----------------------------------------
change 中 pnum  = 100
change 中 parrs[3] = 8
change 中 ppg.getAge() = 18
-----------------------------------------
调用change 后 mian 中 num  = 50
调用change 后 mian 中 arrs[3] = 8
调用change 后 mian 中 prettyGirl.getAge() = 18

下面开启分析之旅,结合之前学过的Java内存模型来画上面代码执行的内存变化的图

注:下图只是为了演示讲解说明,真实内存地址不一定是这样!

int 类型变量num在栈中分配一块内存,而 parrs 与 ppg 分配两块内存,栈中一块,堆中一块。当调用change方法时,创建三个变量 pnum,parrs,ppg这里相当于把栈中的数据全备份一份给这三个数值,则有

在change方法中对传递的参数进行修改,此时pnum的值修改为 100,堆中ppg指向的对象年龄由28改为18,数组中parrs[3]修改为8。也就是 ppg与 parrs 改变了堆中的具体数值,而 pnum 改变的只是栈中的数值。

最后,当change方法调用结束,change栈帧被弹出,则对应的pnum,ppg,parrs 三个变量也消亡,此时只有main栈帧情况如下图:

通过上图的演示,上面代码的打印结果就很清晰明了了。



tips:回顾 java 栈

Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此可知,线程当前执行的方法所对应的栈帧必定位于Java栈的顶部。

根据上面例子中这样的内存变换,想必你应该知道按值传递与按引用传递的深层原因了吧!

从上图中看所有的参数传递 本质都是按址值传递, 也就是内存地址的值 。 基本类型因为栈内存地址中保存就是其本身值,所有在参数传递的时候,拷贝本身的值进行传递,而引用类型在栈内存地址中保存的是引用的值,通过栈内存保存引用的值指向堆中获取对象真是的值,在参数传递的时候,拷贝的是引用的值。

所有在方法传递参数后,如果基本类型的值在传递的方法中有修改,不会影响传递前方法中的值。而引用类型就不同了,因为它修改的是引用地址指向堆中数据,这部分数据在参数传递的时候不会拷贝一份,就如上面图解标识出的一样。

四、本文总结

在Java中,对象通过引用传递,基本类型按值传递。

这句话的描述有一半是不准确的。就如上面我们图中看到的那样基本类型是按照值传递的; 引用类型是拷贝引用的址值传递的,也即对象通过引用传递。正确的描述语句是对象引用也是按值传递

其实在Java语言规范(JLS)中描述:Java 严格按值传递,可以理解与C完全相同,也就是Java中参数传递的本质是按址值传递。

如果你在阅读完本篇后,对上面问题有自己的深入的理解,有欢迎文末留言一起探讨!

参考文章

Is Java “pass-by-reference” or “pass-by-value”?

Java is Pass-by-Value, Dammit!



谢谢你的阅读,如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到!祝你每天开心愉快!



不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页 : http://blog.csdn.net/u010648555

愿你我在人生的路上能都变成最好的自己,能够成为一个独挡一面的人

© 每天都在变得更好的阿飞云

原文地址:https://www.cnblogs.com/aflyun/p/10702006.html

时间: 2024-10-22 00:04:41

Java内存管理-Stackoverflow问答-Java是传值还是传引用?(十一)的相关文章

Java内存管理:深入Java内存区域

Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C和C++程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权力的皇帝,又是从事最基础工作的劳动人民—既拥有每 一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任. 对于Java程序员来说,在虚拟机的自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,而且不容易出现 内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切

java内存管理机制

JAVA 内存管理总结 1. java是如何管理内存的 Java的内存管理就是对象的分配和释放问题.(两部分) 分配 :内存的分配是由程序完成的,程序员需要通过关键字new 为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间.释放 :对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作.但同时,它也加重了JVM的工作.因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请.引用.被引用.赋值等,GC都需要进行监控. 2. 

java内存管理

一.jvm内存结构 程序计数器(Program Counter Register).JVM虚拟机栈(JVM Stacks).本地方法栈(Native Method Stacks).堆(Heap).方法区(Method Area) (1)PCR 跟随线程生命周期,记录当前执行到的.class字节码行数,用于多线程操作 (2)JVM Stacks 跟随线程生命周期,在方法执行中存储数据 (3)Native Method Stacks 处理native方法,如object中的hashCodes()等

简单的例子 关于Java内存管理的讲解

我想做的是,逐行读取文件,然后用该行的电影名去获取电影信息.因为源文件较大,readlines()不能完全读取所有电影名,所以我们逐行读取. 就这段代码,我想要在位置二处使用base64,然后结果呢? 两处位置都打印了,位置一得到base64,ok,没问题.当我在位置二想使用base64时,问题来了?onload队列的问题,位置二总是无法正确的获取到想要的base64,这个时候就可以考虑异步问题了. 在还没有接触到angular的时候,还真的不知道它到底有什么作用,直到我开始学习它,并且运用到它

【Java】Java内存管理

Java内存管理是面试中经常会问到的问题.Java的内存管理其实是指对象 的分配和释放问题.曾经看过这样一句话:"C++程序员觉得内存管理太重要了,所以一定要自己进行管理,而Java程序员觉得内存管理太重要了,一定不能自己管理".我觉得这句话说得太精辟了. C++程序员需要显式分配内存,释放内存,而这样常常会引起"内存泄露".而Java程序员不需要显式分配和释放内存,Java在创建对象的时候会自动分配内存,在对象不再使用的时候释放内存.Java的对象是通过new关键

java内存管理的分析

java 中的内存分为四个部分: stack(栈):存放基本类型的数据和对象的引用,即存放局部变量. Note: 如果存放的是基本类型数据(非静态变量),则直接将变量名和值存入stack中. 如果存放的是引用类型,则将变量名存入栈,然后指向它new出的对象(存放在堆中). heap(堆)存放 new 出来的东西. data segment(数据区):分为静态区和常量区(常量池) 静态区(static segment): 存放在对象中用 static 定义的静态成员(即静态变量,如果该静态变量是基

Java内存管理的9个小技巧

Java内存管理的9个小技巧很多人都说“Java完了,只等着衰亡吧!”,为什么呢?最简单的的例子就是Java做的系统时非常占内存!一听到这样的话,一定会有不少人站出来为Java辩护,并举出一堆的性能测试报告来证明这一点.其实从理论上来讲Java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么多理由来证明它确实占内存呢?两个字,陋习. 1.别用new Boolean(). 在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolea

Java内存管理第二篇 - 内存的分配

Java内存管理无非就是对内存进行分配和释放.对于分配来说,基本类型和对象的引用存储到栈中,常量存储到常量池中,对象存储到堆上,这是一般的分配.而对于回收来说要复杂的多,如果回收不好,还可能造成分配出去的内存得不到回收而造成内存泄漏. 这一篇将简单介绍一下Java内存的分配,下一篇将介绍内存的回收及内存泄漏等知识. 1.JVM内存模型 1.程序计数器(Program Counter Register): 程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是

java内存管理浅析

首先感谢强大的网络资源,本博文是根据网络上的各种资源进行整合,然后加入自己的理解而成,可能会与其它网络资源有重复,望其他作者多多包涵.由于初学java,如有不准确的描述还请读者指正.下面正式切入正题: 众所周知,java和C++都是面向对象的编程语言,但是与C++相比,java上手比较容易,而且使用方便.小弟对c++了解不是很多,但是有一点是C++初学者最为头痛的问题,那就是内存管理,这也正是C++和java之间很大的一个区别.在C++中,内存是依靠程序员自己来管理的,编写程序过程中稍有不慎就会