1、String 的基本特性
1.1、String 概述
1、String:字符串,使用一对 “” 引起来表示
1 String s1 = "mogublog" ; // 字面量的定义方式 2 String s2 = new String("moxi"); // new 对象的方式
2、String声明为final的,不可被继承
3、String实现了Serializable接口:表示字符串是支持序列化的。实现了Comparable接口:表示String可以比较大小
4、string在jdk8及以前内部定义了final char[] value用于存储字符串数据。JDK9时改为byte[]
为什么 JDK9 改变了 String 的结构
官方文档:http://openjdk.java.net/jeps/254
为什么改为 byte[] 存储?
-
String类的当前实现将字符存储在char数组中,每个字符使用两个字节(16位)。
-
从许多不同的应用程序收集的数据表明,字符串是堆使用的主要组成部分,而且大多数字符串对象只包含拉丁字符。这些字符只需要一个字节的存储空间,因此这些字符串对象的内部char数组中有一半的空间将不会使用。
-
之前 String 类使用 UTF-16 的 char[] 数组存储,现在改为 byte[] 数组 外加一个编码标志位存储,该编码标志将指定 String 类中 byte[] 数组的编码方式
-
结论:String再也不用char[] 来存储了,改成了byte [] 加上编码标记,节约了一些空间
-
同时基于String的数据结构,例如StringBuffer和StringBuilder也同样做了修改
1 // 之前 2 private final char value[]; 3 // 之后 4 private final byte[] value
1.2、String 的基本特征
String:代表不可变的字符序列。简称:不可变性。
-
当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
-
当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
-
当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
1)当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
- 代码
1 @Test 2 public void test1() { 3 String s1 = "abc";//字面量定义的方式,"abc"存储在字符串常量池中 4 String s2 = "abc"; 5 s1 = "hello"; 6 7 System.out.println(s1 == s2);//判断地址:false 8 9 System.out.println(s1);//hello 10 System.out.println(s2);//abc 11 }
-
字节码指令
-
取字符串 “abc” 时,使用的是同一个符号引用:#2
-
取字符串 “hello” 时,使用的是另一个符号引用:#3
0 ldc #2 <abc> 2 astore_1 3 ldc #2 <abc> 5 astore_2 6 ldc #3 <hello> 8 astore_1 9 getstatic #4 <java/lang/System.out> 12 aload_1 13 aload_2 14 if_acmpne 21 (+7) 17 iconst_1 18 goto 22 (+4) 21 iconst_0 22 invokevirtual #5 <java/io/PrintStream.println> 25 getstatic #4 <java/lang/System.out> 28 aload_1 29 invokevirtual #6 <java/io/PrintStream.println> 32 getstatic #4 <java/lang/System.out> 35 aload_2 36 invokevirtual #6 <java/io/PrintStream.println> 39 return
-
2)当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
- 代码
1 @Test 2 public void test2() { 3 String s1 = "abc"; 4 String s2 = "abc"; 5 s2 += "def"; 6 System.out.println(s2);//abcdef 7 System.out.println(s1);//abc 8 }
- 字节码指令:拼接操作通过 StringBuilder 的 append() 方法完成
0 ldc #2 <abc> 2 astore_1 3 ldc #2 <abc> 5 astore_2 6 new #7 <java/lang/StringBuilder> 9 dup 10 invokespecial #8 <java/lang/StringBuilder.<init>> 13 aload_2 14 invokevirtual #9 <java/lang/StringBuilder.append> 17 ldc #10 <def> 19 invokevirtual #9 <java/lang/StringBuilder.append> 22 invokevirtual #11 <java/lang/StringBuilder.toString> 25 astore_2 26 getstatic #4 <java/lang/System.out> 29 aload_2 30 invokevirtual #6 <java/io/PrintStream.println> 33 getstatic #4 <java/lang/System.out> 36 aload_1 37 invokevirtual #6 <java/io/PrintStream.println> 40 return
3)当调用string的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
- 代码
1 @Test 2 public void test3() { 3 String s1 = "abc"; 4 String s2 = s1.replace('a', 'm'); 5 System.out.println(s1);//abc 6 System.out.println(s2);//mbc 7 }
-
来看看 replace() 方法的源码,new String(buf, true); 后,返回新的 String 对象
View Code1 public String replace(char oldChar, char newChar) { 2 if (oldChar != newChar) { 3 int len = value.length; 4 int i = -1; 5 char[] val = value; /* avoid getfield opcode */ 6 7 while (++i < len) { 8 if (val[i] == oldChar) { 9 break; 10 } 11 } 12 if (i < len) { 13 char buf[] = new char[len]; 14 for (int j = 0; j < i; j++) { 15 buf[j] = val[j]; 16 } 17 while (i < len) { 18 char c = val[i]; 19 buf[i] = (c == oldChar) ? newChar : c; 20 i++; 21 } 22 return new String(buf, true); 23 } 24 } 25 return this; 26 }