关于Java是值传递还是引用传递,可以从代码层面来实现一下拿到结果
执行下面的代码:
public static void main(String[] args) { int num = 10; String name = "Tom"; modify(num, name); System.out.println("第3次打印int:" + num); System.out.println("第3次打印String:" + name); System.out.println("------------------------------------"); } public static void modify(int n, String str){ System.out.println("第1次打印int:" + n); System.out.println("第1次打印String:" + str); System.out.println("------------------------------------"); // 尝试在方法内部修改传进来的参数 n = 999; str = "ABC"; System.out.println("第2次打印int:" + n); System.out.println("第2次打印String:" + str); System.out.println("------------------------------------"); }
打印出来的结果如下:
第1次打印int:10 第1次打印String:Tom ------------------------------------ 第2次打印int:999 第2次打印String:ABC ------------------------------------ 第3次打印int:10 第3次打印String:Tom ------------------------------------
可以看到无论是基本类型还是引用类型,传参数进去的时候的值和执行完modify方法后的值是一样的,也就是第1次打印和第三次打印是一样的。可是为什么明明在第2次已经修改成功了,第3次却又变回去了呢?
尝试换个方法把参数拿出来,
public static void main(String[] args) { int num = 10; String name = "Tom"; int modifiedNum = modifyAndReturn(num); String modifiedName = modifyAndReturn(name); System.out.println("打印num:" + num); System.out.println("打印name:" + name); System.out.println("------------------------------------"); System.out.println("打印modifiedNum:" + modifiedNum); System.out.println("打印modifiedName:" + modifiedName); } public static int modifyAndReturn(int n){ System.out.println("modifyAndReturn第1次打印int:" + n); // 尝试在方法内部修改传进来的参数 n = 999; System.out.println("modifyAndReturn第2次打印int:" + n); System.out.println("------------------------------------"); return n; } public static String modifyAndReturn(String str){ System.out.println("modifyAndReturn第1次打印String:" + str); // 尝试在方法内部修改传进来的参数 str = "ABC"; System.out.println("modifyAndReturn第2次打印String:" + str); System.out.println("------------------------------------"); return str; }
得到的结果为
modifyAndReturn第1次打印int:10 modifyAndReturn第2次打印int:999 ------------------------------------ modifyAndReturn第1次打印String:Tom modifyAndReturn第2次打印String:ABC ------------------------------------ 打印num:10 打印name:Tom ------------------------------------ 打印modifiedNum:999 打印modifiedName:ABC
可以看到通过return出来的值,的确是被改变了的,那又是为什么导致这个改变没有应用到参数本体呢?修改下代码再次测试
public static void main(String[] args) { int num = 10; String name = "Tom"; // 打印num和str的地址 System.out.println("修改前,传参前:"); System.out.println(System.identityHashCode(num)); System.out.println(System.identityHashCode(name)); System.out.println("---------------------------"); printAddr(num, name); System.out.println("---------------------------"); System.out.println("修改后,执行完方法后:"); System.out.println(System.identityHashCode(num)); System.out.println(System.identityHashCode(name)); } public static void printAddr(int n, String str){ // 打印n和str的地址 System.out.println("修改前,传参后:"); System.out.println(System.identityHashCode(n)); System.out.println(System.identityHashCode(str)); n = 999; str = "ABC"; // 打印n和str的地址 System.out.println("---------------------------"); System.out.println("修改后,传参后:"); System.out.println(System.identityHashCode(n)); System.out.println(System.identityHashCode(str)); }
执行结果如下
修改前,传参前: 1324119927 990368553 --------------------------- 修改前,传参后: 1324119927 990368553 --------------------------- 修改后,传参后: 1096979270 1078694789 --------------------------- 修改后,执行完方法后: 1324119927 990368553
可以看到传参进来的参数地址是和外部定义的地址是同一个,但是修改之后会指向另一个新的地址,导致原来地址上的数据不会受到影响,这其实是一个保护机制,防止参数传入方法内被篡改指向。
下面演示引用类型的另一种情况,一些老铁可能以为是对引用类型本身的修改,其实这是不对的。
先定义一个类Person
class Person{ private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + ''' + ", age=" + age + '}'; } }
执行下面的代码,可以看到传进去的参数的属性被改变
public static void main(String[] args) { Person person = new Person("Rosy", 24); String [] strings = {"AAA", "BBB", "CCC"}; System.out.println("第1次打印:"); System.out.println(person); System.out.println(Arrays.toString(strings)); modifyObjAndPrintValue(person, strings); System.out.println("---------------------------"); System.out.println("第4次打印:"); System.out.println(person); System.out.println(Arrays.toString(strings)); } public static void main5(String[] args) { Person person = new Person("Rosy", 24); String [] strings = {"AAA", "BBB", "CCC"}; System.out.println("第1次打印:"); System.out.println(System.identityHashCode(person)); System.out.println(System.identityHashCode(person.getAge())); System.out.println(System.identityHashCode(person.getName())); System.out.println(System.identityHashCode(strings)); modifyObj(person, strings); System.out.println("---------------------------"); System.out.println("第4次打印:"); System.out.println(System.identityHashCode(person)); System.out.println(System.identityHashCode(person.getAge())); System.out.println(System.identityHashCode(person.getName())); System.out.println(System.identityHashCode(strings)); } public static void modifyObjAndPrintValue(Person person, String [] strings){ System.out.println("---------------------------"); System.out.println("第2次打印:"); System.out.println(person); System.out.println(Arrays.toString(strings)); person.setAge(1024); person.setName("ABC"); strings[0] = "XXX"; System.out.println("---------------------------"); System.out.println("第3次打印:"); System.out.println(person); System.out.println(Arrays.toString(strings)); }
执行结果为
第1次打印: Person{name='Rosy', age=24} [AAA, BBB, CCC] --------------------------- 第2次打印: Person{name='Rosy', age=24} [AAA, BBB, CCC] --------------------------- 第3次打印: Person{name='ABC', age=1024} [XXX, BBB, CCC] --------------------------- 第4次打印: Person{name='ABC', age=1024} [XXX, BBB, CCC]
从结果可以发现,Person对象的属性都被修改,String数组的元素也被修改,说明参数里对属性或数组的修改是会影响对象本身的,具体可以打印地址再查看一下:
public static void main(String[] args) { Person person = new Person("Rosy", 24); String [] strings = {"AAA", "BBB", "CCC"}; System.out.println("第1次打印:"); System.out.println(System.identityHashCode(person)); System.out.println(System.identityHashCode(person.getAge())); System.out.println(System.identityHashCode(person.getName())); System.out.println(System.identityHashCode(strings)); System.out.println(System.identityHashCode(strings[0])); modifyObjAndPrintAddr(person, strings); System.out.println("---------------------------"); System.out.println("第4次打印:"); System.out.println(System.identityHashCode(person)); System.out.println(System.identityHashCode(person.getAge())); System.out.println(System.identityHashCode(person.getName())); System.out.println(System.identityHashCode(strings)); System.out.println(System.identityHashCode(strings[0])); } public static void modifyObjAndPrintAddr(Person person, String [] strings){ System.out.println("---------------------------"); System.out.println("第2次打印:"); System.out.println(System.identityHashCode(person)); System.out.println(System.identityHashCode(person.getAge())); System.out.println(System.identityHashCode(person.getName())); System.out.println(System.identityHashCode(strings)); System.out.println(System.identityHashCode(strings[0])); person.setAge(1024); person.setName("ABC"); strings[0] = "XXX"; System.out.println("---------------------------"); System.out.println("第3次打印:"); System.out.println(System.identityHashCode(person)); System.out.println(System.identityHashCode(person.getAge())); System.out.println(System.identityHashCode(person.getName())); System.out.println(System.identityHashCode(strings)); System.out.println(System.identityHashCode(strings[0])); }
第1次打印: 990368553 1096979270 1078694789 1831932724 1747585824 --------------------------- 第2次打印: 990368553 1096979270 1078694789 1831932724 1747585824 --------------------------- 第3次打印: 990368553 1023892928 558638686 1831932724 1149319664 --------------------------- 第4次打印: 990368553 2093631819 558638686 1831932724 1149319664
从地址上可以看到,person和strings的地址一直没有变过。而在参数内部修改的person属性和数组元素,会对这部分成员的地址进行修改,并且会应用到对象本体上。
总结下来就是,无论传的是基本类型还是引用类型,只要在方法内部尝试改变参数地址的,都只能在方法内部使用,不会影响本体,而在方法内部改变属性的,会把对应的改变应用到本体上。所以Java是值传递的,传参的时候并不是把本身传入,而是创建一个副本,当被修改指向的时候不会影响本身,修改属性由于不会修改本身的地址,因此的时候可以应用到本体上。