1本文主要探讨String类的不可变原因?
1.1我们首先分析什么是可变不可变数据类型?
答案:
可变数据类型:当该数据类型对应的变量的值发生了变化时,如果它对应的内存地址不发生改变,那么这个数据类型就是 可变数据类型。
不可变数据类型:当该数据类型对应的变量的值发生了变化时,如果它对应的内存地址发生了改变,那么这个数据类型就是 不可变数据类型。
总结:可变数据类型更改值后,内存地址不发生改变。不可变数据类型更改值后,内存地址发生改变。
1.2了解不可变的解释之后,我们可以看一下String的源码来深刻了解。String为什么不可变。
1.2.1题外话关于基本数据类型和引用类型的存储位置
基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的。因为String是一个类,所以Java中的字符串String属于引用数据类型。
栈 :由JVM分配区域,用于保存线程执行的动作和数据引用。栈是一个运行的单位,Java中一个线程就会相应有一个线程栈与之对应。
堆 :由JVM分配的,用于存储对象等数据的区域。
1.双引号括起来的字符串,如:“abc”, "xyz"都是直接存储在“方法区”的“字符串常量池”当中的。
2.new关键字实例化的对象都存在堆当中。
2.对于equals方法、hashcode()、==判断的区别:
==:来说比较两个对象时,实质上是检查对象的内存地址是否相等;
equals:判断的是两个对象地址是否相同。String类重写了该方法,所以判断的是两个字符串类型是否相同。
对于 str1 == str3 表达式,==运算符进行比较时,将检查这两个String变量是否引用了同一个字符串,如果它们引用的是不同的字符串,其返回值为false,而不管其字符串内容是否相等。在这里,str1 和str3 引用的是分开在两处的两个字符串,返回false。若让 str3 = str1,则此时str1和str3引用同一个字符串,返回true。
那么有没有一种方法能让==运算符适用于字符串的比较呢?
答案是有的,用字符串扣留机制能实现 == 与equals产生相同的比较结果。字符串扣留功能确保了两个String对象不能封装同一个字符串,因此所有的String对象均封装惟一的字符串。这就意味着,如果两个String变量引用的字符串相等,那么这两个引用也必定相等。换一种说法就是,如果两个String变量包含不相等的引用,它们引用的字符串也必定不相等。如何安排才能使得所有的String对象封装惟一的字符串呢?这只需要为每一个String对象调用intern方法。
还是上面的例子,在str1 += str2;后加上
str1=str1.intern();
str3=str3.intern();
最后str1 == str3;表达式返回结果为true。这就是字符扣留的功能实现,它能减少用户程序中存储String对象需要的存储量,实现== 替代equals()方法。
使用 equals 比较
equals方法是Object类的一个方法,Java当中所有的类都是继承于Object这个超类。
JDK1.8 Object类equals方法源码如下,即返回结果取决于两个对象的使用==判断结果。
public boolean equals(Object obj) {
return (this == obj);
}
Integer类,一些源码方法:
public int intValue() {
return value;
}
@Override
public int hashCode() {
return Integer.hashCode(value);
}
public static int hashCode(int value) {
return value;
}
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
equals方法比较的是两个对象的值是否相等;
String类
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
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;
}