其实按值还是按引用的区别在于“是否在传递的时候进行对象的内存拷贝”,
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() 中的无影响