【Java SE】String
Java
字符串就是Unicode
字符序列(一个字符占两个字节,可表示世界上任何文字和标志)。Java
中没有内置的字符串类型,但Java
标准库中提供了一个预定义类String
,用于保存字符串。每个用双引号括起来的字符串都是String
类的一个实例。
String e = "Hello";
String
类是final
类,这意味着它不能被其他类继承。String
有属性private final char value[]
,用于存放字符串内容。另外,String
类实现了Serializable
接口,这使得字符串可以被序列化并在网络间传输;同时,它也实现了Comparable
接口,使得字符串对象可以进行比较。
一、字符串创建
在Java
中,创建String
对象的方式有两种:
- 直接赋值
- 调用构造器
(1)直接赋值
在Java
中,字符串字面量(直接使用双引号的字符串)会存储在字符串常量池中。当创建一个新的字符串字面量时,Java
虚拟机会首先检查常量池中是否已有相同内容的字符串。如果存在,则返回该字符串的引用;如果不存在,则会在常量池中创建一个新的字符串实例。
String s = "abc";
以"abc"
为例子:Java
虚拟机首先从常量池
查看是否有相同内容的字符串,若有则直接指向(返回引用),若无则重新创建并返回引用。s
最终指向的是常量池中的内存地址。
(2)调用构造器
在Java
中,String
类较常用的构造方法包括:
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a, int startIndex, int count);
// 实例
String s = new String("abc");
以new String("abc")
为例子:在使用构造器创建的过程中,Java
虚拟机首先在堆
中为对象创建内存空间,该对象中维护value
属性,指向常量池的"abc"
空间,若常量池没有"abc"
则重新创建,若有则value
直接指向对应位置。s
最终指向的空间是堆中的内存地址。
二、字符串子串
在Java
中,使用String
类的substring
方法可以从较大的主串中提取出子串。例如:
String s = "hello";
String t = s.substring(0, 3); // "hel"
substring
方法的第一个参数是子串的开始索引位置,第二个参数是子串结束索引的下一个位置(不想提取的第一个位置),即左闭右开。
三、字符串拼接
与绝大多数程序设计语言类似,Java
允许使用+
号拼接两个字符串:
String s1 = "hello ";
String s2 = "world!";
String s3 = s1 + s2;
注意细节
(1)当将一个字符串与一个非字符串拼接时,后者会转成字符串。
(2)在Java 11
中,提供了repeat
方法:"Hi".repeat(2); //"HiHi"
。
四、字符串判等
在Java
中使用equals
方法判断两个字符串是否相等,相等返回true
,否则返回false
。另外,若以不区分大小写的方式进行判等,可以使用equalsIngoreCase
方法。
s.equals(t);
注意细节
(1)不要使用==
判等,这个运算符只能确定两个字符串是否存在同一位置。
(2)可以用s.compareTo(t) == 0
判断字符串是否相等。
五、空串&Null
串
空串""
是一个Java
对象,有串长度(0
)和内容(内容为空)。
// 判断是否为空串
if (s.length == 0)
if (s.equals(""))
String
变量可以存放一个特殊值,名为null
,表示目前没有任何对象与该变量关联。
// 判断是否为null
if (s == null)
六、字符串特性
不可变性:String
对象的value
属性是final
类型,这意味着一旦字符串对象被创建,它内部存储的字符内容value
就不能再改变。String
类也没有提供任何方法来修改字符串中的某个字符,通常的做法是通过子串结合拼接操作实现修改。如果需要对字符串进行任何操作,比如拼接或替换,都会生成一个新的String
对象,而原来的字符串对象保持不变。另外,需要注意的是:虽然value
指向的内容无法修改,但可以修改String
对象的引用,即可以让一个变量指向另一个字符串对象。
/**
* 下面的语句在常量池创建了两个字符串对象:"hello"和"hi"。
* 具体流程:
* 1)JVM检查并在常量池中创建"hello"对象,将"hello"引用返回给s;
* 2)JVM检查并在常量池中创建"hi"对象,将"hi"引用返回给s。
*/
String s = "hello";
s = "hi"
/**
* 下面的语句在常量池创建了一个字符串对象:"hellohi"
* 原因是:编译器进行了优化,优化后的语句为:String s = "hellohi";
*/
String s = "hello" + "hi";
/**
* 下面的语句在常量池创建了三个对象,在堆创建了一个对象,底层实现:
* 1)在常量池创建 a 对象
* 2)在常量池创建 b 对象
* 3)创建一个StringBuilder sb = StringBuilder()
* 4)执行sb.append("hello");
* 5)执行sb.append("hi");
* 6)String c = sb.toString()
* 7)c指向堆中对象,这个对象的value指向常量池中的"hellohi"
*/
String a = "hello";
String b = "hi";
String c = a + b;
由于String
类是保存字符串常量的,每次更新都需要重新开辟空间,效率较低,因此,java
设计者还提供了StringBuilder
和StringBuffer
来增强String
的功能,并提高效率。
七、码点与代码单元
Java
字符串实际上是一个char
值序列(即字符串属性value
)。char
数据类型是采用UTF-16
编码表示Unicode码点
(码点
指编码表中的某个字符对应的代码值)的一个代码单元。常用的Unicode
字符可以用一个代码单元表示,而辅助字符则需要一对代码单元表示。
即一个
char
表示一个代码单元,常用字符可以用一个char
表示,辅助字符需要两个char
表示。
String s = "𝕆"; // 辅助字符
int n = s.length(); // 2
int cpCount = s.codePointCount(0, s.length()); // 1
在上面的代码中,length
方法返回采用UTF-16
编码表示的字符串所需的代码单元个数。若想要获得实际长度,即码点个数(字符个数),可以调用codePointCount
。