文章目录
- 1 String的特性
- 2 String的内存结构
- 1:拼接相关
- 2:new相关
- 3:intern()
1 String的特性
-
java.lang.String
类代表字符串。Java程序中所有的字符串文字(例如"hello"
)都可以看作是实现此类的实例。 -
字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
-
字符串String类型本身是final声明的,意味着我们不能继承String。
-
String对象的字符内容是存储在一个字符数组value[]中的。
"abc"
等效于char[] data={'h','e','l','l','o'}
。
//jdk8中的String源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; //String对象的字符内容是存储在此数组中
/** Cache the hash code for the string */
private int hash; // Default to 0
-
private意味着外面无法直接获取字符数组,而且String没有提供value的get和set方法。
-
final意味着字符数组的引用不可改变,而且String也没有提供方法来修改value数组某个元素值
-
因此字符串的字符数组内容也不可变的,即String代表着不可变的字符序列。即,一旦对字符串进行修改,就会产生新对象。
-
JDK9只有,底层使用byte[]数组。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
}
- Java 语言提供对字符串串联符号(“+”)以及将其他对象转换为字符串的特殊支持(toString()方法)。
2 String的内存结构
因为字符串对象设计为不可变,那么所以字符串有常量池来保存很多常量对象。
JDK6中,字符串常量池在方法区。JDK7开始,就移到堆空间,直到JDK17版本。
1:拼接相关
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
// 内存中只有一个"hello"对象被创建,同时被s1和s2共享。
- 建立对象时,相同的字符串的时候,会存储在同一个常量池里面。
Person p1 = new Person();
p1.name = "Tom";
Person p2 = new Person();
p2.name = "Tom";
System.out.println(p1.name.equals( p2.name)); // true
System.out.println(p1.name == p2.name); //ture
System.out.println(p1.name == "Tom"); //ture
- 字符串常量存储在字符串常量池,目的是共享。
- 字符串非常量对象存储在堆中。
2:new相关
String s1 = "javaEE";
String s2 = "javaEE";
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
练习:String str2 = new String(“hello”); 在内存中创建了几个对象?(2个)
3:intern()
- String s1 = “a”;**
说明:在字符串常量池中创建了一个字面量为"a"的字符串。
- s1 = s1 + “b”;
说明:实际上原来的“a”字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s1+“b”(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。
- String s2 = “ab”;
说明:直接在字符串常量池中创建一个字面量为"ab"的字符串。
- String s3 = “a” + “b”;
说明:s3指向字符串常量池中已经创建的"ab"的字符串。
- String s4 = s1.intern();
说明:堆空间的s1对象在调用intern()之后,会将常量池中已经存在的"ab"字符串赋值给s4。
String s1 = "hello";
String s2 = "world";
String s3 = "hello" + "world";
String s4 = s1 + "world";
String s5 = s1 + s2;
String s6 = (s1 + s2).intern();
System.out.println(s3 == s4);//fasle
System.out.println(s3 == s5);//false
System.out.println(s4 == s5);//false
System.out.println(s3 == s6);//true
结论:
(1)常量+常量:结果是常量池。且常量池中不会存在相同内容的常量。
(2)常量与变量 或 变量与变量:结果在堆中
(3)拼接后调用intern方法:返回值在常量池中
练习:
1.
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = s1 + "world";//s4字符串内容也helloworld,s1是变量,"world"常量,变量 + 常量的结果在堆中
String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是变量,变量 + 变量的结果在堆中
String s6 = "hello" + "world";//常量+ 常量 结果在常量池中,因为编译期间就可以确定结果
System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//true
final String s1 = "hello";
final String s2 = "world";
String s3 = "helloworld";
String s4 = s1 + "world";//s4字符串内容也helloworld,s1是常量,"world"常量,常量+常量结果在常量池中
String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是常量,常量+ 常量 结果在常量池中
String s6 = "hello" + "world";//常量+ 常量 结果在常量池中,因为编译期间就可以确定结果
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//true
System.out.println(s3 == s6);//true
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = (s1 + "world").intern();//把拼接的结果放到常量池中
String s5 = (s1 + s2).intern();
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//true
public class TestString {
public static void main(String[] args) {
String str = "hello";
String str2 = "world";
String str3 ="helloworld";
String str4 = "hello".concat("world"); // 这个也是new一个对象,通过源码可以看出来
String str5 = "hello"+"world";
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
}
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);// 传入基本数据类型,是形参,传入引用数据类型,则是实参
System.out.print(ex.str );//good
System.out.println(ex.ch);// best
}
}