原创博文,转载请注明出处。谢谢~~


java程序运行时,其对象是怎么进行放置和安排的呢?内存是怎么分配的呢?理解好这个很有好处!java有5个地方可以存储数据:

1、寄存器。这是最快的存储区,位于处理器内部。java程序员无法感知到它的存在,所以不用深究。

2、堆栈。位于内存中,通过堆栈指针可以获得直接的内存分配。对象引用和基本数据(int,boolean等)存放于堆栈中。注意:是对象的引用,对象数据本身却是存放在中的。于对象而言,在堆中存放的只是对象数据的一个地址(类似于C语言的指针),通过它可以访问到对象本身的属性和方法。

3、堆。同样位于内存中,用于存放所有的java对象。需要一个对象的时候,只需要用new写一行简单的代码,当执行这行代码的时候,会直接在堆中进行空间分配。

4、常量存储。常量值直接存放在代码内部,保证安全。

5、非RAM存储。比如,流对象和持久化对象,可以存放在磁盘文件中。

先来看一看下面这个对象是如何存放的

String s = new String("hello");

下图表示了其存储状态:

new String("hello") 语句中用关键字 new 定义了一个String的对象,并为其在 “堆内存” 中分配了合适的存储空间。

s 是一个String型对象的引用,实际上就是堆内存中一个String型对象的地址。引用 s 保存在 “堆栈” 中

清楚了对象和其引用的存储方式以后,我们再来看看方法的参数传递是怎么一回事情。看看下面语句:

List-1:

 public static void main(String[] args) {

     Func f = new Func();

     A a1 = new A();
f.modify(a1);
}

语句很简单,在main方法中首先定义了一个Func对象f和A对象a1,然后将a1作为参数传递给f的modify方法中。程序在第6行的时候会从main方法跳转到f.modify(...)方法内部执行。下图展示了数据的保存状态:

可以清楚的看到main方法向modify方法中传递的是对象引用 a1 的一份拷贝(为了方便区分,将拷贝命名为aa1),这两个引用都指向同一块堆内存区域(同一个A对象)。所以,我们可以想象,在modify方法中可以通过 aa1 来修改A对象的属性,而且其变化在由modify返回main以后依然会保留下来

下面通过一个例子来分析一下:

List-2:

 package com;

 public class A {
int cunt = 5;
String str = "hello"; public String toString() {
return "A [cunt=" + cunt + ", str=" + str + "]";
}
}
 package com;

 public class Func {

     public void modify(A a){
//通过“对象引用”来修改对象的属性
a.cunt = 111;
a.str = "modify";
} public void newObj(A a){
//创建了一个新的对象,企图在方法中将新对象引用赋值给形参a的
//方式来达到修改外围方法中实参引用的目的。
//注意:这个是做不到的。
a = new A();
a.cunt = 222;
a.str = "newObj";
} public A reNewObj(A a){
//返回一个新对象,在外围方法中将返回的对象引用赋值给实参
//这种方法是可以实现的。
a = new A();
a.cunt = 333;
a.str = "reNewObj";
return a;
} public static void main(String[] args) { Func f = new Func(); //在modify方法中通过形参来改变a1对象的属性,在modify
//方法返回以后,修改的属性可以保留下来
A a1 = new A();
f.modify(a1);
System.out.println(a1); //企图通过newObj方法让a2指向新的对象
//但是,这样做不可能达到目的
A a2 = new A();
f.newObj(a2);
System.out.println(a2); //reNewObj方法会返回一个新的对象引用,并将其赋值给a3
//这种方式可以让a3指向一个新的对象。
A a3 = new A();
a3 = f.reNewObj(a3);
System.out.println(a3); }
}

运行结果:

我们知道,A对象初始化的时候 cunt = 5, str = "hello" 。现在来分析上面的三个结果的原理。

1、第一个结果是 “cunt = 111, str = "modify" 说明 f.modify(a1) 方法中通过形参a1对A对象属性的修改被保留了下来。现在来分析,为什么会保留下来??

解释:

①. 在main方法中,a1指向堆内存中的一个A对象,a1是作为A对象的一个引用,其值是“引用”(对空间的地址值)。

调用f.modify(a1)的时候,a1作为实参传递给modify方法,由于java参数传递是属于“值传递”(值的拷贝),所以在modify堆栈中会有a1的一份拷贝,两个a1指向同一个A对象。

②. 通过modify堆栈中的引用a1可以操作其所指向的对内存中的对象。在modify方法中将堆内存中的A对象属性值修改为“cunt = 111, str = "modify"”。

③. modify方法返回以后,modify堆栈中的a1引用所占内存被回收,main堆栈中的a1依然指向最开始的那个对象空间。由于在modify方法中已经将对象的属性修改了,所以在main中的a1可以看见被修改后的属性值。

2、第二个结果是 “cunt = 5, str = "hello",说明main方法中的a2所指向的对象没有变化。42行中 f.newObj(a2); 方法的原本意图是:在newObj方法中新创建一个A对象,并将其赋值给newObj的形参a2,并且期望在newObj方法返回以后main方法的局部变量a2也指向新的A对象。

可是,从打印的结果来看,main方法中的局部变量a2依然指向原来的A对象,并没有指向newObj方法中新建的A对象。

那么,这个是为什么呢??

解释:

①. 前面两个图和1中的解释是一样的,无非就是一个参数的“值传递问题”。

②. 在newObj方法中有这样的三条语句:a = new A(); a.cunt = 222; a.str = "newObj";

   其执行过程是这样的:在堆内存中开辟一块新的A对象空间,让newObj堆栈中的a2引用指向新的对象空间 → 后两句语句会修改新对象的属性值。

