首页 > 系统相关 >从内存层面分析Java 参数传递机制

从内存层面分析Java 参数传递机制

时间:2024-09-06 12:50:23浏览次数:7  
标签:Java String 对象 modify System hashCode 参数传递 内存 方法

在 Java 中,理解参数传递机制对于编写高效和可维护的代码至关重要。本文将探讨基本数据类型和引用数据类型的参数传递方式,并介绍 System.identityHashCode 方法及其作用。我们将结合栈帧的概念,通过示例代码来详细解释这些机制。

System.identityHashCode 的作用

System.identityHashCode 方法用于获取对象的哈希码,这个哈希码是基于对象的内存地址的。它可以帮助我们判断两个变量是否指向同一个对象。这个哈希码与 Object 类中的 hashCode 方法不同,后者可能被重写,而 identityHashCode 保证基于对象的实际内存地址。

栈帧的概念与参数传递

⚠️:先普及一下基本知识:一个线程对应一个栈,一个栈包含多个栈帧,一个栈帧对应一个方法。

每当一个方法被调用时,Java 虚拟机(JVM)会为该方法创建一个新的栈帧。栈帧用于存储方法的局部变量、操作数栈和其他执行上下文信息。局部变量表包含了方法中的所有参数和局部变量。每个方法调用都有自己的栈帧,这些栈帧在方法调用结束后会被弹出。

基本数据类型的参数传递

对于基本数据类型(如 int, float, boolean 等),Java 使用 值传递 的方式。这意味着方法接收的是参数值的副本,而不是原始变量的引用。修改副本不会影响原始变量。

示例代码
public class Main {
    public static void main(String[] args) {
        int a = 1;
        int hashCode = System.identityHashCode(a);
        System.out.println("a: " + hashCode); // a:1456208737
        modify(a);
        System.out.println("a: " + hashCode); // a:1456208737
    }

    public static void modify(int b) {
        b = 2;
        int hashCode = System.identityHashCode(b); 
        System.out.println("b: " + hashCode); // b:288665596
    }
}
解释
  1. 方法调用过程

    • main 方法中的栈帧包含了局部变量 a,其值为 1,哈希码为 1456208737
    • 当调用 modify 方法时,a 的值 1 被复制到 modify 方法的栈帧中的局部变量 b
  2. modify 方法中的操作

    • modify 方法的栈帧中,b 的值为 1。将 b 的值修改为 2,并将其哈希码输出为 288665596
    • 由于 bmain 方法栈帧中 a 值的副本,修改 b 不会影响 a 的值。
  3. 栈帧作用

    • main 方法和 modify 方法各自拥有独立的栈帧,局部变量表中的 ab 是不同的变量,修改 b 不会影响 a

引用数据类型的参数传递

对于引用数据类型(如 Person 对象),Java 使用也是值传递(只不过传递的值是引用数据类型的内存地址)的方式。这意味着方法接收的是对象引用的副本,而不是对象的实际值。修改对象内容会影响所有引用该对象的变量,但修改引用本身不会影响原始引用。

示例代码
public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("mike");
        int hashCode = System.identityHashCode(p1); 
        modify(p1);
        System.out.println("p1: " + hashCode); // p1:1456208737
    }

    public static void modify(Person p2) {
        p2.name = "lucy";
        int hashCode = System.identityHashCode(p2); 
        System.out.println("p2: " + hashCode); // p2:1456208737
    }

    static class Person {
        String name;

        public Person(String name) {
            this.name = name;
        }
    }
}
解释
  1. 方法调用过程

    • main 方法中的栈帧包含了局部变量 p1,它是指向 Person 对象的引用,其哈希码为 1456208737
    • 当调用 modify 方法时,p1 的引用被复制到 modify 方法的栈帧中的局部变量 p2
  2. modify 方法中的操作

    • modify 方法的栈帧中,此时p2 也是指向同一个 Person 对象的引用。修改 p2.name 会直接影响到 p1 对象,因为它们指向同一个内存地址。
    • p2 的哈希码与 p1 相同,因为它们是同一个对象的引用。
  3. 栈帧作用

    • main 方法和 modify 方法各自拥有独立的栈帧,但 p1p2 指向同一个内存地址,即同一个 Person 对象,修改 p2 的内容会影响 p1

String 类型的特殊情况

String 类型是一个引用数据类型,但它是 不可变的。这意味着每次对 String 对象进行修改时,实际上会创建一个新的 String 对象,而不会改变原始对象。

示例代码
public class Main {
    public static void main(String[] args) {
        String a = "str1";
        int hashCode = System.identityHashCode(a); 
        modify(a);
        System.out.println("a: " + hashCode); // a:1456208737
    }

