public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash;
上面是String类源码的一部分
1、由final修饰,不可以继承
2、实现了Serializable接口:表示字符串支持序列化;实现了Comparable接口,表示String可以比较大小
3、String内部定义了final char[]value用于存储字符串数据
4、String代表不可变的字符序列(String类的重要性质——不可变性)
5、通过字面量给一个字符串赋值的过程中,此时字符串声明在字符串常量池中(与new完全不同)
6、字符串常量池中没有两个相同的字符串
7、理解不可变性
不可变性
public class Java1Test1 {
public static void main(String[]args)
{
String s1="abc";
String s2="abc";
System.out.println(s1==s2);
s1="hello";
System.out.println(s1);
System.out.println(s2);
}
}
此处显示出来的结果为
String是一个类,“==”符号在类的比较中很显然是对地址进行比较,但是在对于s1和s2的比较中,很明显现实的结果是true,证明二者存储地址相同,但是在对s1进行更改时,s2并没有随着更改,此处的疑惑就涉及到关于String类的不可变性
在这个过程中用图来描述关于不可变性的理解
如图所示,“==”依旧是比较引用数据类型的地址值,此处证明了不可变性,与原本存储方式是在分给的存储空间内部更改数据,此处不会对数据进行更改,而是会更改赋给原本位置的地址值修改读出内容
方法区中的字符串没有任何变动,所以可以从这个方向理解不可变性
这时我们会有一个问题,就是关于字符串的连接,如果字符串连接了一
个字符串2,那么是在方法区对应的地址尾部加上字符串2吗
问题用一个例子来佐证
public class Java1Test1 {
public static void main(String[]args)
{
String s1="abc";
String s2="abc";
System.out.println(s1==s2);
s1+="hello";
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
}
}
此处得出的结果是
很显然在s1和s2之间,存储的地址在最开始是相同的,之后的s1输出的abchello也证明字符串确实是连接成功了,之后s2,发现s2的值实际上并没有发生改变,s2存储的依旧是原来的地址,但是s1并不是,这个时候我们对二者使用“==”进行比较,果然发现结果是false
不可变性,就是指字符常量池中的字符串不可能随意的进行连接或者切割,很明显会在方法区再次创造一个新的字符串,并且赋给它新的字符串的地址
如图所示,原本存储地址的内容并不会受到影响
由此可以适当的去理解关于字符串的不可变性,不可变性并不能说明String对于存储的数据不能够更换数据,但事实上是方法区的问题
String的赋值
直接赋值
如果不使用new函数的话,证明在堆空间里是没有创建新的空间,字符串会直接保存在方法区
间接赋值
使用new函数,创建了新的堆空间,在变量中存储的是堆空间的地址,堆空间中对应的存储就是方法区中字符串的对应地址
以下是对String的赋值进行描述
String str="hellow";
//这种就是直接赋值,以下都是利用new函数的间接赋值
String str1=new String();
//就是简单利用String类本身的空参构造器
String str2=new String(String.orignal);
//也是String类的构造器,该构造器的内部语句是
//this.value=orignal.value;
String str3=new String(char[]a);
//也可以传入字符类型的数组,可以相互转化,内部语句为
//this.value=Arrays.copyOf(value,value.length)
String str4=new String(char[]a,int startIndex,int count);
//这个构造器相当于是对于字符串的切片操作了,输入字符类型的数组
//并输入起始地址值,然后输入想要的字符个数
//因为new出来的结构在堆中,所以不变性不再适用了
如果用字面量赋值,那么只需要二者的字符串内容相同,进行“s1==s2”这个语句的时候返回的值就是true, 如果使用构造器(也就是用new构造)即使字符串一致,那么执行如上语句不成立,返回的值是false
String的不可变性,即使使用构造器进行构造对象,也依旧不影响String类本身的不可变性,而且始终都是这一套原则
字符串加法
在字符串加法中
public static void main(String[]args)
{
String str="hellow";
String str1="world";
String str2=str+str1;
System.out.println(str2);
}
如图上就是两个简单的操作,对两个字符串进行相加,需要注意的是,此处依旧遵守不可变性
str中存储的”hellow“在方法区中的位置依旧是没有变化的,对于str1也是一样,但是str2存储的连接字符串并不是二者实际意义上的加和,而是在方法区再次创建了一个”hellowworld“的空间,依旧是不可变性。
1、常量与常量拼接的内容在方法区,且方法区中没有相同内容的常量
2、只要有其中一个是变量,结果存储在堆中,不在方法区
String类中的方法
intern()
是返回在常量池的地址
测试
public static void main(String[]args)
{
String str=new String("hellow");
//此处对于str来说,str存储的地址是在堆中的空间
//但是堆中存储的才是真的在常量池的部分
String str1=str.intern();
//str1相当于存储的是str在堆中存储的常量池地址
System.out.println(str1==str);
//此处对于二者的地址值进行比较,以证实上面的结论
}
结果是false
所以intern()所得到的地址就是字符串常量所在方法区的位置
String作为一个常用的类,其中最值得注意一下的地方就是它独特的不可变性