1.源码
●初始化
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
public String(){
this.value = new char[0];
}
}
可以看见String类和属性值被声明为final,不可被修改。本质上是一个字符数组。
●实例化
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
●查找某个位置的字符
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
●判断两个字符串是否相等
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;
}
--------重写equals()同时也必须重写hashCode()----------
public int hashCode(){
int h = hash;
if(h == 0 && value.length>0){
char val[] = value;
for(int i=0;i<value.length;i++){
h = 31*h+val[i];
}
hash = h;
}
return h;
}
2.JVM内存原理
Java虚拟机主要管理两种内存:堆和非堆。
堆是指运行时数据区域,在虚拟机启动时创建并所有线程共享。几乎所有对象实例及数组都在堆内分配,因此堆也是垃圾收集器的主要作用区域。
我们平时说的内存就是运行时数据区
1.方法区:存储类信息、常量、静态变量(全局共享)
2.堆:存放对象和数组(全局共享)
3.栈:基本数据类型、对象的引用(地址信息),线程私有。
String有两种赋值方式:
1.字面量赋值 String str = “hello”
2.new关键字 String str = new String(“hello”)
他们的区别:
字面量赋值:
当主线程开始创建str变量的,虚拟机会去字符串池中找是否有equals(“Hello”)的String,如果相等就把在字符串池中“Hello”的引用复制给str。如果找不到相等的字符串,就会在堆中新建一个对象,同时把引用驻留在字符串池,再把引用赋给str。当用字面量赋值的方法创建字符串时,无论创建多少次,只要字符串的值相同,它们所指向的都是堆中的同一个对象。
new对象:
当利用new关键字去创建字符串时,前面加载的过程是一样的,只是在运行时无论字符串池中有没有与当前值相等的对象引用,都会在堆中新开辟一块内存,创建一个对象
由于不同版本的JDK内存会有些变化,JDK1.6字符串常量池在永久代,1.7移到了堆中,1.8用元空间代替了永久代。但是基本对上面的结论没有影响,思想是一样的。
补充
String是不可变类,任何对String对象的修改都会生成新的String对象。
如对原字符串进行拼接:
jdk1.8之后字符串拼接底层就是创建了一个StringBuilder,然后调用append方法,最后调用toString转化成String
个人分析:
若底层采用字符数组动态地在末尾添加的方法,每添加一个字符都会创建一个新的String对象,极浪费空间,而采用StringBuilder方式则不会造成空间浪费,且效率更优。
因此,如果遇到大量的字符串拼接操作,我们还是采用StringBuilder/StringBuffer操作效率更高。
参考链接
如有不对敬请指正。