文章目录
- 字符串常量池
- 驻留intern 方法
- String重载“+”,“+=”
- String a = "1"+"2" 创建了几个对象?
- String a = new String ("1");创建了几个对象?
- String s = new String("1");String s2 = "1";System.out.println(s== s2);System.out.println(s.intern()== s);System.out.println(s.intern()== s2);
- String,StringBuilder,StringBuffer区别
- 写一个方法来判断一个String是否是回文(顺读和倒读都一样的词)
- 为什么我们在使用HashMap的时候总是用String做key
- 没事我来瞅瞅
参考文章:
美团技术团队:深入解析String#intern
https://www.zhihu.com/question/29884421
https://javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html
字符串常量池
字符串常量池是什么?
在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个HashTable,默认值大小长度是1009;字符串常量由一个一个字符组成,放在了StringTable上。
- 在JDK6.0,StringTable长度是固定了1009,如果放入String Pool中的String非常多,就会造成hash冲突,导致链表过长,当调String#intern()时会需要到链表上一个一个找,从而导致性能大幅度下降
- JDK7.0,StringTable的长度可以通过参数指定:
-XX:StringTableSize=66666
字符串常量池存储位置
- JDK6:常量池存储在永久代中
- JDK7:常量池存储在堆中。
字符串常量池存储着什么
个人认同:
字符串常量池:存放的是字符串的引用地址。
驻留intern 方法
当对象s调用intern(),首先查找字符串常量池中引用的字符串的值,看有没有和s的值相等;如果有相等,则返回与s值相等的字符串的引用;如果找不到,则这把s的引用存入字符串常量池中,并返回。
String重载“+”,“+=”
Java对String的+操作符进行了重载。
例子:
public class Demo {
public static void main(String[] args) {
String a = "1";
String b = a + "2";
}
}
- 编译:javac Demo.java
- 反编译:javap -c Demo
public class basis.wei.Demo {
public basis.wei.Demo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 1
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String 2
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: return
}
编译器自动创建StringBuilder对象, 并调用append()方法进行拼接,最后toString()生成结果。
String a = “1”+“2” 创建了几个对象?
答案: 如果常量池没"12",则创建1个;如果常量池有"12",则创建0个,返回该对象引用
原因: 编译时,会自动优化,进行拼接成“12”;
证据:
public class Demo {
public static void main(String[] args) {
String s = "1"+"2";
}
}
- javac Demo.java #编译
- javap -verbose Demo #查看 常量池
public class basis.wei.Demo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#13 // java/lang/Object."<init>":()V
#2 = String #14 // 12
#3 = Class #15 // basis/wei/Demo
#4 = Class #16 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 main
#10 = Utf8 ([Ljava/lang/String;)V
#11 = Utf8 SourceFile
#12 = Utf8 Demo.java
#13 = NameAndType #5:#6 // "<init>":()V
#14 = Utf8 12
#15 = Utf8 basis/wei/Demo
#16 = Utf8 java/lang/Object
{
public basis.wei.Demo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 7: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=1
0: ldc #2 // String 12
2: astore_1
3: return
LineNumberTable:
line 10: 0
line 11: 3
}
编译后只有一个“12”。
String a = new String (“1”);创建了几个对象?
参考文章:请别再拿“String s = new String(“xyz”);创建了多少个String实例”来面试了吧
答案: 两个,一个是字符串字面量"1"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例
String s = new String(“1”);String s2 = “1”;System.out.println(s== s2);System.out.println(s.intern()== s);System.out.println(s.intern()== s2);
本题参考:String#intern
public static void main(String[] args) {
String s = new String("1");
String s2 = "1";
System.out.println(s== s2);
System.out.println(s.intern()== s);
System.out.println(s.intern()== s2);
}
主要考查intern的使用,以及字符串常量池的概念;
JDK8环境,答案
false
false
true
图解
s== s2 为false;可以很好理解,s1和s2的引用地址不同。
s.intern()== s为false,s.intern()在String Pool中找到有“1”与s的值相等,所以返回找到的值相等的引用值,但与s的引用并不相等;所以false
第三个要先了解以下内容
在创建字符串对象时,会先到字符串常量池查到字符串引用的值,看有没有值相等,如果有值相等,则将查到字符串的引用赋值给变量;如果查不到则在堆上创建实例,并将实例的引用存储在String Table,并将引用赋值给变量。
String s2 = “1”; “1” 可以在String pool找到引用对象的值相等;所以直接将引用赋值给s2
所以上图表示的s1的引用和常量池中的"1"的引用指向同一地址。so,s.intern()返回的是常量池中“1”的引用 与s1的引用相等。
如果以上理解了,在看看这题
public static void main(String[] args) {
String s = new String("1")+"1";
System.out.println(s.intern()== s);
String s2 = "11";
System.out.println(s.intern()== s2);
}
jdk 8答案
true
true
第一次s.intern() 发现String Pool中的引用的值没有和11相等的,就将“11”的引用驻留在了String Pool;并将引用返回。s.intern()== s ;等于true。
第二个true;直接将“11”的引用返回给s2,如图。
String,StringBuilder,StringBuffer区别
String | StringBuilder | StringBuffer | |
---|---|---|---|
储存结构 | private final char value[]; | char[] value; | private transient char[] toStringCache; |
线程是安全性 | 安全 | 不安全 | 安全,synchronized修饰方法 |
运算 | 1. 编译器优化 2. “+”,“+=” 重载 | 效率高 | 效率低 |
使用场景 | 操作少量数据 | 单线程,操作大量数据 | 多线程,操作大量数据 |
写一个方法来判断一个String是否是回文(顺读和倒读都一样的词)
/**
* 判断是否是回文
* @param s
* @return
*/
public static boolean isPalindromeString(String s){
StringBuilder stringBuilder = new StringBuilder(s);
return stringBuilder.reverse().toString().equals(s);
}
为什么我们在使用HashMap的时候总是用String做key
我们知道HashMap的key只能是引用类型,而在进行路由寻址时,是根据key的hashcode来计算的。
而String有以下特点。
- String 有个hash属性,用来储存hashcode,可避免重复计算hashCode。
- String的值是根据值来计算的,所以String 作为key,如果他的值相等,那么hashcode也一定相等;这样的能减少很多实际工作中可能会出现的麻烦。
参考文章:为什么 HashMap 常用 String 对象作 key
//缓存hashcode
/** Cache the hash code for the string */
private int hash; // Default to 0
// 根据值计算hashcode
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
没事我来瞅瞅
如果有什么地方不对,麻烦请指出!谢谢!!