注意,此时main堆栈中的a2依然指向最开始的那个对象空间,而newObj堆栈中的a2却指向了新的对象。

③. newObj方法返回,newObj堆栈空间被回收,堆空间中的新对象没有任何变量引用它,在合适的时机会被JVM回收。此时,返回到main方法中,此时的a2依然指向

依然指向最开始的那个A对象,其属性值没有发生变化。

所以,外围方法中的实参无法看见在内部方法中新创建的对象。除非,新创建的对象作为内部方法的返回值,并且在外围方法中将其赋值给实参变量。就像47~49行所做的那样:在reNewObj方法中新创建一个对象并赋值给形参,同时在方法最后将新对象作为返回值返回给外围方法,进一步在外围方法中将返回的对象赋值为a3,从而达到目的。

1、探究java方法参数传递——引用传递?值传递!的更多相关文章

  1. 辨析Java方法参数中的值传递和引用传递

    小方法大门道 小瓜瓜作为一个Java初学者,今天跟我说她想通过一个Java方法,将外部变量通过参数传递到方法中去,进行逻辑处理,方法执行完毕之后,再对修改过的变量进行判断处理,代码如下所示. publ ...

  2. Java的参数传递是「值传递」还是「引用传递」?

    关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题. 有人说Java中只有值传递,也有人说值传递和引用传递都是存在的,比较容易让人产生疑问. 关于值传递和引用传递其实需要分情况看待. ...

  3. java方法参数传递方式只有----值传递!

    在通常的说法中,方法参数的传递分为两种,值传递和引用传递,值传递是指将实际参数复制一份传递到方法中, 在方法中的改动将不会影响到实际参数本身,而引用传递则是指传递的是实际参数本身,在方法中的改动将会影 ...

  4. 疯狂java学习笔记之面向对象(三) - 方法所属性和值传递

    方法的所属性: 从语法的角度来看:方法必须定义在类中 方法要么属于类本身(static修饰),要么属于实例 -- 到底是属于类还是属于对象? 有无static修饰 调用方法时:必须有主调对象(主语,调 ...

  5. Java 参数传递都是值传递

    Java 参数传递都是值传递,验证代码如下 public class ParamTransferTest { public static void swap(int a, int b) { int t ...

  6. java中为什么只存在值传递(以传入自定义引用类型为例)

    java中只有值传递 为什么这么说?两个例子: public class Student { int sage = 20; String sname = "云胡不归"; publi ...

  7. java中方法的参数传递机制(值传递还是引用传递)

    看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参 ...

  8. java 基础:方法调用中的值传递是call by value,并且传递的是参数的值的拷贝,而不是引用

    public class TestExtends { public static void main(String[]args){ int s = 10; System.out.println(Sys ...

  9. 面试之路(18)-java的函数参数传递类型之值传递还是引用传递

    关于这个问题争论了很久,最近也是偶然发现这个问题 经典名言: O'Reilly's Java in a Nutshell by David Flanagan (see Resources) puts ...

随机推荐

  1. openerp学习笔记 调用工作流

    获取工作流服务:wf_service = netsvc.LocalService("workflow")删除对象对应记录的工作流:wf_service.trg_delete(uid ...

  2. 回溯(su)算法之N皇后问题

    这里回溯算法还要好好研究一下 试探一个位置是否有效,如果有效,试探下一个位置(DFS),如果无效则回退 1.定义一个解空间,存放一个解的空间 2.DFS(暂且认为是DFS) 这里N皇后用的是递归+回溯 ...

  3. 《Power》读书笔记

    原创作品 版权所有 转载请注明出处! 序言 权力是“争”来的,不是“等”来的. 会计.工商管理.营销和销售部门.财务人员(背景).企业咨询小组 在位晋升而竞争的时候,对于公平竞争原则,有些人会采取变通 ...

  4. PHP Cookie处理函数

    (o゜▽゜)o☆[BINGO!] ok,我们先看看cookie是什么东东? cookie是服务器留在客户端的用于识别用户或者存储一些数据的小文件(注意,session存储在服务器端,这是两者的区别之一 ...

  5. 第三章DOM

    1. DOM的概念 D:Document. O:Object.对象可以分为三类, 1. 用户自定义的对象. 2. 内建对象,如Array,Math,Date. 3. 宿主对象,浏览器提供的对象.如wi ...

  6. Maven--(一个坑)在settings.xml文件中添加mirrors导致无法新建Maven项目

    这是用新电脑第一次创建Maven项目--当然是一个测试项目.已经差不多忘了该怎样做,所以参考我的博客:http://www.cnblogs.com/wql025/p/4996486.html,这应该是 ...

  7. Jquery 查看DOM上绑定的事件列表

    $(dom).data( "events" ); 包括事件类型和关联的处理函数 下面是firefox的截图

  8. 【BZOJ】【2879】【NOI2012】美食节

    网络流/费用流 跟 BZOJ 1070 修车 几乎是一道题,只是这题“要修的车”(即菜)多了很多……几乎是从$n$变成了$n^2$,所以建图的时候就得动态加点…… 也就是说,当一个厨师已经确定了他的后 ...

  9. 【BZOJ】【1029】【JSOI2007】建筑抢修

    贪心 按T2(完成时限)排序,然后从前往后依次枚举 如果sum+a[i].t1<=a[i].t2则加入 如果来不及修这个建筑: 如果当前这个建筑的维修时间t1比之前修过的建筑中耗时最长的耗时短, ...

  10. cg 到hlsl的转换

    http://msdn.microsoft.com/en-us/library/windows/desktop/ff471376(v=vs.85).aspx http://gamedev.stacke ...