- 字符串常量池
- 什么是常量池
- String对象的创建
- 直接引用字符串
- 使用new创建
- intern方法
- String创建方法的区别
- 字符串的不可变性
- 为什么String类要设计为不可修改的
- StringBuilder和StringBuffer
- String,StringBuffer,StringBuilder的区别
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false
}
通过之前的知识可以知道s1和s2引用的是同一个对象,而s3和s4不是,但是为什么会这样呢。
在Java中,由于有些数字和字符,字符串需要被频繁的调用,为了更快的运行速度,更节省的内存,Java为基本数据类型和String类提供了常量池,这样的话只要再次调用同样的内容,编译器不会再创建新的内容,而是去常量池拿出上一次创建的内容。
Java中有以下的池:
1.Class文件常量池:每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息
2. 运行时常量池:在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份
3. 字符串常量池
String s1 = "hello";
当使用上面这种方法创建字符串时,在字节码文件加载时,hello就保存在了字符串常量池中,创建时,先在字符串常量池中寻找,找到了就把其引用赋值给s1
使用new创建String s2 = new String("hello");
以下过程了解即可:
-
当用new创建字符串对象时,先在堆上开辟String对象的空间,并把其成员初始化为0,然后将空间首地址压栈。
-
将String对象的首地址拷贝一份到栈顶备用
-
将常量池中的hello字符串引用拷贝到栈顶
-
调用String的构造方法,此时取走栈顶的hello的引用和String的地址
-
用栈顶的元素给s2赋值
总结:
只要用new创建对象,都是唯一的,地址肯定时不一样的
使用常量串创建对象的效率高,占用空间小。
手动将创建的字符串常量入池
public static void main(String[] args) {
char[] ch = new char[]{'a', 'b', 'c'};
String s1 = new String(ch); // s1对象并不在常量池中
//s1.intern(); // s1.intern();调用之后,会将s1对象的引用放入到常量池中
String s2 = "abc"; // "abc" 在常量池中存在了,s2创建时直接用常量池中"abc"的引用
System.out.println(s1 == s2);
}
本来是输出false,但是将注释取消掉,就会输出true
String创建方法的区别-
String str = “hello”
只会开辟一块堆内存空间,保存在字符串常量池中,然后str共享常量池中的String对象 -
String str = new String(“hello”)
会开辟两块堆内存空间,字符串"hello"保存在字符串常量池中,然后用常量池中的String对象给新开辟的String对象赋值。 -
String str = new String(new char[]{‘h’, ‘e’, ‘l’, ‘l’, ‘o’})
现在堆上创建一个String对象,然后利用copyof将重新开辟数组空间,将参数字符串数组中内容拷贝到String对象中
- String类被final修饰,表明该类不能被继承
- value被修饰被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。
因此,所有修改字符串的方法都是创建一个新的字符串
为什么String类要设计为不可修改的- 方便实现字符串对象池
- 线程安全
- 更方便缓存hashcode,作为key可以更高效的保存到HashMap
由于每次修改字符串中的内容都要创建新的对象,因此我们可以使用下面这两个类来实现字符串的修改
StringBuilder和StringBuffer| 方法 | 说明 |
|---|---|
| StringBuffer append(String str) | 在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、double、float、int、long、Object、String、StringBuff的变量 |
| char charAt(int index) | 获取index位置的字符 |
| int length() | 获取字符串的长度 |
| int capacity() | 获取底层保存字符串空间总的大小 |
| void ensureCapacity(int mininmumCapacity) | 扩容 |
| void setCharAt(int index, char ch) | 将index位置的字符设置为ch |
| int indexOf(String str) | 返回str第一次出现的位置 |
| int indexOf(String str, int fromIndex) | 从fromIndex位置开始查找str第一次出现的位置 |
| int lastIndexOf(String str) | 返回最后一次出现str的位置 |
| int lastIndexOf(String str, int fromIndex) | 从fromIndex位置开始找str最后一次出现的位置 |
| StringBuffer insert(int offset, String str) | 在offset位置插入:八种基类类型 & String类型 & Object类型数据 |
| StringBuffer deleteCharAt(int index) | 删除index位置字符 |
| StringBuffer delete(int start, int end) | 删除[start, end)区间内的字符 |
| StringBuffer replace(int start, int end, String str) | 将[start, end)位置的字符替换为str |
| String substring(int start) | 从start开始一直到末尾的字符以String的方式返回 |
| String substring(int start,int end) | 将[start, end)范围内的字符以String的方式返回 |
| StringBuffer reverse() | 反转字符串 |
| 反转字符串 | 将所有字符按照String的方式返回 |
注意:
StringBuilder和String不能相互直接转换,String要想变为StringBuilder,可以调用StringBuilder构造方法,StringBuilder变为String可以调用toString方法
- String不i能被修改,剩下两个可以
- StringBuffer采用同步处理,是线程安全的,而StringBuilder未采用,因此尽量使用StringBuffer



