1.String 类型不可改变
String类型是public final class String,而其实现主要是private final char value[],
这里 final 修饰基本数据类型,不可被更改,
修饰引用数据类型不可指向其他对象,
修饰方法,该方法不可被重写,
修饰类,不可被继承。
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
s1 = "def";
System.out.println(s1 == s2);
System.out.println(s1);
System.out.println(s2);
}
第一个问题 为什么String不可变,s1却可以被重新赋值?
主要原因还是String里的final修饰的数组不可指向其他对象,而String类只是不可被继承。仿照String自己实现OOO类。
import java.util.Arrays;
public final class OOO {
private final char value[];
public OOO(char[] value) {
this.value = value;
}
@Override
public String toString() {
return "OOO{" +
"value=" + Arrays.toString(value) +
'}';
}
}
String代表不可变的字符序列
当字符串重新赋值,需要重写指定内存区域赋值,不能使用原有的value进行赋值
当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能对使用原有的value进行赋值
当调用String的replace方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
一道面试题
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.println(ex.str);//good
System.out.println(ex.ch);//best
}
}
这里char ch[]是一定会变化的,因为Java的值传递。
对于基本类型 ,赋值运算符会直接改变变量的值,原来的值被覆盖。 对于引用类型,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变。
这里ch[]是引用数据类型,所以是一定会改变的,但如果是下面这样ch[]也是不会改变的。
也就是说 change 方法的参数拷贝的是 ch[](实参)的地址,因此,它和 ch 指向的是同一个数组对象。这也就说明了为什么方法内部对形参的修改会影响到实参。
今天在看Java设计模式时,在享元模式中,作者说了这样一段
JDK类库中的String类使用了享元模式
public static void main(String[] args) { String a = "abcd"; String b = "abcd"; String c = "ab" + "cd"; String d = "ab"; d += "cd"; System.out.println(a == b); //true System.out.println(a == c); //true System.out.println(a == d); //false b += "e"; d = d.intern(); System.out.println(a == d); //true System.out.println(a == b); //false }
Java String类这种在修改享元对象时先将原有对象复制一份,然后在新对象上实现修改操作的机制叫做“Copy On Write”。
这也就对应了
- 常量与常量的拼接结果在常量池,原理是编译期优化(a==c)
- 常量池中不会存在相同内容的变量(a==b)
- 拼接前后,只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder(第一个a==d)
- 如果拼接的结果调用intern()方法,根据该字符串是否在常量池中存在,分为:
- 如果存在,则返回字符串在常量池中的地址(第二个a==d)
- 如果字符串常量池中不存在该字符串,则在常量池中创建一份,并返回此对象的地址