String类在我们开发中使用频率相当高,本篇内容将深入介绍String类的一些特性。
1、String 不可变性
String
类是不可变的,对String
类的每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象。 看下边这段代码:
public class string {
public static void main(String[] argus){
String oldString = "string";
String newString = add123(s);
System.out.println(oldString);
System.out.println(newString);
}
private static String add123(String d){
return d+="123";
}
}
当把oldString
传给add123
方法时,实际传递的是oldString
引用的一个拷贝,而在add123方法内部执行+=操作时,由于String的不可变性,实际上是创建了一个新的String对象,因此oldString
的值不会发生变化。
过程如下图所示:
2、String类为什么是不可变的?
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
}
看string源码可以看出,Java中String类其实就是对字符数组的封装,在Java中,数组也是对象,所以value也只是一个引用,它指向一个真正的数组对象。而这个数组对象使用private final修饰的,也就是初始化之后不可改变。
在执行了String s = “ABCabc”;
这句代码之后,真正的内存布局应该是这样的:
3、 重载+与Stringbuilder
拼接字符串是java中非常常见的操作,“+
”和StringBuilder
都可以拼接字符串,在拼接操作较少的情况下,这两种操作都可以使用,但在拼接字符串操作较多的情形下(比如循环中),+
操作符会影响性能,更推荐使用StringBuilder
。
为什么会这样呢?看看这段代码进行反编译后的结果你就知道了:
public static void main(String[] argus) {
String s = "s";
for (int i = 0; i < 10; i++) {
s += "s";
}
System.out.println(s);
}
“+”操作符拼接字符串实际上也是调用了String Builder方法,从反编译后的结果,可以看到从第8行到34行,执行了循环语句,在每一次循环语句中创建了一个StringBuilder对象(11行),这会生成大量临时对象,造成资源浪费。
4、 字符串常量池
作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能。
字符串常量池的设计:
- JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
- 为字符串开辟一个字符串常量池,类似于缓存区
- 创建字符串常量时,首先检测字符串常量池是否存在该字符串
- 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
- 实现的基础
- 实现该优化的基础是因为字符串是不可变的,可以不用担心数据冲突进行共享
- 运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收
使用new String 和直接赋值创建String对象的区别:
String str1 = “abc”;
String str2 = “abc”;
String str3 = “abc”;
String str4 = new String(“abc”);
String str5 = new String(“abc”);
String使用equal和==的区别
先上结论,String对象应该使用equal对象判断。
首先了解equal和==的区别: =**=**
操作符:
- 用于基本类型的比较,判断值是否相等
- 用于引用类型比较:判断引用是否指向堆内存的同一块地址。
**equal**
: equal是Object类的方法,子类一般会重写该方法。一般用于比较对象的值是否相等。
String对象使用不同的创建方式,equal和==判断的结果的是不同的:
String s1 = "ss"; // String 直接创建
String s2 = "ss"; // String 直接创建
String s3 = new String("ss"); // String 对象创建
String s4 = new String("ss"); // String 对象创建
System.out.println(s1 == s2); // true
System.out.println(s1.equals(s2));//true
System.out.println(s3 == s4); // false
System.out.println(s3.equals(s4)); //true
System.out.println(s1 == s3);//false
System.out.println(s1.equals(s3));//true