一、String对象的创建
1.创建形式
- String a = “” ——直接赋值
- String b = new String( [String original]… ); ——new
- String c = String.class.newInstance(); ——反射
----------以上仅用一行代码可以实现----------
- clone
- 反序列化
2.以下代码块创建出了几个对象?
String s1 = "abc";
- 1个:(“abc”) - 常量区;
String s1 = new String("abc");
- 2个:(“abc”) - 常量区;( new String(“abc”) ) - 堆内存
所有在代码里面以双引号""创建出来的东西都会创建在常量区
String s1 = "";
String s2 = new String();
String s3 = new String("");
- s1:1个:空字符串("") - 常量区
- s2:2个:空字符串("") - 常量区;( new String() ) - 堆内存
- s3:同s2
String s1 = new String("abc");
s1 += "xyz";//自动转化(StringBuilder和.toString()返回的String看成是一个对象)
-------------------------------
String s2 = new String("abc");
s2 = s2 + "xyz";
- s1两行代码:4个:(“abc”)、(“xyz”) - 常量区;
( new String(“abc”) )、( new String(“abcxyz”) ) - 堆内存 - s2两行代码5个:(“abc”)、(“xyz”) - 常量区;
( new String(“abc”) )、( new StringBuilder() )、( new String(“abcxyz”) ) - 堆内存
二、String的内存性能优化
1.String对象的底层创建
Demo1
String s1 = "abc";
String s2 = new String("abc");
String s3 = s1 + s2;
//System.out.println(s3);该行代码表示使用了s3对象
String s4 = "abc" + "xyz"// " + " 的左右是两个明文
对于 String s3 = s1 + s2 该行代码创建出几个对象的问题:
- 如果不使用s3 ——2个:( new StringBuilder() )(编译优化) 、( new String(“abcabc”) ) - 堆内存
- 如果使用s3 ——1个:( new String(“abcabc”) ) - 堆内存
对于 String s4 = “abc” + “xyz” 该行代码创建出的对象:
- 1个:“abcxyz” - 常量区,编译优化
用 " + " 连接两个(或多个)String对象创建出一个新的String对象而·不用·,其消耗的空间比用了以后还多,因为其新创建了一个占用较多内存的StringBuilder对象
对于用" + "拼接两个String而后使用它时:(即上述 String s3 = s1 + s2 拼接完s3后打印输出s3)
- String s3 = s1 + s2 在 执行时 会变成:String s3 = s1.concat(s2);
即 " + " 在执行时被转化成调用.concat()进行拼接
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);
}
------------------------------------------------------
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
concat做了两次数组的循环遍历(将数组拷贝了两遍),所以其性能很,不推荐使用 " + " 进行字符串拼接
用 " + " 连接两个(或多个)String对象创建出一个新的String对象而后续需要·使用·它时,若需要连续拼接,强烈推荐使用StringBuilder
Demo2
String s1 = new String();
String s2 = new String("");
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
当创建出的String对象仅为空串时,由String源码知,两种构造方法相比较:
- s1 和 s2 的地址(value)相等,均指向空串
- s1.hash == 0,s2多了一个hash的赋值的过程
String类型的对象 hash是一个属性 属性默认值都是0 什么时候调用了hashCode 才会赋值
如果以后我们将String存储在HashMap集合里,用第二种性能会更快,因为在初始化时就已经给hash赋值了,而第一种还要现算hash值
Demo3
String s1 = "hello";
String s2 = new String(s1);//从加载性能上讲推荐使用此方法创建对象
-------------------------------
String s3 = new String("hello");
s2和s3两种方式相比较:
- 在运行过程中,两者没有区别
- 在 编译 过程中,s2的编译速度要远远快于s1的编译速度
new String()时,参数传引用可以自己索引,参数传字符则需串遍历常量区,查看是否有相同的字符串,如果有相同的则使用常量区里的
2.String方法的应用
(1)字符串长度在10个以内,怎么比较两个字符串是否长度相等速度最快?
//字符串长度在10位以内
String s1 = new String("sadfasdsdf");
String s2 = new String("xcvbxcvbx");
//System.out.println(s1.equals(s2));
//System.out.println(s1.hashCode() == s2.hashCode());
equals() 与 hashCode() 方法比较:
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() { //String类重写的hashCode()直接用字符串内容算出一个数字
int h = hash;
if (h == 0 && value.length > 0) { //当hash值为0时才比较
char val[] = value; //String为了方便判断,使其重写的hashCode()与value相关,即用value进行运算
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
- equals() 会使用循环依次去比较,非常慢(如果有字符串有事10个字母,用字符串要比较10次)
- hashCode() 直接返回hash值,只需做1次hash值比较,远高于 equals() 速度
若字符串太长hash值可能重复,即上述比较方法只适用于比较两个比较短的字符串(10个字符串以内hash值基本不会重复)
未完待续…