一、Java中的字符串
Java中的字符串是由双引号括起来的多个字符,下面示例都是表示字符串常量:
"Hello World" "\u0048\u0065\u006c\u006c\u006f\u0020\u0057\u006f\u0072\u006c\u0064" "世界你好" "A" ""
Java中的字符采用Unicode编码,所以Java字符串可以包含中文等亚洲字符
Java SE提供了三个字符串类:String、StringBuffer和StringBuilder。
String是不可变字符串
StringBuffer和StringBuilder是可变字符串。
关于字符串的详细解释,可以去查看官方文档
关于查询API的一般流程是:找包→找类或接口→查看类或接口→找方法或变量。可以尝试查找一下 String、StringBuffer和StringBuilder这些字符串类的API文档,熟悉一下这些类的用法。String类在java.lang包中。
二、不可变字符串
(1)String
Java中不可变字符串类是String,属于java.lang包,它也是Java非常重要的类。
创建String对象可以通过构造方法实现,常用的构造方法:
String():使用空字符串创建并初始化一个新的String对象。
String(String original):使用另外一个字符串创建并初始化一个新的 String 对象。
String(StringBuffer buffer):使用可变字符串对象(StringBuffer)创建并初始化一个新的 String 对象。
String(StringBuilder builder):使用可变字符串对象(StringBuilder)创建并初始化一个新的 String 对象。
String(byte[] bytes):使用平台的默认字符集解码指定的byte数组,通过byte数组创建并初始化一 个新的 String 对象。
String(char[] value):通过字符数组创建并初始化一个新的 String 对象。
String(char[] value, int offset, int count):通过字符数组的子数组创建并初始化一个新的 String 对 象;offset参数是子数组第一个字符的索引,count参数指定子数组的长度。
示例代码:
// 创建字符串对象 String s1 = new String(); String s2 = new String("helloWorld"); String s3 = new String("\u0048\u0065\u006c\u006c\u006f\u0020\u0057\u006f\u0072\u006c\u0064"); System.out.println("s2 = " + s2); System.out.println("s3 = " + s3); char chars[] = { 'a', 'b', 'c', 'd', 'e' }; // 通过字符数组创建字符串对象 String s4 = new String(chars); // 通过子字符数组创建字符串对象 String s5 = new String(chars, 1, 4); System.out.println("s4 = " + s4); System.out.println("s5 = " + s5); byte[] bytes = { 97, 98, 99 }; // 通过byte数组创建字符串对象 String s6 = new String(bytes); System.out.println("s6 = " + s6); System.out.println("s6字符串长度 = " + s6.length());
运行结果:
s2 = helloWorld s3 = Hello World s4 = abcde s5 = bcde s6 = abc s6字符串长度 = 3
(2)字符串池
前面的示例代码中获得字符串对象时都是直接使用字符 串常量,但Java中对象是使用new关键字创建,字符串对象也可以使用new关键字创建,代码如下:
//字符串常量 String s9 = "Hello"; String s10 = "Hello"; //使用new关键字创建 String s7 = new String("Hello"); String s8 = new String("Hello"); System.out.println(s7 == s9); System.out.println(s9 == s10); System.out.println(s7 == s8); System.out.println("--------------"); System.out.println(s7.equals(s9)); System.out.println(s7.equals(s8));
使用new关键字与字符串常量都能获得字符串对象,但它们之间有一些区别。
看打印结果:
false true false -------------- true true
这里就会抛出一个Java中的常见面试题:== 与equals()的区别?
==运算符比较的是两个引用是否指向相同的对象(即同一内存空间),也就是说在内存空间中的存储位置是否一致。
equals是判断两个变量或者实例指向同一个内存空间的值是不是相同
文字解析:
从上面的运行结果可见,s7和s8指的是不同对象, s9和s10指向的是相同对象。
Java中的不可变字符串String常量,采用字符串池(String Pool)管理技术,字符串池是 一种字符串驻留技术。采用字符串常量赋值时会在字符串池中查 找"Hello"字符串常量,如果已经存在把引用赋值给s9,否则创建"Hello"字符串对象,并放到池中。根 据此原理,可以推定s10与s9是相同的引用,指向同一个对象。但此原理并不适用于new所创建的字符 串对象,它并没有放到字符串池中。s7和s8是不同的引用,指向不同的对象。
图解:
(3)字符串拼接
String字符串虽然是不可变字符串,但也可以进行拼接只是会产生一个新的对象。
String字符串拼接可 以使用+运算符或String的concat(String str)方法。+运算符优势是可以连接任何类型数据拼接成为字符 串,而concat方法只能拼接String类型字符串。
字符串拼接示例代码:
String s1 = "Hello"; // 使用+运算符连接 String s2 = s1 + " "; String s3 = s2 + "World"; System.out.println(s3); String s4 = "Hello"; // 使用+运算符连接,支持+=赋值运算符 s4 += " "; s4 += "World"; System.out.println(s4); String s5 = "Hello"; // 使用concat方法连接 s5 = s5.concat(" ").concat("World"); System.out.println(s5); int age = 18; String s6= "她的年龄是" + age + "岁。"; System.out.println(s6); char score = 'A'; String s7= "她的英语成绩是" + score; System.out.println(s7); java.util.Date now = new java.util.Date(); //对象拼接自动调用toString()方法 String s8= "今天是:" + now; System.out.println(s8);
(4)字符串查找
在给定的字符串中查找字符或字符串是比较常见的操作。在String类中提供了indexOf和lastIndexOf方法 用于查找字符或字符串,返回值是查找的字符或字符串所在的位置,-1表示没有找到。这两个方法有 多个重载版本:
int indexOf(int ch):从前往后搜索字符ch,返回第一次找到字符ch所在处的索引。
int indexOf(int ch, int fromIndex):从指定的索引开始从前往后搜索字符ch,返回第一次找到字符 ch所在处的索引。
int indexOf(String str):从前往后搜索字符串str,返回第一次找到字符串所在处的索引。
int indexOf(String str, int fromIndex):从指定的索引开始从前往后搜索字符串str,返回第一次找 到字符串所在处的索引。
int lastIndexOf(int ch):从后往前搜索字符ch,返回第一次找到字符ch所在处的索引。
int lastIndexOf(int ch, int fromIndex):从指定的索引开始从后往前搜索字符ch,返回第一次找到 字符ch所在处的索引。
int lastIndexOf(String str):从后往前搜索字符串str,返回第一次找到字符串所在处的索引。
int lastIndexOf(String str, int fromIndex):从指定的索引开始从后往前搜索字符串str,返回第一次 找到字符串所在处的索引。
字符串本质上是字符数组,因此它也有索引,索引从零开始。String的charAt(int index)方法 可以返回索引index所在位置的字符。
字符串查找的示例代码:
String sourceStr = "There is a string accessing example."; //获得字符串长度 int len = sourceStr.length(); //获得索引位置16的字符 char ch = sourceStr.charAt(16); //查找字符和子字符串 int firstChar1 = sourceStr.indexOf('r'); int lastChar1 = sourceStr.lastIndexOf('r'); int firstStr1 = sourceStr.indexOf("ing"); int lastStr1 = sourceStr.lastIndexOf("ing"); int firstChar2 = sourceStr.indexOf('e', 15); int lastChar2 = sourceStr.lastIndexOf('e', 15); int firstStr2 = sourceStr.indexOf("ing", 5); int lastStr2 = sourceStr.lastIndexOf("ing", 5); System.out.println("原始字符串:" + sourceStr); System.out.println("字符串长度:" + len); S ystem.out.println("索引16的字符:" + ch); System.out.println("从前往后搜索r字符,第一次找到它所在索引:" + firstChar1); System.out.println("从后往前搜索r字符,第一次找到它所在索引:" + lastChar1); System.out.println("从前往后搜索ing字符串,第一次找到它所在索引:" + firstStr1); System.out.println("从后往前搜索ing字符串,第一次找到它所在索引:" + lastStr1); System.out.println("从索引为15位置开始,从前往后搜索e字符,第一次找到它所在索引:" + firstChar2); System.out.println("从索引为15位置开始,从后往前搜索e字符,第一次找到它所在索引:" + lastChar2); System.out.println("从索引为5位置开始,从前往后搜索ing字符串,第一次找到它所在索引:" + firstStr2); System.out.println("从索引为5位置开始,从后往前搜索ing字符串,第一次找到它所在索引:" + lastStr2);
运行结果:
原始字符串:There is a string accessing example. 字符串长度:36 索引16的字符:g 从前往后搜索r字符,第一次找到它所在索引:3 从后往前搜索r字符,第一次找到它所在索引:13 从前往后搜索ing字符串,第一次找到它所在索引:14 从后往前搜索ing字符串,第一次找到它所在索引:24 从索引为15位置开始,从前往后搜索e字符,第一次找到它所在索引:21 从索引为15位置开始,从后往前搜索e字符,第一次找到它所在索引:4 从索引为5位置开始,从前往后搜索ing字符串,第一次找到它所在索引:14 从索引为5位置开始,从后往前搜索ing字符串,第一次找到它所在索引:-1
(5)字符串比较
字符串比较是常见的操作,包括比较相等、比较大小、比较前缀和后缀等。
比较相等
String提供的比较字符串相等的方法:
boolean equals(Object anObject):比较两个字符串中内容是否相等。
boolean equalsIgnoreCase(String anotherString):类似equals方法,只是忽略大小写。
比较大小
String提供的比较大小的方法:
int compareTo(String anotherString):按字典顺序比较两个字符串。如果参数字符串等于此 字符串,则返回值 0;如果此字符串小于字符串参数,则返回一个小于 0 的值;如果此字 符串大于字符串参数,则返回一个大于 0 的值。
int compareToIgnoreCase(String str):类似compareTo,只是忽略大小写。
比较前缀和后缀
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束。
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始。
字符串比较示例代码如下:
String s1 = new String("Hello"); String s2 = new String("Hello"); // 比较字符串是否是相同的引用 System.out.println("s1 == s2 : " + (s1 == s2)); // 比较字符串内容是否相等 System.out.println("s1.equals(s2) : " + (s1.equals(s2))); String s3 = "HELlo"; // 忽略大小写比较字符串内容是否相等 System.out.println("s1.equalsIgnoreCase(s3) : " + (s1.equalsIgnoreCase(s3))); // 比较大小 String s4 = "java"; String s5 = "Swift"; // 比较字符串大小 s4 > s5 System.out.println("s4.compareTo(s5) : " + (s4.compareTo(s5))); // 忽略大小写比较字符串大小 s4 < s5 System.out.println("s4.compareToIgnoreCase(s5) : " + (s4.compareToIgnoreCase(s5))); // 判断文件夹中文件名 String[] docFolder = { "java.docx", " JavaBean.docx", "Objecitve-C.xlsx", "Swift.docx " }; int wordDocCount = 0; // 查找文件夹中Word文档个数 for (String doc : docFolder) { // 去的前后空格 doc = doc.trim(); // 比较后缀是否有.docx字符串 if (doc.endsWith(".docx")) { wordDocCount++; } } System.out.println("文件夹中Word文档个数是: " + wordDocCount); int javaDocCount = 0; // 查找文件夹中Java相关文档个数 for (String doc : docFolder) { // 去的前后空格 doc = doc.trim(); // 全部字符转成小写 doc = doc.toLowerCase(); // 比较前缀是否有java字符串 if (doc.startsWith("java")) { javaDocCount++; } } System.out.println("文件夹中Java相关文档个数是:" + javaDocCount);
运行结果:
s1 == s2 : false s1.equals(s2) : true s1.equalsIgnoreCase(s3) : true s4.compareTo(s5) : 23 s4.compareToIgnoreCase(s5) : -9 文件夹中Word文档个数是: 3 文件夹中Java相关文档个数是:2
(6)字符串截取
Java中字符串String截取方法主要的方法如下:
String substring(int beginIndex):从指定索引beginIndex开始截取一直到字符串结束的子字符串。
String substring(int beginIndex, int endIndex):从指定索引beginIndex开始截取直到索引endIndex-1处的字符,注意包括索引为beginIndex处的字符,但不包括索引为endIndex处的字符。
字符串截取方法示例代码如下:
String sourceStr = "There is a string accessing example."; // 截取example.子字符串 String subStr1 = sourceStr.substring(28); // 截取string子字符串 String subStr2 = sourceStr.substring(11, 17); System.out.printf("subStr1 = %s%n", subStr1); System.out.printf("subStr2 = %s%n",subStr2); // 使用split方法分割字符串 System.out.println("-----使用split方法-----"); String[] array = sourceStr.split(" "); for (String str : array) { System.out.println(str); }
运行结果:
subStr1 = example. subStr2 = string -----使用split方法---- There is a string accessing example.
三、可变字符串
可变字符串在追加、删除、修改、插入和拼接等操作不会产生新的对象。
(1)StringBuffer和StringBuilder
Java提供了两个可变字符串类StringBuffer和StringBuilder,中文翻译为“字符串缓冲区”。
StringBuffer是线程安全的,它的方法是支持线程同步,线程同步会操作串行顺序执行,在单线程环境 下会影响效率。
StringBuilder是StringBuffer单线程版本,它不是线程安全的,但它 的执行效率很高。
总结一下:
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
因此在这方面运行速度快慢为:StringBuilder > StringBuffer > String
StringBuffer和StringBuilder具有完全相同的API,即构造方法和普通方法等内容一样。
StringBuilder的中 构造方法有4个:
StringBuilder():创建字符串内容是空的StringBuilder对象,初始容量默认为16个字符。
StringBuilder(CharSequence seq):指定CharSequence字符串创建StringBuilder对象。CharSequence 接口类型,它的实现类有:String、StringBuffer和StringBuilder等,所以参数seq可以是String、 StringBuffer和StringBuilder等类型。
StringBuilder(int capacity):创建字符串内容是空的StringBuilder对象,初始容量由参数capacity指 定的。
StringBuilder(String str):指定String字符串创建StringBuilder对象。
上述构造方法同样适合于StringBuffer类,具体可以见官方文档
字符串长度和字符串缓冲区容量区别。字符串长度是指在字符串缓冲区中目前所包含字符 串长度,通过length()获得;字符串缓冲区容量是缓冲区中所能容纳的最大字符数,通过capacity() 获得。当所容纳的字符超过这个长度时,字符串缓冲区自动扩充容量,但这是以牺牲性能为代价 的扩容。
字符串长度和字符串缓冲区容量示例代码如下:
// 字符串长度length和字符串缓冲区容量capacity StringBuilder sbuilder1 = new StringBuilder(); System.out.println("包含的字符串长度:" + sbuilder1.length()); System.out.println("字符串缓冲区容量:" + sbuilder1.capacity()); StringBuilder sbuilder2 = new StringBuilder("Hello"); System.out.println("包含的字符串长度:" + sbuilder2.length()); System.out.println("字符串缓冲区容量:" + sbuilder2.capacity()); // 字符串缓冲区初始容量是16,超过之后会扩容 StringBuilder sbuilder3 = new StringBuilder(); for (int i = 0; i < 17; i++) { sbuilder3.append(8); } System.out.println("包含的字符串长度:" + sbuilder3.length()); System.out.println("字符串缓冲区容量:" + sbuilder3.capacity());
输出结果:
包含的字符串长度:0 字符串缓冲区容量:16 包含的字符串长度:5 字符串缓冲区容量:21 包含的字符串长度:17 字符串缓冲区容量:34
(2)字符串追加
StringBuilder在提供了很多修改字符串缓冲区的方法,追加、插入、删除和替换等。
字符串追加方法是append,append有很多重载方法,可以追加任何类型数据,它的返回 值还是StringBuilder。StringBuilder的追加法与StringBuffer完全一样。
字符串追加示例代码如下:
//添加字符串、字符 StringBuilder sbuilder1 = new StringBuilder("Hello"); sbuilder1.append(" ").append("World"); sbuilder1.append('.'); System.out.println(sbuilder1); StringBuilder sbuilder2 = new StringBuilder(); Object obj = null; //添加布尔值、转义符和空对象 sbuilder2.append(false).append('\t').append(obj); System.out.println(sbuilder2); //添加数值 StringBuilder sbuilder3 = new StringBuilder(); for (int i = 0; i < 10; i++) { sbuilder3.append(i); } System.out.println(sbuilder3);
运行结果:
Hello World. false null 0123456789
(3)字符串插入、删除和替换
StringBuilder中实现插入、删除和替换等操作的常用方法说明如下:
StringBuilder insert(int offset, String str):在字符串缓冲区中索引为offset的字符位置之前插入str, insert有很多重载方法,可以插入任何类型数据。
StringBuffer delete(int start, int end):在字符串缓冲区中删除子字符串,要删除的子字符串从指定 索引start开始直到索引end - 1处的字符。start和end两个参数与substring(int beginIndex, int endIndex)方法中的两个参数含义一样。
StringBuffer replace(int start, int end, String str)字符串缓冲区中用str替换子字符串,子字符串从指 定索引start开始直到索引end - 1处的字符。start和end同delete(int start, int end)方法。
StringBuffer也完全一样
示例代码如下:
// 原始不可变字符串 String str1 = "Java C"; // 从不可变的字符创建可变字符串对象 StringBuilder mstr = new StringBuilder(str1); // 插入字符串 mstr.insert(4, " C++"); System.out.println(mstr); // 具有追加效果的插入字符串 mstr.insert(mstr.length(), " Objective-C"); System.out.println(mstr); // 追加字符串 mstr.append(" and Swift"); System.out.println(mstr); // 删除字符串 mstr.delete(11, 23); System.out.println(mstr);
输出结果:
Java C++ C Java C++ C Objective-C Java C++ C Objective-C and Swift Java C++ C and Swift