杂记-Java传值与传地址
Java传值与传地址
Java中传值还是传址问题在我初学Java的时候一直都没有很好的理解它,一直都以为Java不存在传值,只有传地址,很迷惑Java有传值传址的问题吗?
这几天工作不是很忙把我想到的了解的整理一下,记录自己的成长历程。
Java到底传值还是传址?
Java传值,传址都有的,并不是所有的都是传址。
Java什么时候传址,什么时候传值?
Java的八种基本类型都是直接传值的,其他引用类型是地址传递。
如何证明Java传递的是值或是地址?
使用System.out.println() 打印 如果打印结果是地址那就是传址如果是具体值那就是传值。(这样说有点偏颇,但可以这样大体判别,其中String是个例外)
为什么除String外的引用类型直接打印都是地址,而String是个例外呢?
所有的引用数据类型都会继承Object,所有的打印都会走Object 的toString()方法,而String其实是一个char[],它自己重写了toString()方法。这不是重点,重点是打印他不会走toString方法,最终走的是getChars()方法。所以会打印值,而其他引用类型打印地址。
String的toString()方法返回的是this,为什么可以打印出字符串呢?
打印String不会走toString方法,最终走的是getChars()方法。所以会打印值。
传值与传址有什么不同吗?
有的,传址的如果对象的值被改变那其他的对该对象的引用的值会同步的改变。而传值的修改对象的值,是不会对其他对象造成影响的,或者说无法传递改变。(String又是一个例外,它虽然传址,但缺无法传递修改)
为什么String传递的值无法被修改呢?
String是一个final 修饰的最终类,它的很多方法都被final修饰,final不可变,但这不是传递值无法被改变的直接原因;真相是在修改String时创建了新的引用。也就是说引用发生了改变。(当你试图“改变”一个String时,你实际上是在创建一个新的String)
在Java中String进行参数传递传递的是对象的引用吗?
是的,String在传递的时候传递的是对象的引用,但是在对对象修改时传递的这个引用会发生改变(也就是创建了一个新的引用代替了原有的引用,而原本的引用没有发生改变。基于如此,可以推测:例如:String a="aa"; String b=a; String c=b; a="bb"; 当给a赋值更改为“bb”时,b、c都不会受到影响也就是还是第一次a的值 “aa”。这就是String 赋值引起的。),所以它无法进行传递修改。
String 赋值都进行了什么操作?
String赋值的方式有两种,一种是直接赋值字面量,另一种是new一个新的对象。在赋值字面量的时候 jvm 直接把字符串放入到了常量池中(jdk7及以后常量池位于堆中,也就是说直接进行字面量赋值,只会产生一个对象。当使用new 的形式赋值时如果使用字面量赋值(更准确的说是在编译期可以确定的时候,会直接放入字符串常量池,运行时确定的无法自动放入常量池,需要手动调用intern()),那么字面量会放入字符串常量池中(会先判断是否已经存在),后jvm 会在堆中创建一个String 对象。如果没有字面量出现,那么不会放入常量池中。可以手动调用intern()方法放入常量池中)。
String 常量池中存放的是引用还是对象?
jdk7后即有引用也有常量。
是所用的final 修饰的类都无法进行传址修改吗?
被final修饰的类是没有办法被继承,被修饰的方法无法被重写,被修饰的变量无法被修改。所以这个问题有点不对劲,如果你要修改的值被final修饰,那是无法改变的,只是类被final修饰,是可以传址修改的。
String 使用+拼接字符串的底层是什么?
String使用+拼接字符串时,是使用StringBuilder进行拼接的。StringBuilder 使用数组扩容原理进行字符串的拼接。每次扩容1倍+2。当使用+是每次都要new 一个StringBuilder ,然后要toString()创建new String(),消耗很大。当拼接很少的字符串时使用String.concat(),效果更好。(具体原因是在源码中String.concat() 不进行拷贝保护,在原数组上扩容直接进行赋值拼接。而sb会进行多次的扩容,有拷贝保护)