`

值传递与引用传递

阅读更多
一、值传递与引用传递

其实按值还是按引用的区别在于“是否在传递的时候进行对象的内存拷贝”,
java中基本类型是由于在JVM中存储区域不同于普通对象所以传递前会拷贝,传递的是拷贝后的值,
但是对象在传递的时候不拷贝,直接传“引用值”,指向同一片对象堆内存区域

二、基本数据类型

public class IntegerTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		int a = 10 ;
		Integer b = new Integer(10);
		IntegerTest test = new IntegerTest();
		test.change(a);
		test.change(b);
		System.out.println(a);
		System.out.println(b);
	}

	public int change(Integer a){
		a = a + 10 ;
		return a ;
	}
}



输出结果:
10
10

基本数据类型
main() 方法向 change() 中传递的是 栈中a 所存储的内容 10
change() 方法,形参 a ,虽然与 main() 中,名称相同,但是两个不同的变量


对应的包装类
// Integer 的 value 是 final 修饰的
    /**
     * The value of the <code>Integer</code>.
     *
     * @serial
     */
    private final int value;

其底层实现中value是final修饰,是不可改变的
与String 类似,其值是不可变更的

若欲通过change() 修改数值:
通过change()的返回值,重新赋值给 main()中的a

结论:
基本数据类型及对应的包装类在传递过程中,不会改变其所指向的内容


三、String 类型

public class StringChangeTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		String str = "abc" ;
		new StringChangeTest().change(str);
		System.out.println(str);
	}

	public void change(String str){
		str = "bcd";
	}
}



字符串只要赋值就相当于新new一个对象,字符串变量指向这个新new的对象,之前的对象就成了没有引用指向的对象了。

1.String 的两种声明方式

String str = "abc"
栈中建立 str
判断堆中是否已有"abc",无则创建新的匿名空间,存放 "abc"
匿名空间首地址存放在 str 中,即 str 指向堆中的此空间

String str = new String("abc")
栈中建立 str
判断堆中是否已有"abc",无则创建新的匿名空间,存放 "abc"
堆中开辟新的空间,存放 "abc"
新的空间首地址存放在 str 中,匿名空间因无引用,过后被GC回收

备注:
提高程序运行效率,优选第一种方式

2.
String str = "abc" ;
未显示调用 new 操作,在编译时 相当于 new String("abc");

3.
change(String str);
此处的str 不同于 main() 中的 str ,相当于重新开辟了一个空间,虽然名称相同
调用方法时,main() 方法将 str = "abc" 的堆空间地址传递给了 change() 中的str
此时 main() change() 中的两个不同的 str 均指向同一个堆内存块

但 str = "bcd" ; 相当于 在堆中新开辟了一块空间,存放 "bcd",并将地址存放在了str 中
即 change() 中的 str 指向了新的堆中的地址 -- "bcd" 的地址,与 main() 中的 str 指向的 "abc" 无关了,在 change() 中对 str 的修改,也与 main() 中无关,所以值没有改变

四、引用类型

public class ArrayIntegerTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		int [] a1 = {1,2,3,4,5};
		new ArrayIntegerTest().change(a1);
		for(int i = 0 ; i < a1.length ; i++){
			System.out.println(a1[i]);
		}
		
		int [] a12 = new int[]{1,2,3,4,5};
		new ArrayIntegerTest().change(a12);
		for(int i = 0 ; i < a12.length ; i++){
			System.out.println(a12[i]);
		}
		
		Integer [] a2 = {1,2,3,4,5};
		new ArrayIntegerTest().change(a2);
		for(int i = 0 ; i < a2.length ; i++){
			System.out.println(a2[i]);
		}
		
		char [] s1 = {'1','2','3'};
		new ArrayIntegerTest().change(s1);
		System.out.println(new String(s1));
	}

	public void change(int[] a){
		a[0] = 10 ;
	}
	
	public void change(Integer[] a){
		a[0] = 10 ;
	}
	
	public void change(char[] a){
		a[0] = 'a';
	}
}




main() 中,建立引用对象,在堆中开辟新的空间
change() 中,形参声明新的对象,与main的不同
main() 调用 change() 时,将 栈中存放的堆中空间的首地址传递给 change() 中的参数
两者指向同一个数据地址
所以,change() 中的操作影响 main() 中的数据内容

public class PersonTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Person p = new Person();
		p.setAge(10);
		
		new PersonTest().change(p);
		System.out.println(p.getAge());
	}

	public void change(Person p){
		p.setAge(20);
		
		p = new Person();
		p.setAge(30);
	}
}

class Person{
	private int age ;

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
	
}


输出:20

调用 change() ,与 main() 中 p 是两个不同的句柄,但此时指向同一个堆中的地址
setAge(20) 有效
重新new 一个p ,此时 change() 中的 p 与 main() 的 p 指向的堆中的地址已经不一样了
new 新开辟空间,一定与之前的不同
所以 setAge(30) 对 main() 中的无影响

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics