字符串常用API
一、字符串的特点
- 字符串不可变。这种不可变性是通过内部的
private final char[]
字段,以及没有任何修改char[]
的方法实现的。修改一个字符串变量值,相当于新生成一个字符串对象。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
private final char value[];
也是字符串对象的内部存储形式。
JDK1.9之前有一个char[] value数组,JDK1.9之后byte[]数组
例如:
String str = "abc";
相当于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
// String底层是靠字符数组实现的。
- 字符串字面量也是一个String类的实例,存储在字符串常量池中,相同的字符串字面量表示的对象在内存中只有一份。
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
// 内存中只有一个"abc"对象被创建,同时被s1和s2共享。
- 字符串String类型本身是final声明的,不能继承String,也不能去重写他的方法。
二、构造字符串对象
- 使用构造方法
public String()
:初始化新创建的 String对象,以使其表示空字符序列。String(String original)
: 初始化一个新创建的String
对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。使用这种方式如果前面没有声明一个相同的字符串会产生两个对象。
例:new String(“abc”)会在常量池生成一个,然后在堆空间new一个一样的字符串
public String(char[] value)
:通过当前参数中的字符数组来构造新的String。public String(char[] value,int offset, int count)
:通过字符数组的一部分来构造新的String。public String(byte[] bytes)
:通过使用平台的默认字符集解码当前参数中的字节数组来构造新的String。public String(byte[] bytes,String charsetName)
:通过使用指定的字符集解码当前参数中的字节数组来构造新的String。
代码示例:
//字符串常量对象,推荐
String str = "hello";
// 无参构造,不推荐
String str1 = new String();
//创建"hello"字符串常量的副本,不推荐
String str2 = new String("hello");
//通过字符数组构造
char chars[] = {'a', 'b', 'c','d','e'};
String str3 = new String(chars);
String str4 = new String(chars,0,3);
// 通过字节数组构造
byte bytes[] = {97, 98, 99 };
String str5 = new String(bytes);
String str6 = new String(bytes,"GBK");
- 使用"+"
任意数据类型与"字符串"进行拼接,结果都是字符串
Student stu = new Student();
String s2 = stu + "";//自动调用对象的toString(),然后与""进行拼接,如果Student类没有重写toString方法,则输出的是地址值。
System.out.println(s2);
三、字符串的常用方法
-
基本操作
(1)boolean isEmpty():字符串是否为空
(2)int length():返回字符串的长度
(3)String concat(xx):拼接,等价于+
(4)boolean equals(Object obj):比较字符串是否相等,区分大小写
(5)boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写
(6)int compareTo(String other):比较字符串大小,区分大小写,按照Unicode编码值比较大小
(7)int compareToIgnoreCase(String other):比较字符串大小,不区分大小写
(8)String toLowerCase():将字符串中大写字母转为小写
(9)String toUpperCase():将字符串中小写字母转为大写
(10)String trim():去掉字符串前后空白符 -
查找
(11)boolean contains(String xx):是否包含xx
(12)int indexOf(String xx):从前往后找当前字符串中xx,即如果有返回第一次出现的下标,要是没有返回-1
(13)int lastIndexOf(String xx):从前往后找当前字符串中xx,即如果有返回最后一次出现的下标,要是没有返回-1
(13)int indexOf(String xx, int from ):从from位置开始找当前字符串中xx,即如果有返回第一次出现的下标,要是没有返回-1 -
字符串截取
(14)String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
(15)String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
案例:查看字符串中包含几个符合要求的字符串
public class StringTest {
public static void main(String[] args) {
String s = "好好学习,天天向上,每天写一写,记一记,每天都能进步一点点,加油,祝你我都能找到好工作!";
String s1 = "写一写";
int i = countNum2(s, s1);
System.out.println(i);
}
public static int countNum(String s, String s1) {
int count = 0;
int index = 0;
while ((s.indexOf(s1, index)) != -1) {
index = s.indexOf(s1, index) + s1.length();
count++;
}
return count;
}
public static int countNum1(String s, String s1) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
int i1 = s.indexOf(s1);
if (i1 != -1) {
i1 += s1.length();
s = s.substring(i1);
count++;
}
}
return count;
}
public static int countNum2(String s,String s1){
int count =0;
char[] chars = s.toCharArray();
char[] chars1 = s1.toCharArray();
for (int i = 0; i < s.length();) {
if(chars[i]==chars1[0]){
if(chars[i+1]==chars1[1]){
if(chars[i+2]==chars1[2]){
count++;
i+=s1.length();
}
}
}
i++;
}
return count;
}
}
-
和字符相关
(16)char charAt(index):返回[index]位置的字符
(17)char[] toCharArray(): 将此字符串转换为一个新的字符数组返回
(18)String(char[] value):返回指定数组中表示该字符序列的 String。
(19)String(char[] value, int offset, int count):返回指定数组中表示该字符序列的 String。
(20)static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String
(21)static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String
(22)static String valueOf(char[] data, int offset, int count) : 返回指定数组中表示该字符序列的 String
(23)static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String -
编码与解码
(24)byte[] getBytes():编码,把字符串变为字节数组,按照平台默认的字符编码进行编码
byte[] getBytes(字符编码方式):按照指定的编码方式进行编码
(25)new String(byte[] ) 或 new String(byte[], int, int):解码,按照平台默认的字符编码进行解码new String(byte[],字符编码方式 ) 或 new String(byte[], int, int,字符编码方式):解码,按照指定的编码方式进行解码 -
开头与结尾
(26)boolean startsWith(xx):是否以xx开头
(27)boolean endsWith(xx):是否以xx结尾 -
正则匹配
boolean matches(正则表达式):判断当前字符串是否匹配某个正则表达式 -
替换
(29)String replace(xx,xx):不支持正则
(30)String replaceFirst(正则,value):替换第一个匹配部分
(31)String repalceAll(正则, value):替换所有匹配部分@Test public void test4(){ String str = "hello244world.java;887"; //把其中的非字母去掉 str = str.replaceAll("[^a-zA-Z]", ""); System.out.println(str); //helloworldjava }
-
拆分
(32)String[] split(正则):按照某种规则进行拆分
String str = "Hello World java atguigu";
String[] all = str.split(" ");
四、字符串对象的内存分析
@Test
public void test02(){
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = s1 + "world";//s4字符串内容也helloworld,s1是变量,"world"常量,变量 + 常量的结果在堆中
String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是变量,变量 + 变量的结果在堆中
String s6 = "hello" + "world";//常量+常量,编译期经过优化,跟s3完全相同的情况。
System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//true
}
@Test
public void test03(){
final String s1 = "hello";
final String s2 = "world";
String s3 = "helloworld";
final String s7=new String("world");
String s4 = s1 + "world";//s4字符串内容也helloworld,s1是常量,"world"常量,常量+ 常量 结果在常量池中
String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是常量,常量+ 常量 结果在常量池中
String s6 = "hello" + "world";//常量+ 常量 结果在常量池中,因为编译期间就可以确定结果
String s8=s7+s1;//s7是new出来的在堆内存中,最终不是同一个地址。
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//true
System.out.println(s3 == s6);//true
System.out.println(s3 == s8);//false
}
@Test
public void test04(){
String s1 = "hello";
String s2 = "world";
String s3 = "helloworld";
String s4 = (s1 + "world").intern();//如果常量池已经有“helloworld”,直接返回,否则把拼接结果的引用放到常量池中
String s5 = (s1 + s2).intern();
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//true
}
结论:
- 常量+常量:结果是常量池
- 常量与变量 或 变量与变量:结果是堆
- intern方法有jvm版本的区别,这里不再深入分析,jdk8中执行原理是如果字符串常量池有内容相同的字符串则直接返回,否则把堆中创建的字符串引用放入字符串常量池,返回此引用,jdk8之前是常量池中没有则把值(而不是地址值)复制到常量池。总之所有版本都是通过字符串常量池返回的内容。