Java内存分析 其二
来源:互联网 发布:mysql redis 特点区别 编辑:程序博客网 时间:2024/06/09 16:35
Java内存分析 值传递与址传递
关于值传递与值传递,之前已经有了初步的认识,不过总有点云里雾里的感觉,运用时总是会出问题,经过几天的学习,这里算是一个详细的总结。另外还有一些关于使用String时所遇到的问题。
两种传递?一种传递?
当Java的方法进行传递时,对于基本类型,传递的是基本类型的值,而对于引用类型,传递的则是地址。
但是,地址实际上也是一个值,因此可以理解为,无论传递的是什么,所传递的都是值。
当传入一个基本类型变量时,是从常量池中把值复制了一份出来,也就是说方法的操作都是在对这个复制进行的,因此原来的值不会改变。
当传入一个引用类型变量时,情况会分为两种:
- 在方法中地址发生了改变,那么原来的值不变。
- 在方法中地址没有发生改变,那么在方法中操作地址对应对象的属性,原来的值就会改变。
值传递
class Test { public static void main(String[] args) { int num = 11; add(num); System.out.println(num); } private static void add(int i) { i = i + 989; }}//显然,这里会打印 11
址传递
class Test { public static void main(String[] args) { int[] arr = {1, 2, 3}; System.out.println("before: " + arr); System.out.println("before: " + arr[0]); change(arr); System.out.println("after: " + arr); System.out.println("after: " + arr[0]); } private static void change(int[] arr) { System.out.println("changing: " + arr); arr[0] = 100; System.out.println("changed: " + arr); }}/*before: [I@1540e19dbefore: 1changing: [I@1540e19dchanged: [I@1540e19dafter: [I@1540e19dafter: 100*/
上面的例子中,从创建到改变后返回 引用地址始终没有发生改变,所有的操作都是对该地址所指向的对象进行的。
再看下面一个例子
class Test { public static void main(String[] args) { String str = "abc"; System.out.println("before: " + str); change(str); System.out.println("after: " + str); } private static String change(String str) { str = "def"; return str; }}
这里最后会输出什么呢?刚开始,我认为和上面的例子一样,str的引用地址被传入change()中,进行操作后,地址不变,也就是最后输出的str的值是def。
然而,结果却正相反,str的值并没有发生改变,还是abc。
为什么会出现这种情况呢?
显然,在change()中,str的引用地址发生了改变。
s传递到方法里的时候,复制了s指向的地址给change,change里的s是另一个s,s指向aaa(@718),然后在change中s又指向了123(@731),由于String是不可变类(final and Immutable),这里只是把副本s的指向修改成731,原地址718里的对象没有发生改变因为String不可变。那么,回到主方法的时候,s变量本身没有任何改变,s仍旧指向地址718,718的内容是aaa。所以最终打印aaa。
转自 http://www.cnblogs.com/woshimrf/p/5263018.html
结合来看,当str被传入change()中后,实际上是创建了一个str的副本,并将这个指向的地址传给了change(),是值相同的两个str。
在change()中,当重新赋值时,实际上是在堆中重新分配了一个内存空间,change()中str的副本有了新的引用地址,自然,原str的指向的对象的内容并没有改变,最终打印也不会发生变化。
最后记录一个自己犯的低级错误!
public class PublicAClass { public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(); p1.age = 30; p2.age = 40; change(p1, p2); System.out.println(p1.age); } public static boolean change(Person p1, Person p2) { p1 = p2; return true; }}class Person { String name; int age; char sex;}//最后输出30
被传入change()中的只是同原p1引用地址相同的一个副本,而在change()中的操作,仅仅是将p2的引用地址赋值给了方法中p1的副本,而不是对p1在堆中的值进行赋值,而方法也没有将副本的新的引用地址返回给原p1,自然不可能返回我们期望输出的结果!
学完了值传递与址传递后,对于整个内存操作都有了更深的认识理解,收获还是非常多的。
- Java内存分析 其二
- java方法的参数传递其二
- java 排序算法实现 其二:插入排序
- java高级特性之集合概述 其二
- JAVA互联网架构学习之SpringMVC其二
- oracle 11g 自动内存管理(其二)
- 浅谈Android开发中内存泄露与优化-------其二
- Java的内存分析
- Java内存分析图
- Java内存分析小节
- Java 程序内存分析
- Java 程序内存分析
- Java 内存溢出分析
- java内存分析
- java内存分析
- java内存分析【精辟】
- JAVA内存分析笔记
- java内存模型分析
- SSM—CRUD maven 管理
- 日语的年份怎么读
- 动态规划实现:给定整数m , 取若干个1到n的整数可求和等于整数m,编程求出所有组合的个数。
- jenkins android自动化构建
- BZOJ 5011 [JXOI2016]颜色
- Java内存分析 其二
- JSON总结篇
- java web spring mvc 框架中使用mybatis报错的解决方案
- 视频播放的三种方式
- 【持续集成 Jenkins入门系列】02第二章 Jenkins安装与配置
- 基于Servlet的会话跟踪
- iOS Xcode9 不使用USB运行真机
- Vsphere日记01.ESXi5.5.install
- Jquery事件绑定函数:on和bind的区别