String
String的基本特性
String:字符串 使用一对””来表示
- String s1 = “haha”
- String s2 = new String(“haha”)
String声明为final 不可被继承
String 实现了Serializable接口 表示字符串是支持序列化
实现了Comparable接口 表示String可以比较大小
JDK1.8 以前内部定义了 final char[] value用于存储字符串数据
JDK1.9改为byte[]
1 2 3 4
| public final class string implements java.io.Serializable,Comparable<String>,CharSequence{ @Stable private final byte[] value; }
|
相关的类也同样改变
String: 代表不可变的字符序列 简称不可变性
通过字面量的方式 给一个字符串赋值 此时的字符串值声明在字符串常量池中
字符串常量池中是不会存储相同内容的字符串的
String的String Pool是一个固定大小的Hashtable,默认值大小长度是1009.如果放进String Pool的String很多,就会造成Hash冲突严重,导致链表会很长,而链表长了直接造成的影响就是调用String.intern时性能会大幅下降
使用-XX:StringTableSize可设置StringTable的长度
在Jdk6中StringTable是固定的,就是1009的长度
JDK7中,StringTable的长度是60013
JDK8开始,StringTable的长度1009是可设置的最小值
Java语言中有8中基本数据类型和一种比较特殊的类型String.这些类型为了使它们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念
常量池就类似于Java系统基本提供的缓存.8种基本类型的常量都是系统协调的
String类型的常量池比较特殊.它的主要使用方法有两种
- 直接使用双引号声明出来的String对象会直接存储在常量池中
- 使用String的intern()方法
Java6之前 字符串常量池放在永久代
Java7调整到Java堆中
String的基本操作
字符串拼接
- 常量与常量的拼接结果在常量池,原理是编译期优化
- 常量池中不会存在相同内容的常量
- 只要有一个是变量,结果就在堆中.变量拼接的原理是StringBuilder
- 如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void test2(){ String s1 = "javaEE"; String s2 = "hadoop";
String s3 = "javaEEhadoop"; String s4 = "javaEE" + "hadoop"; String s5 = s1 + "hadoop"; String s6 = "javaEE" + s2; String s7 = s1 + s2;
System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7); System.out.println(s5 == s6); System.out.println(s5 == s7); System.out.println(s6 == s7); String s8 = s6.intern(); System.out.println(s3 == s8); }
|
拼接的底层操作原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void test3(){ String s1 = "a"; String s2 = "b"; String s3 = "ab";
String s4 = s1 + s2; System.out.println(s3 == s4); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
@Test public void test4(){ final String s1 = "a"; final String s2 = "b"; String s3 = "ab"; String s4 = s1 + s2; System.out.println(s3 == s4); }
|
拼接操作 append 效率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
@Test public void test6(){
long start = System.currentTimeMillis();
method2(100000);
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start)); }
public void method1(int highLevel){ String src = ""; for(int i = 0;i < highLevel;i++){ src = src + "a"; }
}
public void method2(int highLevel){ StringBuilder src = new StringBuilder(); for (int i = 0; i < highLevel; i++) { src.append("a"); }
} }
|
intern()方法
如果字符串常量池中没有对应data的字符串的话 则在字符串常量池中生成

1 2 3 4 5 6 7 8 9 10 11
| /** * 如何保证变量s指向的是字符串常量池中的数据呢? * 有两种方式: * 方式一: String s = "shkstart";//字面量定义的方式 * 方式二: 调用intern() * String s = new String("shkstart").intern(); * String s = new StringBuilder("shkstart").toString().intern(); * * @author shkstart shkstart@126.com * @create 2020 18:49 */
|
面试题


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public class StringNewTest { public static void main(String[] args) {
String str = new String("a") + new String("b"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
public class StringIntern { public static void main(String[] args) {
String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2);
String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4); }
}
|
总结String的intern()使用
- jdk6中 将这个字符串对象尝试 放入串池
- 如果串池中有,不放入,返回已经有的串池中对象的地址
- 如果没有,对象复制一份,放入串池,并返回串池中的对象地址
- jdk7起,将这个字符串对象尝试 放入串池
- 如果串池中有,不放入,返回已经有的串池中对象的地址
- 如果没有,则把对象的引用地址复制一份,放入串池,返回串池中的引用地址
Intern()空间效率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import java.util.Random;
public class StringIntern2 { static final int MAX_COUNT = 1000 * 10000; static final String[] arr = new String[MAX_COUNT];
public static void main(String[] args) { Integer[] data = new Integer[]{1,2,3,4,5,6,7,8,9,10};
long start = System.currentTimeMillis(); for (int i = 0; i < MAX_COUNT; i++) {
arr[i] = new String(String.valueOf(data[i % data.length])).intern();
} long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start));
try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } System.gc(); } }
|
StringTable垃圾回收测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
public class StringGCTest { public static void main(String[] args) { for (int j = 0; j < 100000; j++) { String.valueOf(j).intern(); } } }
|
G1中的String去重操作


