值传递和引用传递分析
Java中数据类型分为两大类:基本类型和引用类型(也就是对象类型)。
基本类型:boolean、char、byte、short、int、long、float、double
引用类型:类、接口、数组
因此,变量类型也可分为两大类:基本类型和引用类型。
在分析值传递和引用传递之前,建议了解下以上变量类型在Java内存管理模型中的位置,如果对此有所了解,将更加有助于理解两种传递的方式^_^
方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。
引用传递:
也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象。
Demo代码:
输出结果:
值传递测试
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方法进行的是值传递,对形参修改但实参未改变,利用内存模型详解原理:
通过上面的分析,对于传递方式应该很好理解了^_^
注意:这里要特殊考虑String,以及Integer、Double等基本类型包装类,它们的类前面都有final修饰,为不可变的类对象,每次操作(new或修改值)都是新生成一个对象,对形参的修改时,实参不受影响,与值传递的效果类似,但实际上仍是引用传递。
总结:
1)基本类型变量作为方法中的参数,进行的值传递,对形参的修改不影响实参的原来的值;
2)非final修饰的类、数组、接口作为方法中的参数,进行的引用传递(地址传递),对形参修改后实参也会改变,因为二者指向的是同一个实例;
3)final修饰的类作为方法中的参数,因为final的存在初始化后值不可变,每次操作都相当于产生一个新的实例对象,因此对形参修改时,实参也不受影响。
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的存在初始化后值不可变,每次操作都相当于产生一个新的实例对象,因此对形参修改时,实参也不受影响。