很多人都会答错的一道关于String的题目,究竟有什么难度?
我们一起来看一道关于String的面试题,准确说是改编的面试题!
准备好啦?在放大招之前先来一个小招式
String s1 = new String("hello");
String s2 = new String("hello");
来,问大家,在这一小段代码中一共创建了几个对象呢?
其实这道题并不难,考察的是关于String的重点知识点常量池和字符串优化。
这个是经典题了,答案是3个,因为这里的“hello”会在字符串常量池中创建一个,然后在堆中创建2个,所以这道题的答案一共是创建了三个对象。
不过我觉得更加严谨的一点说法是应该判断在字符串常量池中是否已经存在“hello”这个字符串,不过从这道题的本意来看,事先在字符串常量池中是不存在这个“hello”这个字符串的。
再来放一个小招式
public class TestString {
public static void main(String[] args) { String s1 = "Java";
String s2 = "从0到1";
String s3 = s1 + s2;
String s4 = "Java从0到1"; System.out.println(s3 == s4); }
}
对于字符串常量池的位置,字符串常量池在jdk1.6及之前在方法区中,但是在jdk1.7及以后就放在了堆中,我们现在一般很少使用jdk1.6及之前的版本,因此现在讨论所说的字符串常量池除非特别说明jdk版本,否则默认是在堆中。
另外对于字符串常量池中存放的是字符串对象,比如String a = “abc”那么在字符串常量池中就存放着这个abc,而这个abc则是一个字符串对象。
哈哈哈,是不是已经开始懵了!
通过上面那两题,我们来悄悄的总结一下
1.编译阶段确定的字符串才会被放到字符串常量池中
2.字符串常量池在堆中
把第二段代码细讲一下吧
我们知道s1和s2还有s4都会在字符串常量池中,这已经有三个对象了,关键的分析点在这行代码上
String s3 = s1 + s2;
对于s3无法在编译阶段确定下来,所以无论最后的字符串是什么都不会存在字符串常量池中,那么存在哪呢?因为对象创建在堆中,而字符串常量池也在堆中,既然不在字符串常量池中那就是在字符串常量池之外的堆中了,而s3最终的字符串对象是“Java从0到1”,那么也就是说这个字符串在常量池之外的堆中,这是一个对象了,加上之前的三个,这里一共四个了,但是我们还要分析这个s3是怎么来的,是通过s1和s2相加得到的,因为这个s3最终是在常量池之外的堆中形成的,而s1和s2都是在常量池中,因此会将s1和s2拷贝一份到字符串常量池之外的堆中来形成s3,我们看下面的一张图来加深理解
所以,这道题的答案是一共创建了6个对象,这道题经常作为公司的面试题,为此还有不少人被绊倒呢
大魔王还找了另外一题,同样也是关于String的面试题
程序如下:
public class test8 { String str = new String("good");
char[] ch = { 'a', 'b', 'c' }; public static void main(String args[]){
test8 ex=new test8();
ex.change(ex.str,ex.ch);
System.out.print(ex.str+" and ");
for(int i=0;i<ex.ch.length;i++) {
System.out.println(ex.ch[i]);
}
}
public void change(String str,char ch[]){
str="test ok";
ch[0]='g';
} }
解析:
这是java参数传递方式的问题,Java参数,不管是原始类型还是引用类型,传递的都是副本。
如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值。如果在函数中改变了副本的 值不会改变原始的值。
如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。
a.传递值的数据类型:八种基本数据类型和String(这样理解可以,但是事实上String也是传递的地址,只是string对象和其他对 象是不同的,string对象是不能被改变的,内容改变就会产生新对象。那么StringBuffer就可以了,但只是改变其内容。不能改变外部变量所指 向的内存地址)。
b.传递地址值的数据类型:除String以外的所有复合数据类型,包括数组、类和接口
ps: String 是 final 的。所以值是不变的。 函数中String对象引用的副本指向了另外一个新String对象,而数组对象引用的副本没有改变,而是改变对象中数据的内容.