一 string如何保证不可变
打开string源码可看到,string类事被final修饰的,被final修饰的类不可被继承;第一行代码定义就是一个final修饰的char数组,首先final修饰的值不可变,由于它是引用类型不可变的只是地址,其实value数组还是可以改变的,如把value[1] = 'a',这个时候整个string还是可变的,但是此时的value数组是被private修饰的,所以外部不可能直接改变value数组,这样也就保证了string不可变;
结论:final保证是value数组不被改变,private保证的是数组元素不被改变,类上的final是保证类不被继承后破坏;这3个底层实现才保证了string的不可变,缺一不可
二 string为什么不可变
1. Java种有一个字符串池的概念(string pool),因为使用string类会比较多,如果没次都去新建一个对象,新分配空间则会浪费资源,所以Java设计了一个字符串池的概念,创建一个字符串会去池中存放,下次有相同的字符串则直接引用,如果string一直改变则无法实现
2. 安全考虑,多线程下不会出现问题;网络传输过程中可以保证内容不被串改,如密码,url地址等
3. 效率提升,值不可变意味着hashcode值不会改变,可以把hash值缓存起来,不用重复计算,这也是Map喜欢把string作为key的原因
三 string常量池(string pool)
字符串常量池主要就是为了提高性能和减少内存的开销,因为string被太多使用;JDK7前是存放在运行时常量池中,JDK7后就把他移到了堆中; 使用字符串常量池,每当我们使用字面量(String s=”1”;)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s(引用s在Java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s(引用s在Java栈中)
四 string stringBuffer 和 stringBuilder
1. string是字符串常量,每次改变string都会新创建一个string对象,把指针指向新的string对象上;而stringBuffer和stringBuilder定义的字符数组没被final修饰,所以长度是可变的
2. string在JVM内部是有优化的,如string s = "hello" + "world",并不是创建了多个string对象不断的重新引用,而是在字节码编译阶段就会优化为string s = "hello world";如果是string h = "hello";string w = "world";string s = h + w;这个时候JVM就会按原有方法进行对象创建
3. 因为stringBuffer和stringBuilder字符数组未被final修饰,所以他的长度是可变的,还可以看到他的数组长度默认为16;stringBuffer和stringBuilder的区别就是stringBuffer方法上都加了synchronized关键字保证数据安全,而stringBuilder则没有加,所以相应的stringBuilder的性能也比stringBuffer高
4. 三者区别
都是final类,都不允许被继承;
String类长度是不可变的,StringBuffer和StringBuilder类长度是可以改变的;
StringBuffer类是线程安全的,StringBuilder不是线程安全的;
5. 在多数情况 StringBuilder > StringBuffer > String 的性能;如果能确保无多线程或多线程下不会有数据安全问题则使用StringBuilder,否则选择StringBuffer,操作少量数据适用String
6. 在 Java 9 之后,String 、StringBuilder 与 StringBuffer 的实现改用 byte 数组存储字符串 private final byte[] value
五 char和string区别
1. 形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的 0 个或若干个字符
2. 含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
3. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (char 在 Java 中占两个字节)
六 string的equals()方法
七 String s = new String("abc")创建了几个对象
正确答案是1个或者2个,实际上是"abc"本身就是文字池中的一个对象,在运行 new String()时,把文字池即pool中的字符串"abc"复制到堆中,并把这个对象的引用交给s,所以创建了两个String对象,一个在pool 中,一个在堆中;如果这个时候pool内已经有abc了那就是创建了一个对象,所以答案是1个或者2个