一道印象深刻的面试题:String参数传递问题
今天小菜去北京某知名公司面试,做了公司的面试题,然后就是轻松的面试。
面试过程中,面试官让我讲讲其中一个题是怎么选的答案,代码大致内容如下:
1 public class StringTest{ 2 public static void main(String[] args){ 3 String str = "s"; 4 5 changeStr(str); 6 7 System.out.println(str); 8 9 } 10 11 private static void changeStr(String old){ 12 old = old + "-change"; 13 } 14 15 }
真实的输出结果应该是s,而小菜则错误的认为是s-change,还跟面试官讲了一堆道理。
事后瞬间感觉学艺不精,羞愧难当。。。。
这个结果为什么s呢?经过查阅资料,原来是String虽然以对象的形式存在,但仍认为是基本数据类型,就像Integer、Double那样,虽然是基本类型的包装类,但仍然认为是基本数据类型。
既然认为是基本数据类型,因此无论在方法中如何操作,都不会影响到外界。
其实,换个角度,也可以发现这样是错误的。
首先,大家都知道字符串是不可改变的,简单理解即可,不做深入讨论。
当我们把字符串当成参数传入方法时,传递的是值,是一个指针,这个指针指向了堆区的真实字符串,因此在方法中可以读到这个字符串,但是仅仅是能读到而已,当我们试图做如下操作时:
str = str.replace(“a”,”b”);
后边的str中保存的是原来的指针,的确是可以读到原来的字符串,然后执行替换操作,但是替换操作执行时,绝对不可能去修改原来的字符串,因为字符串是不可变的,因此只能是在堆区产生一个新的字符串,然后把新字符串的地址(指针)赋给str。
此时str中保存的已经不再是原来的指针,因此读出的内容发生了变化,但不代表原来指针指向的内容发生了变化。
另外,小菜顺便再补充一个知识点。
关于Integer i = 1;和Integer i = new Integer(1);的区别。
Integer i = 1;会直接从常量池中找到1,然后把地址赋给i,这样充分利用常量池,节省内存,注意除了字符串,其他类型的常量池都是有范围的,超范围了失效。
Integer i = new Integer(1);这样写,写多少次,就在内存中创建多少个对象,每个对象里都保存了一个数字1,因此这样是极其浪费内存的,不推荐使用。
常量池是保存在堆中的。
小菜水平有限,不当之处多多包涵!