String类称为不可变字符序列,String类部分源码如下:
char value[ ]表示String产生的对象时字符串数组,final表示常量不可修改
String类拼接字符串优化
public class TestString {
public static void main(String[] args) {
//编译器优化,在编译时直接将字符串进行拼接
String s1 = "hello" + " java"; //相当于s1="hello java"
String s2 = "hello java";
System.out.println(s1 == s2); //true
String s3 = "hello";
String s4 = " java";
//编译时不知道变量中储存的是什么,所以没法在编译时优化
String s5 = s3+s4;
System.out.println(s1 == s5); //false
System.out.println(s1.equals(s5)); //true 字符串比较时使用equals,比较值
}
}
StringBuilder和StringBuffer
StringBuilder被称为可变字符序列,继承自抽象类AbstractStringBuilder类,该抽象类部分源码如下:
可以看到,char[ ] value未用final修饰,故而可变。
public class TestString {
public static void main(String[] args) {
//StringBuilder线程不安全,效率高;StringBuffer线程安全,效率低
StringBuilder sb = new StringBuilder("adasdasdf");
System.out.println(Integer.toHexString(sb.hashCode()));
System.out.println(sb);
sb.setCharAt(2, 'F');
System.out.println(Integer.toHexString(sb.hashCode()));
System.out.println(sb);
}
}
执行结果如下:
可见,使用StringBuilder定义的字符串可变,且字符串内容改变,其内存不变。
【注】StringBuilder线程不安全,效率高;StringBuffer线程安全,效率低。一般情况下不考虑线程安全问题,常用StringBuilder()
StringBuilder和StringBuffer基本用法
public class TestString {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
for(int i=0;i<26;i++){
sb.append((char)('a'+i)); //在已有序列后追加字符
}
System.out.println(sb);
sb.reverse(); //可变字符序列倒序
System.out.println(sb);
sb.setCharAt(3, '啊'); //将索引为3的位置的字符替换为指定字符
System.out.println(sb);
sb.insert(0, '*'); //在指定位置插入字符,原位置字符后移;
//insert调用后return自身,可以继续调用
sb.insert(2, '1').insert(3, '?'); //链式调用
System.out.println(sb);
//删除操作也返回自身,可以链式调用
sb.delete(5, 10); //删除某区间内的字符
sb.deleteCharAt(6); //删除指定位置字符
}
}
可变字符序列和不可变字符序列的使用陷阱(循环累加注意)
在使用循环累加时,一般使用StringBuilder中的append方法进行字符串拼接,提高性能。
public class TestStringBB {
public static void main(String[] args) {
/**使用String进行字符串拼接*/
String str8 = "";
//本质上使用了StringBuilder拼接,但每次循环都会生成一个StringBuilder
long num1 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间
long time1 = System.currentTimeMillis(); //获取当前系统时间
for(int i=0;i<5000;i++){
str8 = str8 + i; //相当于产生10000个对象
}//不适合作为服务器程序,一个用户访问产生10000个对象,容易造成服务器崩溃
long num2 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间
long time2 = System.currentTimeMillis(); //获取当前系统时间
System.out.println("String占用内存:"+(num1-num2));
System.out.println("String花费时间:"+(time2-time1));
/**使用StringBuilder进行字符串拼接*/
StringBuilder sb1 = new StringBuilder();
long num3 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间
long time3 = System.currentTimeMillis(); //获取当前系统时间
for(int i=0;i<5000;i++){
sb1.append(i);
}
long num4 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间
long time4 = System.currentTimeMillis(); //获取当前系统时间
System.out.println("StringBuilder占用内存:"+(num3-num4));
System.out.println("StringBuilder花费时间:"+(time4-time3));
}
}
执行结果如下: