String类:
- String中的“+”号:“+”是用来拼接两个字符串的,底层实现时是调用了StringBuilder类的append方法进行拼接的。如下所示:
String str1="aaa";
String str2="bbb";
System.out.println(str1+str2);
//其中valueof方法保证用字符串来创建StringBuilder类,与str1+str2等价
System.out.println(new StringBuilder(String.valueOf(str1)).append(str2));
当“+”号两边时常量时,在编译的时候就计算出字符串的值。而不会在运行时创建临时的StringBuilder对象来完成字符串的连接。
如果在循环中对String对象进行连接,应使用StringBuilder来代替“+”,这样可以提高性能。
public static void main(String[] args) {
new Thread(new Runnable() {
@Override public void run() {
String string1=null;
long start;
long end;
start=System.nanoTime();
for(int i=0;i<10000;i++)
{
string1+='a';
}
end=System.nanoTime();
System.out.println("+号所用的时间:"+(end-start));
}
}).start();
new Thread(new Runnable() {
@Override public void run() {
String string2=null;
long start;
long end;
start=System.nanoTime();
StringBuilder stringBuilder=new StringBuilder(10000);
for(int i=0;i<10000;i++)
{
stringBuilder.append('a');
}
string2=stringBuilder.toString();
end=System.nanoTime();
System.out.println("append所用的时间是:"+(end-start));
}
}).start();
}
运行结果如下:
append所用的时间是:454230
+号所用的时间:101082562
由运行结果可得:“+”号在循环中的使用会使性能大大降低。在循环中,应该用append方法来拼接以提高效率。
2.不可修改的String对象:String类是final类型的,因此无法继承。并且所有的成员变量都是私有的,而且没有提供修改私有成员的共有方法。因此对String对象的操作都没有修改当前对象,而是创建了新的对象。例如:toUpperCase方法、concat方法、substring方法、replace方法、spilt方法等。这样做的好处是可以实现资源共享 ,在多线程操作时,可以将其认为是不变的,而不用担心其他线程会对其进行修改。这样就没有必要使用线程同步了,简单方便。
3.String对象的内部是用一个字符数组来维护序列的。数组的长度可用byte、short、char、int来指定,因此数组最大长度为2147483647。但是在实际中所取得值比理论值要小。因为Java对象都是分配在堆上的,因此这个长度还和堆的大小有关系。可用java -Xmx1G chapter.Length2 来设置对的大小为1G。下面的代码来探索最大的长度。
public static void main(String[] args) {
int high=Integer.MAX_VALUE;
int low=0;
int mid=(high+low)/2;
for(int i=0;i<32;i++)
{
try {
char[] ch=new char[mid];
//申请成功
System.out.println("第"+i+"次申请成功,长度为:"+mid);
low=mid+1;
mid=(low+high)/2;
} catch (OutOfMemoryError e) {
System.out.println("第"+i+"次申请失败");
high=mid-1;
mid=(low+high)/2;
}
}
}
运行结果最后一行:
第31次申请成功,长度为:347812940
4.equals方法和“==”的对比:equals方法是定义在Object方法中的,Object中的equals的内部就是使用"==”来进行比较的。String类重写了equals方法,其内部比较的时两个字符串的内容。StringBuilder和StringBuffer类没有重写equals方法。
String string1="abc";
String string2="abc";
StringBuilder stringBuilder1=new StringBuilder("Builder");
StringBuilder stringBuilder2=new StringBuilder("Builder");
StringBuffer stringBuffer1=new StringBuffer("Buffer");
StringBuffer stringBuffer2=new StringBuffer("Buffer");
System.out.println(string1.equals(string2));
System.out.println(stringBuilder1.equals(stringBuilder2));//没有重写,调用的时Object类中的方法
System.out.println(stringBuffer1.equals(stringBuffer2));
运行结果为:
true
false
false
重写equals的规则:
- 自己和自己equals返回true;
- x.equals(y)返回true时,y.equals(x)也返回true;
- x.equals(y)返回true,y.equals(z)也返回true时,x.equals(z)也返回true;
- x和y的值没有改变时,equals返回的值始终是true或始终是false;
- x.equals(null)返回false。
hashcode在添加键值对的时候,会根据key的hashcode计算下标。如果key是自定义的一个类。那么key类除了要重写equals方法外,还要重写hashCode方法。在map中相等的key是指equals返回true,hashCode相等。如果仅仅只是equals返回true,那么get(key1)和get(key2)所取到的值可能不一样。
- String常量池:因为String是不可改变的类,一经创建,不能修改。由于这个特性,对象可以自由的由多个线程所共享,不必担心会被修改。因此就没有必要去创建重复的String对象。常量池能自动将String字面常量加入其中。当程序中出现String字面常量的时候,会搜索常量池中是否存在该String对象(用String类中的equals方法来判断)。若不存在,将其加入常量池中,并返回该对象。如果存在,直接返回该对象。
String string1=new String("abc");
String string2=new String("abc");
String string3="abc";
String string4="abc";
System.out.println("string1==string2:"+(string1==string2));
System.out.println("string3==string4:"+(string3==string4));
System.out.println("string1==string3:"+(string1==string3));
运行结果:
tring1==string2:false
string3==string4:true
string1==string3:false
除了字面常量,在编译时期就能确定其值的String类型的表达式也可以放到常量池中。final修饰的变量可以当做常量。
6.intern方法:可以将string对象加入常量池中。常量或者常量表达式加入到常量池也是通过调用intern方法实现。