首先,我们要先了解常量池的概念,常量池在java中用于保存在编译期已确定的,存在于已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = “str”这种申明方式;当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。
问题一:
String是最基本的数据类型吗?
基本数据类型包括byte、int、char、long、float、double、boolean和short。
java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类,或者用StringBuffer类。前者效率高,后者支持线程同步。
问题二:
final String str1 = "aaa";
final String str2 = "bbb";
String str3 = "aaabbb";
String str4 = str1 + str2;
System.out.println(str3 == str4);//true
解析:
因为str1与str2都定义成了常量,在编译时就已经确定,编译时就会将变量替换为常量,等同于 str4 = “aaa”+”bbb”,因此不产生新的对象。
问题三:
String s = new String("abc");
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s == s1);
System.out.println(s == s2);
System.out.println(s1 == s2);
解析:
第一句执行后内存中有两个对象,而不是一个。一个由new String(“abc”)中的”abc”在字符串常量池生成一个值为”abc”的对象;第二个由new在堆里产生一个值为”abc”的对象,该对象完全是String Pool里的”abc”的一个拷贝。变量s最后指向堆中产生的”abc”对象;
第二句执行时,变量s1先去String Pool找是否有值为”abc”的对象,很显然在上一步中java已经在String Pool里生成一个”abc”对象了,所以s1直接指向String Pool中的这个”abc”;
第三句中又有一个new,首先会去字符串常量池中找“abc”,若能找到则直接获取地址,不再创建新的常量。在java中凡遇到new时,都会在堆里产生一个新的对象。因此,该句执行后堆里又多了一个”abc”对象,这与执行第一句后生成的”abc”是两个不同的对象,s2最后指向这个新生成的对象。
因此,执行后面的打印语句的结果是三个false 。
问题四:
String hello = "hello";
String hel = "hel";
String lo = "lo";
System.out.println(hello == "hel" + "lo");
System.out.println(hello == "hel" + lo);
解析:
前三句在String Pool里分别产生“hello”、“hel”、“lo”三个常量字符串对象。
当做第一个加法连接时,+号两边都是常量字符串,”hel” + “lo”在编译时就变成了“hello”,java就会将两者拼起来后到String Pool里找与之相等(用equals)的字符串,若存在则将其地址返回;不存在则在String Pool里新建一个常量对象,其值等于拼接后的字符串,并将其地址返回。
而第二个+号两边有一个是变量,此时,java会在堆里新建一个对象,其值是两字符串拼接后的值,此时返回的地址是堆中新对象的地址。
所以,第一句做+连接后返回String Pool中“hello”的地址,显然与变量hello的地址相等; 第二句返回的是堆中地址,显然与变量hello的地址不等;
问题五:类似于问题四:
public class Test {
public static void main(String[] args) {
final String str1 = "HelloWorld";
final String str2 = "Hello" + "World";
System.out.println(str1 == str2);//true
final String str3 = "Hello" + String.valueOf("World");
System.out.println(str1 == str3);//false
}
}
解析:
str3需要通过valueOf方法调用之后才能确定。而不是在编译时确定。
问题六:
在Java中String是不可变的和无法改变的,有什么好处?
1,可以使用字符串池来存储字符串,提高存储效率。
2,增加安全性,在存储一些敏感信息,如数据库用户名,密码等是,黑客不能改变它的值。java的类加载器加载类时,字符串的不变性可以确保正确的类被加装。
3,由于String是不可变的,它是安全的,在多线程环境下,我们不需要任何同步。