您的位置:首页 > 其它

通过JVM内存模型深入理解值传递和引用传递两种方式

2018-01-01 23:16 585 查看
值传递和引用传递分析
Java中数据类型分为两大类:基本类型和引用类型(也就是对象类型)。
基本类型:boolean、char、byte、short、int、long、float、double
引用类型:类、接口、数组
因此,变量类型也可分为两大类:基本类型和引用类型。
在分析值传递和引用传递之前,建议了解下以上变量类型在Java内存管理模型中的位置,如果对此有所了解,将更加有助于理解两种传递的方式^_^
在Java内存中,基本类型变量存储在Java栈(VM Stack)中,引用变量存储在堆(Heap)中,模型如下:



值传递和引用传递的定义:
这里要用实际参数和形式参数的概念来帮助理解

值传递:

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

引用传递:

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

Demo代码:

package cn.roc.other;

/**
* @author Roc
* @desc Java中参数传递方式分为值传递和引用传递
* 1)基本类型变量
* @date created on 2017/12/31
*/
public class ArgumentsPassTypeTest {
public static void main(String[] args) {
System.out.println(" 值传递测试 ");
int a = 10;
int b = 20;
System.out.println("before swap " + "a = " + a + " b = " + b);
swap(a, b);
System.out.println("after swap " + "a = " + a + " b = " + b);

System.out.println("-------------------------------------------------------------");

System.out.println(" 引用传递测试 ");
ReferenceObj obj = new ReferenceObj();
System.out.println("before swapByReference: count = " + obj.count);
swapByReference(obj);
System.out.println("after swapByReference: count = " + obj.count);

System.out.println("-------------------------------------------------------------");

//String、Char、Byte、Short、Integer、Long、Float、Double等final修饰的类
//对形参修改时实参不受影响
System.out.println(" final修饰的类-特殊的引用传递测试 ");
String str = "我是final我不变";
swapByFinalClass(str);

System.out.println("after swapByFinalClass,  str = " + str);
}

/**
* 值传递方式    基本类型
* @param a
* @param b
*/
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("swaping " + "a = " + a + " b = " + b);
}

/**
* 引用传递方式   类、数组、接口
* @param obj
*/
public static void swapByReference(ReferenceObj obj) {
obj.count = 0;
System.out.println("swaping : count = " + obj.count);
}

/**
* final修饰的类做形参时, 修改形参不影响实参
* @param str
*/
public static void swapByFinalClass(String str) {
str = "我是形参";
System.out.println("swapByFinalClassing : str = " + str);
}
}

class ReferenceObj{
int count = 99;
}


输出结果:
值传递测试

before swap a = 10 b = 20

swaping a = 20 b = 10

after swap a = 10 b = 20

-------------------------------------------------------------

 引用传递测试 

before swapByReference: count = 99

swaping : count = 0

after swapByReference: count = 0

-------------------------------------------------------------

 final修饰的类-特殊的引用传递测试 

swapByFinalClassing : str = 我是形参

after swapByFinalClass,  str = 我是final我不变

1)使用基本类型的变量a、b通过swap方法进行的是值传递,对形参修改但实参未改变,利用内存模型详解原理:

 


2)使用类ReferenceObj的实例变量obj,通过swapByReference()进行的是引用传递的方式,具体的内存模型如下:



 
通过上面的分析,对于传递方式应该很好理解了^_^

注意:这里要特殊考虑String,以及Integer、Double等基本类型包装类,它们的类前面都有final修饰,为不可变的类对象,每次操作(new或修改值)都是新生成一个对象,对形参的修改时,实参不受影响,与值传递的效果类似,但实际上仍是引用传递。

总结:

1)基本类型变量作为方法中的参数,进行的值传递,对形参的修改不影响实参的原来的值;
2)非final修饰的类、数组、接口作为方法中的参数,进行的引用传递(地址传递),对形参修改后实参也会改变,因为二者指向的是同一个实例;
3)final修饰的类作为方法中的参数,因为final的存在初始化后值不可变,每次操作都相当于产生一个新的实例对象,因此对形参修改时,实参也不受影响。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: