Java中的String

80 阅读4分钟

String

在Java中,一切都是对象。Java没有内置的字符串类型,而是标准类库中提供了一个预定义类,很自然地叫做String。Java中每个字符串都是String类的一个实例。

Java中的String是不可变的。原因如下:

字符串是使用最广泛的数据结构,大量的字符串创建是非常耗费资源的,所以,Java提供了对字符串的缓存功能,可以大大节省堆空间。

JVM中专门开辟了一部分空间来存储Java字符串,就是字符串池。

各个字符串存放在字符串池中,字符串变量指向存储池中相应的位置。如果复制一个字符串变量,原始字符串和复制的字符串共享存储在字符串池中的相同的字符。

不可变字符串有一个很大的优点:编译器可以让字符串共享。

需要特别注意的是:千万不要把Java中的字符串想当然的理解为C语言中的字符数组,字符串就是字符串,不能通过下标的方式来改变其中某个字符;类似C语言中的char *

各个字符串存放在字符串池中,字符串变量指向存储池中相应的位置,因此,我们可以想象到,字符串变量存储的是字符串的地址,当检测两个字符串是否相等时,就需要注意一些问题。

可以使用equals方法检测两个字符串是否相等。对于表达式s.equals(t);如果字符串s和字符串t相等,就返回true,否则返回false。s与t可以是字符串变量,也可以是字符串字面量,例如:"abc".equals(t);。但是,不能使用==运算符检测两个字符串是否相等,在Java中,除了8个基本数据类型是值类型,其他的都是引用数据类型;String也是引用数据类型,也就是说,字符串变量中存储的是字符串的地址。==运算符比较的是两个字符串的地址,如果字符串存放在同一个位置上,它们必然相等;但是,完全有可能将多个内容相同的字符串副本存放在不同的位置上。这似乎与上文中所说的字符串不可变、字符串可共享相矛盾,别急,我来说明原因。

在我们创建字符串时,有两种方法: 一、直接赋值的方式 二、采用new关键字新建一个字符串对象

String s1 = "abcd";// String 直接创建
String s2 = "abcd";// String 直接创建
String s3 = s1;// 相同引用
String s4 = new String("abcd");// String对象创建
String s5 = new String("abcd");// String对象创建

采用直接赋值的方式创建时,内部流程如下: 采用直接赋值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在"abcd"这个字符串对象,如果不存在,则在字符串池中创建"abcd"这个对象,然后将池中"abcd"这个对象的引用地址返回给字符串变量s1,这样s1就会指向字符串池中"abcd"这个字符串对象;如果存在,则不创建任何对象,直接将池中"abcd"这个对象的地址返回,赋给s1。

采用new关键字的方式创建时,内部流程如下: 采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"abcd"这个字符串对象,如果有,则不在池中再去创建"abcd"这个对象了,直接在堆中创建一个"abcd"字符串对象,然后将堆中的这个"abcd"对象的地址返回给s4,这样,s4就指向了堆中的这个"abcd"字符串对象;如果没有,则首先在字符串池中创建一个"abcd"字符串对象,然后再在堆中创建一个"abcd"字符串对象,然后将堆中这个"abcd"字符串对象的地址返回赋给s4引用,这样,s4指向了堆中创建的这个"abcd"字符串对象。

示意图如下:

bb17482e3f724960e500609499e8945.png

也就是说,关于字符串的共享;只有字符串字面量会共享(存储在字符串池中的字符串对象才可以共享),存储在堆中的字符串对象并不会共享;因此不能使用==运算符检测两个字符串是否相等,只能使用equals方法来检测。

知识扩展:String str = new String("abcd");创建了几个对象? 解答:1个或2个。原因:不论如何,一次new的过程一定会在堆上创建一个对象;另一个对象就是字符串池中的对象,如果字符串池中已经有"abcd"这个字符串对象了,则不在池中再去创建"abcd"这个对象,如果没有,还会在字符串池中创建一个"abcd"字符串对象。也就是说,如果在之前执行过String *** = new String("abcd");String *** = "abcd";那就创建一个对象,否则就是两个。