Java内存分析 其二

来源:互联网 发布:mysql redis 特点区别 编辑:程序博客网 时间:2024/06/09 16:35

Java内存分析 值传递与址传递


关于值传递与值传递,之前已经有了初步的认识,不过总有点云里雾里的感觉,运用时总是会出问题,经过几天的学习,这里算是一个详细的总结。另外还有一些关于使用String时所遇到的问题。

两种传递?一种传递?

当Java的方法进行传递时,对于基本类型,传递的是基本类型的值,而对于引用类型,传递的则是地址。

但是,地址实际上也是一个值,因此可以理解为,无论传递的是什么,所传递的都是值

当传入一个基本类型变量时,是从常量池中把值复制了一份出来,也就是说方法的操作都是在对这个复制进行的,因此原来的值不会改变。

当传入一个引用类型变量时,情况会分为两种:

  1. 在方法中地址发生了改变,那么原来的值不变。
  2. 在方法中地址没有发生改变,那么在方法中操作地址对应对象的属性,原来的值就会改变。

值传递

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,自然不可能返回我们期望输出的结果!


学完了值传递与址传递后,对于整个内存操作都有了更深的认识理解,收获还是非常多的。