Java引用传递解析

0.前言

因为之前有同学刚好问我一个关于Java引用传递的问题,为什么将ArrayList中拿到的一个元素,赋给一个对象,对象修改了属性,ArrayList中的对应的元素属性也会修改,这就牵扯出Java中一直讨论的一个问题:Java传参时是引用传递还是值传递。 在这篇文章里我们主要会对一下几个问题进行讨论。

  • 1.基本类型和引用类型在内存中的保存
  • 2.变量的基本类型和引用类型的区别
  • 3.引用传递和值传递

1.基本类型和引用类型在内存中的保存

Java中数据类型分为两大类,

  • 基本类型:`byte, short, int, long, char, float, double, Boolean,不过,Java字节码中还有额外的两种基础数据类型,分别是reference type与returnAddress type;
  • 对象类型:类,接口和数组。
    相应的,变量也有两种类型:
  • 基本类型:基本类型的变量保存原始值,即它代表的值就是数值本身;
  • 引用类型:引用类型的变量保存引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身。

2.变量的基本类型和引用类型的区别

2.1 基本类型的初始化

基本数据类型是存储在栈内存中,当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。

举个例子:

int a = 3;

首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。

题外话:不是所有的基本数据类型都存在java栈里,为了反驳观点" Java的基本数据类型都是存储在栈的 ",我们也可以随便举出一个反例,例如:

int[] array=new int[]{1,2};

由于new了一个对象,所以new int[]{1,2}这个对象时存储在堆中的,也就是说1,2这两个基本数据类型是存储在堆中,这也就很有效的反驳了基本数据类型一定是存储在栈中~~

2.2 引用类型的初始化

image.png

上图表示的是在JVM中,Java对象的访问方式。从上图可以看出,对象的引用变量是存储在栈内存中的。我们通过一个例子来分析下这个过程。

Object obj = new Object();
  • 第一步:new Object() :JVM将在GC堆中开辟一定大小的空间用以存放此Object,此Object是引用数据类型(注意不是reference类型,reference类型保存在Java栈中的本地变量表里)。另外,在JAVA堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现接口、方法等)的地址信息,这些类型数据存储在方法区中。
  • 第二步: Object obj:JVM将在虚拟机栈中此线程栈开辟出一个slot用以存放obj,注意此处obj为reference type。(slot可以理解为用以存放基础数据类型的线程私有的空间)
  • 第三步:=:reference type 的obj被赋值为先前new Object() 指令所产生的引用数据类型的一个对象。

分析完不同类型的变量在内存中的储存形式之后,我们就可以来分析下到底什么时候是值传递,什么时候是引用传递。

3.引用传递和值传递

  • 值传递:
    方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。

  • 引用传递:
    也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象

举例:

public class Demo0 {
  
  private User user = new User("Jack", 1l);
  
  private int num;
  
  private String string ="";

  //提供改变自身方法的引用类型
  void fun0(User user){
    user.setName("Lucy");
  }

  //基本类型作为参数传递时,是传递值的拷贝,无论你怎么改变这个拷贝,原值是不会改变的
  void fun1(int value) {
    value = 100;
  }
  
  //没有提供改变自身方法的引用类型
  void fun2(String text) {
    text = "windows";
  }

  //在Java中对象作为参数传递时,是把对象在内存中的地址拷贝了一份传给了参数。
  public static void main(String[] args) {
    Demo0 demo = new Demo0();
    demo.num = 10;
    demo.string = "Linux";
    
    System.out.println("before name: " + demo.user.getName());
    demo.fun0(demo.user);
    System.out.println("after name: " + demo.user.getName());

    System.out.println("before num: " + demo.num);
    demo.fun1(demo.num);
    System.out.println("after num: " + demo.num);
    
    System.out.println("before string: " + demo.string);
    demo.fun2(demo.string);
    System.out.println("after string: " + demo.string);
    
  }

}

  • 函数fun0运行结果:
before name: Jack
after name: Lucy

user的name属性被修改,原因是:fun0调用时,函数接收的是原始值的内存地址;方法执行setName操作,修改了堆中改内存地址上的User对象的属性。通过这个例子我们可以知道:引用类型传递的是对象的引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象。

  • 函数fun1运行结果:
before num: 10
after num: 10

很显然输出的 是10,10。传递的是值得一份拷贝,这份拷贝与原来的值没什么关系。基本数据类型传值,对形参的修改不会影响实参。

  • 函fun2运行结果:
before string: Linux
after string: Linux

这就很有意思了,为什么String是引用类型,为什么不会修改其值呢?原因是:String,以及Integer、Double等几个基本类型包装类,它们都是immutable类型,因为没有提供自身修改的函数,每次操作都是新生成一个对象,所以要特殊对待,可以认为是和基本数据类型相似,传值操作。

现在来看一开始提出的那个问题:为什么从ArrayList中get第一个元素,赋给一个对象,对象修改了属性,ArrayList中的元素属性也会修改?

public class Demo2 {
  public static void main(String[] args) {

    List<User> list = new ArrayList<User>();
    User user1 = new User("name1", 1111l);
    User user2 = new User("name2", 2222l);
    User user3 = new User("name3", 3333l);
    list.add(user1);
    list.add(user2);
    list.add(user3);
    
    System.out.println("--------------Before---------------");
    for (User user : list) {
      System.out.println("name = " + user.getName());
    }
    
    User userTemp = list.get(0);
    userTemp.setName("xxxxxx");
    
    System.out.println("--------------After---------------");
    for (User user : list) {
      System.out.println("name = " + user.getName());
    }
    
  }
}

输出:

--------------Before---------------
name = name1
name = name2
name = name3
--------------After---------------
name = xxxxxx
name = name2
name = name3

原因很简单:userTemp 和list的第一个对象都指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象

4.总结

java中方法参数传递方式是按值传递。

  • 如果参数是基本类型,传递的是基本类型的字面量值的拷贝。
  • 如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝
  • String,以及Integer、Double等几个基本类型包装类,它们都是immutable类型,因为没有提供自身修改的函数,每次操作都是新生成一个对象,所以要特殊对待,可以认为是和基本数据类型相似,传值操作。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,246评论 11 349
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,631评论 18 399
  • JVM内存模型Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: ...
    光剑书架上的书阅读 2,509评论 2 26
  • 机缘巧合的机会,我参加了趁早的百日理财训练营。作为一只理财小白,从没想过从中能收获什么,只是默默地跟着组织...
    solarxue阅读 890评论 0 0
  • 在市场经济环境下,任何理论、方法、技术、产品或服务的发扬光大,都离不开“营销”。“营销”的基本套路为:传播信息——...
    木青观察阅读 537评论 0 0