1.String
String是一个用final修饰的类,该类中存在一个final修饰的 ‘char[]’ 数组用于保存字符串,String的部分源码如下:
所以String类第一次赋值后的不可再改变,如需要修改会重新创建一个新的Sting对象进行赋值,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String(建议使用StringBuilder),因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
String 常用方法:
String s1 = "";
// 执行完for循环后相当于创建了20个对象(10个s1和i)会导致jvm的垃圾回收器频繁工作从而降低系统的性能
for (int i = 0; i < 10; i++) {
s1 = s1 + i;
}
System.out.println(s1);
// 如果字符串以prefix开始,则返回true
System.out.println(s1.startsWith("prefix")); // false
// 如果字符串以suffix结尾,则返回true
System.out.println(s1.endsWith("suffix")); // false
// 返回一个新的字符串,即创建新的String对象
String s2 = s1.substring(2); // 从索引为2起一直等到结尾
String s3 = s1.substring(2, 5);// 从索引为2起一直到索引5(不包括索引为5的字符)
System.out.println(s2);
System.out.println(s3);
String s4 = "abcDEF";
System.out.println(s4.toLowerCase()); // 大写改小写
System.out.println(s4.toUpperCase()); // 小写改大写
执行结果:
String的注意点:
public class TestStringAndStringBuilderAndStringBuffer {
public static void main(String[] args) {
String s5 = "a" + "b"; // 编译器做了优化,直接在编译的时候进行字符串的拼接,相当于s5 = "ab";
String s6 = "ab";
System.out.println(s5 == s6); // true
String s7 = "a";
/*
* 编译的时候不知道变量中存储的是什么,所以没办法在编译的时候进行优化拼接,
* 只有在程序运行后才确定s8的内容,所以程序会创建一个新的对象
*/
String s8 = s7 + "b";
System.out.println(s6 == s8); // false
/*
* 当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,
* 则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,
* 相当于直接访问的这个常量,不需要在运行时确定。
* 因此在下面的一段代码中,由于变量 s9 被final修饰,因此会被当做编译器常量,
* 所以在使用 s9 的地方会直接将变量 s9 替换为它的值。
* 不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化
*/
final String s9 = "a";
String s10 = s9 + 'b'; // 因为变量 s9 用了final修饰且s9的值在编译时以确定所以等同于 s10 = "a" + "b"
System.out.println(s6 == s10); // true
/*
* 注意:虽然s11也是被final修饰,但在编译时期s11的值是不确定的,这里的getA()只有在运行时才确定值
*/
final String s11 = getA();
String s12 = s11 +"b";
System.out.println(s6 == s12); // false
}
public static String getA() {
return "a";
}
}
执行结果:
StringBuffer和StringBuilder
区别
1、StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,
2、只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全但是效率会更高(常用)。
3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低
StringBuffer和StringBuilder都是继承了AbstractStringBuilder抽象类,其中AbstractStringBuilder抽象类部分源码如下:
因为没有final修饰所以StringBuffer和StringBuilder对象里的内容是可变的,每次进行字符串的修改时不会产生新的对象,在内存使用上要优于String类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入,删除等操作,使用StringBuilder要更加适合一些。代码示例如下
// StringBuilder线程不安全,效率高(一般使用它),StringBuffer线程安全,线程安全
StringBuilder sb1 = new StringBuilder("abc");
System.out.println(Integer.toHexString(sb1.hashCode()));
System.out.println(sb1);
sb1.setCharAt(0, 'A');
System.out.println(Integer.toHexString(sb1.hashCode()));
System.out.println(sb1);
执行结果
StringBuilder与String
直接上代码
/*使用String进行字符串拼接*/
String s = "";
// 本质上使用String进行拼接,但是每次循环都会生成一个String对象
long memory1 = Runtime.getRuntime().freeMemory(); // 获取系统剩余内存空间
long time1 = System.currentTimeMillis(); // 获取系统当前时间
for (int i = 0; i < 1000; i++) {
s = s + i; // 相当于产生了2000 个对象
}
long memory2 = Runtime.getRuntime().freeMemory(); // 获取系统剩余内存空间
long time2 = System.currentTimeMillis(); // 获取系统当前时间
System.out.println("String 占用内存: " + (memory1 - memory2));
System.out.println("String 占用时间: " + (time2 - time1));
/*使用StringBilder进行字符串拼接*/
StringBuilder sb = new StringBuilder("");
// 本质上使用String进行拼接,但是每次循环都会生成一个String对象
long memory3 = Runtime.getRuntime().freeMemory(); // 获取系统剩余内存空间
long time3 = System.currentTimeMillis(); // 获取系统当前时间
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
long memory4 = Runtime.getRuntime().freeMemory(); // 获取系统剩余内存空间
long time4 = System.currentTimeMillis(); // 获取系统当前时间
System.out.println("StringBuilder 占用内存: " + (memory3 - memory4));
System.out.println("StringBuilder 占用时间: " + (time4 - time3));