如果参数是String则比较内容,否则比较地址;
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;
}
String字符串常量
String的值是不可变;String的值是不可变;String的值是不可变;
String str = "hello";
System.out.println(str.hashCode()); //99162322
str = str + "world" ;
System.out.println("world".hashCode()); //113318802
System.out.println(str.hashCode()); //-1524582912
String str2 = "world";
System.out.println(str2.hashCode()); //-1524582912
字符串常量池中的字符串会复用,在字符串"abc"已经入池的情况下,str2 直接指向之前"abc"而不用重新开辟内存;
但是new String(String str)
会重新开辟内存,即字符串 不会入 常量池;
intern()
调用intern(),会使new String(String str)
入常量池,并返回指向常量池的引用;
String a = new String("zhangsan");//"zhangsan"在堆内存中
String b = a.intern();//此时"zhangsan"进入常量池,b指向常量池中的"zhangsan"
String c = "zhangsan";//常量池中有“zhangsan”,故a指向常量池中的"zhangsan"
System.out.println(a == c);//false ,a指向堆内存,c指向从常量池
System.out.println(b == c);//true ,b,c都指向池中"zhangsan"
https://blog.csdn.net/xiezhi_1130/article/details/84953379
String 的char数组是final修饰的,且内部不提供修改数组的方法;
public final class String
implements java.io.Serializable, Comparable, CharSequence {
private final char value[];
private int hash; // Default to 0
private static final long serialVersionUID = -6849794470754667710L;
......此处省略N多代码
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
}
即使是其它的方法,返回的也不是同一个对象;
public static void main(String[] args) {
String s1 = new String("s");
String s2 = s1.replace("s","a");
System.out.println(s1==s2); //false
String s3 = s1;
System.out.println(s1==s3); //true
}
通过查看字节码
String str = "abc";
str = str + "de";
编译器会自动创建一个临时StringBuider类,然后使用append方法 依次append(str),append(de);最后toString 给str;
即 反编译后应该是这样的
String str = "abc";
StringBuider tmp = new StringBuider(str);
tmp.append("de");
str= tmp.toString();
注意,String str = “” 与 new String() 2者都是创建一个String对象,但是区别很大;
String str = “abc"实现过程:首先栈区创建str引用,然后在常量池池中是否有“abc”,若有则str指向"abc",若没有则创建一个;
也就是说
String str = "abc"
String str2 = "abc"
str==str2,即 指向常量池中相同内存地址;
String str = new String(“abc”)的实现过程:先检查常量池中是否存在"abc",若不存在,则先在常量池中创建”abc“,再执行new过程,对象像和普通的引用对象一样,分配在堆中,str指向堆。
如果 常量池中存在”abc“,那么直接执行new操作,在堆中分配内存,str指向堆中的对象;
因此有
String str = new String("abc")
String str2 = new String("abc")
str != str2 //==比较的是地址
但是str.equals(str2)却是ture,equals比较的是内容;
StringBuffer字符串变量、StringBuilder字符串变量
和 String 类不同的是,StringBuffer和StringBuilder类的对象能够被多次的修改,并且不产生新的未使用对象。
构造函数
StringBuilder stringBuilder = new StringBuilder(); //如果不指定初始容量,则默认容量为16;
StringBuilder stringBuilder1 = new StringBuilder(10); //指定容量;
StringBuilder stringBuilder2 = new StringBuilder("asd");
参数为String的构造器源码
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
在参数Str 数组长度的基础上再增加16个字符长度,作为StringBuilder实例的初始数组容量,并将str字符串 append到StringBuilder的数组中。
除了append方法,其它与StringBuilder基本一致;
append方法
可以看到,添加了synchronized同步锁;
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
https://www.jianshu.com/p/64519f1b1137
String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,不存在扩容。
字符串拼接,截取,都会生成一个新的对象。
频繁操作字符串效率低下,因为每次都会生成新的对象。
StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容。非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象。
StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。其toString方法而重新生成的String对象。
使用场景
1.使用String类的场景:在字符串不经常变化的场景中可以使用String类,例如常量的声明、少量的变量运算。
2.使用StringBuffer类的场景:在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,则可以考虑使用StringBuffer,例如XML解析、HTTP参数解析和封装。
3.使用StringBuilder类的场景:在频繁进行字符串运算(如拼接、替换、和删除等),并且运行在单线程的环境中,则可以考虑使用StringBuilder,如SQL语句的拼装、JSON封装等。
性能
StringBuilder > StringBuffer > String