String是否相等
==判断的是对象的内存起始地址是否相同,equals判断自定义的语义是否相同
- JVM为了提高内存效率,将所有不可变的字符串缓存在常量池中,当有新的不可变的字符串需要创建时,如果常量池中存在相等的字符串就直接将引用指向已有的字符串常量,而不会创建新对象
- new创建的对象存储在堆内存,不可能与常量区的对象具有相同地址
public class Demo {
public static void main(String[] args) throws Exception {
String s = "abc";
String s1 = "abc";
String s2 = "a" + "bc";
final String str1 = "a";
final String str2 = "bc";
String s3 = str1 + str2;
String s4 = new String("abc");
System.out.println(s == s1);
System.out.println(s == s2);
System.out.println(s == s3);
System.out.println(s == s4);
}
} //结果:true true true false
为什么说String不可变
final修饰变量,如果是基本类型那么内容不可变,如果是引用类型那么引用的对象(包括数组)地址不可变,但是对象(数组)的内容是可以改变的
- final只是保证value不会指向其他的数组
- private属性保证了不可以在类外访问数组,也就不能改变其内容
- String内部没有改变value内容的函数,所以String就不可变了
- String声明为final杜绝了通过继承的方法添加新的函数
- 基于数组的构造方法,会拷贝数组元素,从而避免了通过外部引用修改value的情况
如果类库人愿意,只要增加一个类似的setCharAt(index)的接口,String就变成可变的了
private final char value[];
private int hash; // Default to 0
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
String真的不可变吗
- final 只在编译器有效,在运行期间无效,因此可以通过反射改变value引用的对象
- s与str始终具有相同的内存地址,反射改变了s的内容,并没有新创建对象
- s 与 s1对应常量池中的两个对象,所以即便通过反射修改了s的内容,他们两个的内存地址还是不同的
public class Demo {
public static void main(String[] args) throws Exception {
String s = "abc";
String str = s;
String s1 = "bbb";
System.out.println(str == s);
Field f = s.getClass().getDeclaredField("value");
f.setAccessible(true);
f.set(s, new char[]{'b', 'b', 'b'});
System.out.println(str + " " + s);
System.out.println(s == str);
System.out.println(s == s1);
}
} //结果:bbb bbb true false
String的HashCode
s的内容改变了但是hashCode值并没有改变,虽然s与s1的内容是相同的但是他们hashCode值并不相同
- Object的hashCode方法返回的是16进制内存地址,String类重写了hashCode的,hashCode值的计算是基于字符串内容的
- String的hashCode值初始为0,由于String是不可变的,当第一次运行完hashCode方法后String类对HashCode值进行了缓存,下一次在调用时直接返回hash值
public class Demo {
public static void main(String[] args) throws Exception {
String s = "abc";
String s1 = "bbb";
System.out.println(s.hashCode());
Field f = s.getClass().getDeclaredField("value");
f.setAccessible(true);
f.set(s, new char[]{'b', 'b', 'b'});
System.out.println(s + " "+ s1);
System.out.println(s.hashCode() +" " +s1.hashCode());
}
} //结果:96354 bbb bbb 96354 97314
String hashCode的源码
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}