万万没想到,在工作后的一年,再次犯了当年入门时的错误,在此要对自己所薄弱的知识体系进行重温和巩固。
首先关于参数传值的理解,记得当年自学这一块的时候,看得是云里雾里,后面经过一段时间的编程实践,才变成似懂非懂,直到今天再次犯错,我才知道自己是真的没有理解通透。先看我犯下的错误。
public static void main(String[] args) {
List<String> list =new ArrayList<>();
System.out.println(list.hashCode());
change(list);
System.out.println(list.hashCode());
}
public static void change(List<String> list){
list=new ArrayList<>();
list.add("a");
list.add("b");
System.out.println(list.hashCode());
}
我们可以看到我在change方法外实例化的对象传进方法内后又让形参指向了新的对象,再加上我没有返回值,这相当于我这个方法在做无用功,这是令人羞耻的代码。
首先说说java的参数传值
java没有指针,取而代之的是引用,所以我们要认清的两个概念,一个是基本类型,一个是引用类型(基本类型又与包装类型形成两个相对概念,这一块是拆装箱的概念),在这里不多提及。
既然是取代指针,那它们之间便有共性,那就是我们的引用的内存地址所存储的是地址,就像C中指针指向的是地址。
一般的基本类型,它内存地址中所存储的是它的值。用一个图可以看得更清晰。
理解了这个基础概念后,就好理解为什么java参数都是传值
当我们调用方法传入我们的参数list,这里我们称之为实参。而方法中的list引用我们称之为形参。
如果我们是基本类型,那我们的实参在调用方法时,只是将它的内存中存储的值copy给形参,也就是说形参所指向的内存地址和实参指向的内存地址不是同一个,只是两个内存地址都存放相同的值。
理解了上面这段话,就可以理解,为什么基本类型传入的实参,如果在方法中被修改了,只会修改形参的值,并不会影响实参的值。
关于引用传值
那为什么引用类型作为实参传入却会被改变。这里要对传值这两个字要发散的思考,这个传值的意思是指传这个引用指向的内存块所存储的东西,它不一定是数值的二进制,就好像我上面那图一样,它还有可能是另一块内存地址。
换言之,引用对象传进去的不是值,而是内存地址,这会导致什么情况?会导致形参也指向这个内存地址,所以方法内如果对形参做出了改变,则实参的数据也会被改变。
对象的引用和生命周期
根据上面所说,我的形参已经指向了实参的同一块内存地址,如果这个时候,我又new一个对象,让形参指向它,则这个时候的形参和实参指向的就是不同的内存地址,则我在方法内所做的改变,如果要保存下来的话,最好的方法就是返回它。
因为在方法内实例化的对象,它如果没有被外面的变量所引用,则这个对象会在方法执行完之后被销毁,如果返回了它,外面的引用指向了这个对象,则这个对象会升级,JVM则不会在短时间内回收这个对象。
滤清这些知识点之后,看到自己写下的代码,不禁脸红。。。