String
String 被声明final
在 Java 8 中,String 内部使用 char 数组存储数据。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; }
在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final byte[] value; //final 修饰 value 数组初始化之后就不能再引用其它数组。
/** The identifier of the encoding used to encode the bytes in {@code value}. */
private final byte coder;}
1、不可变性
不可变对象是在完全创建后其内部状态保持不变的对象。一旦对象被赋值给变量,我们既不能更新引用,也不能通过任何方式改变内部状态。
String s = "abcd";
s = s.concat("ef"); //在堆中重新创建了一个"abcdef"字符串
一个string对象在内存(堆)中被创建出来,他就无法被修改。而且,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
缓存
JVM中专门开辟了一部分空间来存储Java字符串,那就是字符串池。
两个内容相同的字符串变量,可以从池中指向同一个字符串对象,从而节省了关键的内存资源。
String s = "abcd";
String s2 = s; 指向字符串常量池中同一个字符串对象
用处:可以缓存hashcode hash值只需要进行一次计算
由于字符串对象被广泛地用作数据结构,它们也被广泛地用于哈希实现,如HashMap、HashTable、HashSet等。在对这些散列实现进行操作时,经常调用hashCode()方法。
不可变性保证了字符串的值不会改变。因此,hashCode()方法在String类中被重写,以方便缓存,这样在第一次hashCode()调用期间计算和缓存散列,并从那时起返回相同的值
安全性
线程安全
不可变对象可以在同时运行的多个线程之间共享。它们也是线程安全的,因为如 果线程更改了值,那么将在字符串池中创建一个新的字符串,而不是修改相同的值。
提高性能
字符串不可变,所以可以用字符串池缓存,可以大大节省堆内存。而且还可以提前对hashcode进行缓存,更加高效
2、substring()
jdk6 和 jdk7的区别(内存泄漏问题的解决)
jdk6
当调用substring方法的时候,会创建一个新的string对象,但是这个string的值仍然指向堆中的同一个字符数组。只是头尾指针的指向位置不一样
//JDK 6
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
public String substring(int beginIndex, int endIndex) {
//check boundary
return new String(offset + beginIndex, endIndex - beginIndex, value);
}
存在性能问题 :字符数组一直在被引用,所以无法被回收 可能会造成内存泄漏
解决:x = x.substring(x, y) + "" 生成一个新的字符串
jdk7
substring方法会在堆内存中创建一个新的数组。
//JDK 7
public String(char value[], int offset, int count) {
//check boundary
this.value = Arrays.copyOfRange(value, offset, offset + count);
}
public String substring(int beginIndex, int endIndex) {
//check boundary
int subLen = endIndex - beginIndex;
return new String(value, beginIndex, subLen);
}
其使用new String创建了一个新字符串,避免对老字符串的引用。从而解决了内存泄露问题。
3、字符串常量池
为了减少相同的字符串的重复创建,为了达到节省内存的目的。会单独开辟一块内存,用于保存字符串常量,这个内存区域被叫做字符串常量池。
字符串池化机制:当代码中出现双引号形式(字面量)创建字符串对象时,JVM 会先对这个字符串进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回;否则,创建新的字符串对象,然后将这个引用放入字符串常量池,并返回该引用。
String str = "heyuliang"; //字面量 字符串常量池中没有就创建新对象 有就直接引用
String str = new String("heyuliang");
在JDK 7以前的版本中,字符串常量池是放在永久代中的。
在JDK 7中,将字符串常量池先从永久代中移出,暂时放到了堆内存中。
在JDK 8中,彻底移除了永久代,使用元空间替代了永久代,于是字符串常量池再次从堆内存移动到永久代中
intern:在运行期将字符串内容放置到字符串常量池的办法
在每次赋值的时候使用 String 的 intern 方法,如果常量池中有相同值,就会重复使用该对象,返回对象引用。
4、 字符串拼接的几种方式
常用的字符串拼接方式有六种,分别是使用+、使用concat、使用StringBuilder、使用StringBuffer以及使用StringUtils.join、StringJoiner(jdk8 )
阿里巴巴Java开发手册建议:循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
语法糖:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。
+ :是将String转成了StringBuilder后,使用其append方法进行处理的。
String wechat = "heyuliang";
String introduce = "长安故里";
String hyL= wechat + "," + introduce; //(new StringBuilder()).append(wechat).append(",").append(introduce).toString();
concat: 首先创建了一个字符数组,长度是已有字符串和待拼接字符串的长度之和,再把两个字符串的值复制到新的字符数组中,并使用这个字符数组创建一个新的String对象并返回。
源码:
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
String wechat = "heyuliang";
String introduce = "长安故里";
String hyL= wechat.concat( ",").concat(introduce);
StringBuilder char[] value;字符数组
源码:线程不安全
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuilder wechat = new StringBuilder("heyuliang");
String introduce = "长安故里";
StringBuilder heyuliang= wechat.append(",").append(introduce);
StringBuffer
源码: 线程安全的
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuffer wechat = new StringBuffer("heyuliang");
String introduce = "长安故里";
StringBuffer heyuliang= wechat.append(",").append(introduce);
拼接字符串效率(高到低):StringBuilder StringBuffer concat + StringUtils.join
StringBuffer在StringBuilder的基础上,做了同步处理
StringUtils.join也是使用了StringBuilder,并且其中还是有很多其他操作(适合字符数组和列表拼接)
如果不是在循环体中进行字符串拼接的话,直接使用+就好了。
如果在并发场景中进行字符串拼接的话,要使用StringBuffer来代替StringBuilder。
5、switch (只支持整型int 其它类型也 是通过转化为整型进行操作的 String->hashcode hashcode是int类型)
6、String的长度限制
编译期:String=“” 字面量
字面量要遵守 字符串常量池的规范 不能大于等于 65535

浙公网安备 33010602011771号