来看有关String类的一个例子:
public class practice {
public static void main(String[] args) {
String strA="hello";
String strB= new String("hello");
String strC=strB;
System.out.println(strA==strB);
System.out.println(strA==strC);
System.out.println(strB==strC);
}
}
上述例子输出
false
false
true
怎么解释?
其实要解释上述例子需要对栈内存和堆内存的相关知识充分了解
首先 String strA="hello"; 这一句开辟出strA指向一个新的堆内存空间(假设为堆地址为ox001)
第二句 String strB= new String(); new关键字又开辟出一个新的堆内存
(假设开辟出的堆内存地址为ox002)
然后呢 第三句话 String strC=strB; c于栈内存中指向了b的堆内存空间(地址为ox002,此处不是假设而是前提是第二句话的必然)
由此,我们可以得到结果:
false
false
ture
所以此处引申出以下结论
当”==“在字符串对象时,比较的是字符串对象的内存地址,且”==“比较的是数值比较。
在引用数据类型操作之中,永远都可以使用”==“进行比较,进行比较的都是地址数值。
此处加入一个比较字符串类的比较方法
equals( )
如上述例子中稍加修改如下
public class practice {
public static void main(String[] args) {
String strA="hello";
String strB= new String("hello");
String strC=strB;
System.out.println(strA.equals(strB));
System.out.println(strA.equals(strC));
System.out.println(strB.equals(strC))
;
}
}
则可以得到输出结果为:
true
true
true
接着来看下面例子
public class practice {
public static void main(String[] args) {
String strA="hello";
String strB="hello";
String strC
="hello";
System.out.println(strA==strB);
System.out.println(strA==strC);
System.out.println(strB==strC)
;
}
}
输出为
true
true
true
为什么?
此处设计String设计模式问题,String设计过程中,考虑到用户频繁使用情况,String采用了”共享设计模式“
此处正式定义稍微难理解,此处给出个人理解:首先存在一个字符串池,用来共享或者开辟,如果有前者开辟出相同的字符串,则下一个直接使用,而不去开辟新的空间;如果没有,则重新开辟堆内存空间。
简单总结来说,String类中的直接赋值实例化中:存在(内容相同)就使用,不存在就开辟。
String使用构造方法实例化时,会开辟出两个新的堆内存空间,
是例如String strA=new String(”Hello“);
首先,hello字符串为匿名对象,然后呢堆内存开辟出一个空间,之后关键字new亦开辟出一个新的空间,然后strA指向new关键字所开辟的堆内存空间,而匿名对象hello字符串所在堆内存因为没有被任何栈内存空间指向,所以将会作为垃圾将被gc回收
观察例子:
public class practice {
public static void main(String[] args) {
String strA=new String("hello");
String strB="hello";
String strC="hello";
System.out.println(strA==strB);
System.out.println(strA==strC);
System.out.println(strB==strC)
;
}
}
输出为
false
false
true
何解?
上述中讲到String直接赋值实例化中,会有一个共享池,而构造函数实例化String却不会直接入共享池中
Java中为此提供一个手动入池的方法:
public String intern()
上述例子中改为
public class practice {
public static void main(String[] args) {
String strA=new String("hello").intern();//手动入池
String strB="hello";
String strC
="hello";
System.out.println(strA==strB);
System.out.println(strA==strC);
System.out.println(strB==strC)
;
}
}
输出为
true
true
true
综上,一般工作中都是用String直接赋值实例化,为了防止产生大量的堆内存垃圾(具体后续会详解)
注意:String类字符串内容一旦声明不可改变
String类字符串内容一经声明就已经不可以改变,原因在于String类字符串最初被定义的时候,采用的是char类型数组采用顺序存储方式进行储存的,并且使用了关键字final进行修饰,由此String类型字符串内容在堆内存中永恒不可改变。对于字符串的修改仅仅是做了一下操作:是改变了字符串变量名(存在于栈内存中)的指向,由一个指向原来堆内存空间变成了指向另一个堆内存空间。