    public static void modify(String b) {
        b = "str2";
        int hashCode = System.identityHashCode(b); 
        System.out.println("b: " + hashCode); // b:288665596
    }
}
解释
  1. 方法调用过程

    • main 方法中的栈帧包含了局部变量 a,它指向 String 对象 "str1",其哈希码为 1456208737
    • 当调用 modify 方法时,a 的引用(即 "str1" 的地址)被复制到 modify 方法的栈帧中的局部变量 b
  2. modify 方法中的操作

    • modify 方法中,将 b 赋值为 "str2"。按理来说操作的也是同一个内存地址,也会将该内存地址中的值修改为 "str2"。但是Java规定了 String 是不可变的,这将会在常量池创建一个新的 String 对象 "str2",并将 b 指向这个新的对象。原始的 String 对象 "str1" 仍然存在,因此a 的哈希码没有改变。
  3. 栈帧作用

    • main 方法和 modify 方法各自拥有独立的栈帧。修改 b 的引用不会影响 a,因为 String 对象的不可变性确保了原始对象不变。

总结

  • 基本数据类型 的参数传递是值传递,修改方法内部的值不会影响外部变量,因为每个方法调用都有自己的栈帧。
  • 引用数据类型 的参数传递是引用传递,修改方法内部的对象内容会影响外部变量,因为它们指向同一个对象。栈帧中的引用指向相同的对象内存地址。
  • String 类型 是不可变的,对 String 对象的修改会创建一个新的对象,原始对象保持不变,这与对象的可变性相关。

标签:Java,String,对象,modify,System,hashCode,参数传递,内存,方法
From: https://blog.csdn.net/weixin_54574094/article/details/141956355

相关文章

  • C语言之动态内存分配与释放
    C语言之动态内存分配与释放通用指针类型void通用类型指针具有以下特点:类型无关,赋值灵活:由于指针本质上是一个存储内存地址的变量,而内存地址是没有类型的,所以void指针可以存储任意类型数据的地址,指向任意类型对象。无论是整数、浮点数、字符或数组、结构体等类型都可以用void指......
  • SSH框架整合实现Java三层架构实例(一)
    HTML前台发送请求代码:1<tr>2<td>选择收派时间</td>3<td>4<inputtype="text"name="takeTimeId"class="easyui-combobox"required="true"5data-options="url:'../........
  • Java静态代码块、构造代码块执行顺序问题
    packagecom.zxl.staticdemo;publicclassBlockTest{static{System.out.println("BlockTest静态代码块执行");}{System.out.println("BlockTest构造代码块执行");}publicBlockTest(){System.out.......
  • Redis 解决内存满了方法
    1.增加内存2.使用内存淘汰策略,也就是在配置文件中设置maxmemory参数,该参数控制最大可用内存(字节),当内存上线超过该参数时,maxmemory-policy规则描述noeviction默认参数,不删除键,只返回错误volatile-lru使用LRU算法删除一个设置了生存时间的键allkeys-lru使用LRU算法......
  • [Java基础]hashcode/equals
    hashcode()/equals()/====当==左右两边是基本类型的时候,比较的是数值是否相等;当==左右两边是对象(引用)类型的时候,比较的是p和p2这两个对象所指向的堆中的对象地址对于==来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是......
  • Java中实现对象和Map之间的转换
    在Java开发中,经常需要将Java对象转换成Map,或者反过来将Map转换成Java对象。这种转换在很多场景下都非常有用,比如在序列化和反序列化过程中、在数据传输和持久化时、或者在进行对象属性的批量操作时。本文将介绍几种不同的方法来实现Java对象和Map之间的相互转换,选择哪种方法取......
  • 基于JAVA 小程序 旅游推荐管理系统,旅行系统
    目录一.研究目的二.系统分析三.系统流程和逻辑四.数据库设计五.页面展示六.源码获取一.研究目的随着互联网技术的快速发展,网络时代的到来,网络信息也将会改变当今社会。各行各业在日常企业经营管理等方面也在慢慢的向规范化和网络化趋势汇合。旅游社交小程序的信息化......
  • JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别
    JavaScript中structuredClone和JSON.parse(JSON.stringify())克隆对象的异同点一、什么是structuredClone?1.structuredClone的发展structuredClone是在ECMAScript2021(ES12)标准中引入的,ECMAScript2021规范正式发布于2021年6月自2022年3月起,该功能适用于最......
  • 【Java】【SpringBoot】yml配置文件解析
    yml的常见配置可以详见官方文档。https://docs.spring.io/spring-boot/appendix/application-properties/index.html#appendix.application-properties.server服务器配置server:port:8080#端口servlet:context-path:/#应用程序上下文路径【设置访问路径前缀......
  • java面试题(Spring、Spring MVC)
    点赞关注+收藏,万分感谢!!Spring1、为什么要使用spring?spring提供ioc技术,容器会帮你管理依赖的对象,从而不需要自己创建和管理依赖对象了,更轻松的实现了程序的解耦。spring提供了事务支持,使得事务操作变的更加方便。spring提供了面向切片编程,这样可以更方便的处理某一类......