六、字符串查找
1. contains( ) 方法
主串通过 contains( ) 方法 调用子串
程序清单17:
public class Test17 { public static void main(String[] args) { String str1 = "abcdef"; String str2 = "cde"; System.out.println(str1.contains(str2)); } }
输出结果:
2. indexOf( ) 方法
主串通过 indexOf( ) 方法 调用子串
如果主串中有子串的数据,那么就返回主串中的索引,即子串对应的第一个字符的位置,如果主串中没有子串的数据,则返回 -1.
程序清单18:
public class Test18 { public static void main(String[] args) { String str1 = "abcdef"; String str2 = "cde"; String str3 = "dec"; System.out.println(str1.indexOf(str2)); System.out.println(str1.indexOf(str3)); } }
输出结果:
3. lastIndexOf( ) 方法
indexOf( ) 方法是在主串中从前往后找子串,lastIndexOf( ) 方法是在主串中从后往前找子串。虽然 lastIndexOf( ) 方法是从后往前找,但是输出的索引下标依然是从前往后算的。只不过,如果两个方法面对重复的字符,会出现不同的结果。 如下所示:
程序清单19:
public class Test19 { public static void main(String[] args) { String str1 = "{abcd}}"; String str2 = "}"; System.out.println(str1.lastIndexOf(str2)); System.out.println(str1.indexOf(str2)); } }
输出结果:
拓展:indexOf( ) 方法 和 lastIndexOf( ) 方法中可以添加第二个整型参数 : ( fromIndex ),表示从某个位置开始找,感兴趣的小伙伴可以自己测试一下。
4. startsWith( ) 方法
字符串通过调用 startsWith( ) 方法,来判断主串的头部是否是我们要输入的数据。传参的时候可以是一个参数,也可以是两个参数。第一个参数可以是字符,也可以是字符串;第二个参数表示的是偏移量,旨在通过某个位置开始判断。
代码如下:
程序清单20:
public class Test20 { public static void main(String[] args) { String str1 = "abcdef"; System.out.println(str1.startsWith("a")); System.out.println(str1.startsWith("bcd")); System.out.println(str1.startsWith("c",2)); //表示从偏移位置2 往后判断 } }
输出结果:
5. endsWith( ) 方法
startsWith( ) 方法是从头部开始判断,而 endsWith( ) 方法是从尾部开始判断,思想是相同的,但是 endsWith( ) 方法 在传参的时候,只能传一个参数,即我们要判断的字符或字符串。
程序清单21:
public class Test21 { public static void main(String[] args) { String str1 = "abcdef"; String str2 = "cde"; System.out.println(str1.endsWith("f")); System.out.println(str1.endsWith("bcd")); } }
输出结果:
七、字符串替换
1. replace( ) 方法
我们看到下图 replace( ) 方法,在底层的代码,当替换成功的时候,返回的是一个新创建的对象,而需要替换的字符的内容和输入字符的内容相同的时候,说明无须替换,那么这个时候,就返回原来的引用。
这就说明一个问题,在上文中的目录,我提到了一个关键点:字符串的内容不可变,也就是说,当我们表面上好像改变了原先字符串的数据,但实际上底层操作的时候,为我们 new 了一个新的对象,所以说我们看到被替换后的字符串,实际意义上是一个完完整整的一个新串,与原先的字符串没有半点关系。
程序清单22:
public class Test22 { public static void main(String[] args) { String str = "abcabeabdabxy"; String ret1 = str.replace('a','z'); String ret2 = str.replace("ab","zz"); String ret3 = str.replace("ab","zzz"); String ret4 = str.replaceFirst("ab","zz"); System.out.println(ret1); System.out.println(ret2); System.out.println(ret3); System.out.println(ret4); } }
输出结果:
八、字符串拆分
1. split( ) 方法
我们看到 split( ) 方法底层代码的返回类型是 String[ ] 类型,即一个字符串数组的类型。
程序清单23:
public class Test23 { public static void main(String[] args) { String str = "name=Jack&age=23"; String[] strings = str.split("&"); for (String s:strings) { System.out.println(s); } } }
输出结果:
在我们通过 IDEA 编译器调试之后,我们可以发现字符串数组中下标 0 和 1 放了两个字符串。
我们需要注意的是:如果我们单纯地调用 split( ) 方法,只会分割一次,因为底层代码就是这么实现的,在后面我会提到多次分割。
程序清单24:
public class Test24 { public static void main(String[] args) { String str = "name=Jack&age=23"; String[] strings = str.split("=", 2); for (String s:strings) { System.out.println(s); } } }
输出结果:
这里我们需要注意下面的代码:在 split( “=”, 2 ) 中的参数2,代表的是按 " = " 分割成两组,而不是均匀分割成两组。
String[] strings = str.split("=", 2);
2. 使用 split( ) 方法 进行二次分割
程序清单25:
public class Test25 { public static void main(String[] args) { String str = "name=Jack&age=23"; String[] strings1 = str.split("&"); for (String s:strings1) { String[] strings2 = s.split("="); for (String ss: strings2) { System.out.println(ss); } } } }
输出结果:
在这里我就不进行调试了,调试的过程中是动态的,感兴趣的小伙伴可以自己她是过后观察。
3. 字符串拆分的特殊情况
对于一些转义字符的特殊情况,我们在给 split( ) 方法传参的时候,需要考虑进去。比方说:在程序清单26中,我们通过字符 " . " 来分割字符串,然而我们不能直接就这么传参了,我们需要通过 ’ \ ’ 转义才能达到我们的目的。
此外,字符 " | ‘’," * " , " + " 等等都得加上转义字符。
程序清单26:
public class Test26 { public static void main(String[] args) { String str = "192.168.1.1" ; String[] strings = str.split("\\.") ; for(String s: strings) { System.out.println(s); } } }
输出结果:
4. 按连接符实现多次拆分
在程序清单26中,我们可以通过连接符 ’ | ’ 实现不同分隔符之间的拆分。
程序清单26:
public class Test26 { public static void main(String[] args) { String str = "name=Jack&age=23"; String[] strings = str.split("=|&|="); for (String s:strings) { System.out.println(s); } } }
输出结果:
九、字符串截取
1. subString( ) 方法
subString( ) 方法的底层代码,可以看到其返回类型是 String 类型。
并且,返回时创建了新的对象。
我们先来展示程序清单27,再来分析代码是如何实现的。
程序清单27:
public class Test27 { public static void main(String[] args) { String str1 = "abcdef"; String str2 = str1.substring(2); String str3 = str1.substring(0); String str4 = str1.substring(2,5); System.out.println(str2); System.out.println(str3); System.out.println(str4); } }
输出结果:
分析程序清单27的代码:
输出 str2 :是从字符串下标索引为2的字符开始往后截取(包括2下标),底层代码 new 了一个新对象。
输出 str3 :字符串下标索引为0的字符开始往后截取,等于拿到了原先的对象,这时候并没有重新创建对象。
输出 str4 :subString( ) 方法遵循左闭右开,左边下标索引为2可以取到字符,右边下标索引为5取不到字符。同时,底层代码 new 了一个新对象。
十、其他的一些字符串操作
1. trim( ) 方法
trim( ) 方法 用来除去字符串两边的空格,代码如下:
程序清单28:
public class Test28 { public static void main(String[] args) { String str1 = " abc xyz "; System.out.print(str1); System.out.println("------------"); String str2 = str1.trim(); System.out.print(str2); System.out.println("------------"); } }
输出结果:
2. 将字符串中的字符转换成大写 / 小写 [ 常用 ]
toUpperCase( ) 方法 和 toLowerCase( ) 方法
程序清单29:
public class Test29 { public static void main(String[] args) { String str1 = "abcdWXYZ123"; String str2 = str1.toUpperCase(); String str3 = str1.toLowerCase(); System.out.println(str2); System.out.println(str3); } }
输出结果:
3. 拼接字符串
程序清单30:
public class Test30 { public static void main(String[] args) { String str1 = "abcd"; String str2 = "wxyz"; String str3 = str1.concat(str2); System.out.println(str3); } }
输出结果:
4. 求字符串长度 [ 常用 ]
求字符串长度并不难,而我想说的是,这里需要区分:字符串求长度的格式 str.length( ) 和 数组求长度的个数的格式 arr.length。注意点:一个有括号,一个没括号。
程序清单31:
public class Test31 { public static void main(String[] args) { String str = "abcde"; System.out.println(str.length()); int[] arr = {1,2,3,4,5}; System.out.println(arr.length); } }
输出结果:
5. 判断字符串是否为空
程序清单32:
public class Test32 { public static void main(String[] args) { String str1 = "abcd"; String str2 = " "; String str3 = ""; System.out.println(str1.isEmpty()); System.out.println(str2.isEmpty()); System.out.println(str3.isEmpty()); } }
输出结果:
十一、理解引用
程序清单33:
public class Test { public void change(String s, char[] ch){ s = "world"; ch[0] = 'g'; } String str = new String("hello"); char[] chars = {'a', 'b', 'c'}; public static void main(String args[]){ Test test = new Test(); test.change(test.str, test.chars); System.out.println(test.str); System.out.println(test.chars); } }
输出结果:
这一题在我第一次做的时候,自己认为输出结果会是下面代码:
//错误答案 world gbc
后来,我才想明白,当我们传参的时候,传了一个是 String 类型,一个是 char[ ] 类型,这两者在传参的时候传的其实都是引用类型。(这其实就是实参和形参之间对应的关系,通过改变形参并不影响传入的实参状态)
说白了,就是把对象的地址传过去了。那么在 change( ) 方法接收的时候,等于拿到了两者的地址。后来,我们改变了引用变量 s ,让它指向了 " world “,那么就不再指向原先的” hello ",所以最终引用 s 与主函数中的引用 str 没有半毛钱关系。而引用 ch 就不同了,它是通过 ch[0] 直接访问到原先数组,通过改变数组下标为 0 的元素值,这会破坏原先数组的结构,所以最终数组元素变成了 [ gbc ]。 下图辅助理解:
十二、StringBuilder 类 和 StringBuffer 类
我们查看底层代码,发现 StringBuilder 类 在底层实现的时候,调用了 append( ) 方法,在 append( )方法中返回的是原先的对象,并没有重新 new 一个新的对象。append( ) 方法在底层代码实现的作用就是:可以拼接字符串,然后一次一次地将字符串放入原先的对象中。
我们查看底层代码,发现 StringBuffer 类 在底层实现的时候,和上面的 StringBuilder 类很相似,唯一不同的就是 StringBuffer 类多了一个限定符 synchronized,这表示 StringBuffer 采用了同步处理,属于线程安全操作,会使程序执行起来更加安全,当然也会使程序执行起来相对 StringBuilder 于更慢一点。
在介绍过 StringBuilder 类 和 StringBuffer 类 两者对应的底层代码之后,因为两者很多地方是相似的,那么我直接以 StringBuilder 类 来演示一些操作字符串的方法。
1. append( ) 方法
append( ) 方法可以用来拼接很多不同类型的变量放入一个 StringBuilder 类的变量中,我们先来看一下编译器中 append( ) 方法,可以拼接的东西应有尽有!
程序清单34:
public class Test34 { public static void main(String[] args) { StringBuilder str = new StringBuilder("abc"); System.out.println(str); System.out.println("----------------"); str.append("opq"); System.out.println(str); System.out.println("----------------"); str.append("xyz"); System.out.println(str); } }
输出结果:
在程序清单34中,我们可以发现 append( ) 方法在放字符串的时候,每次都放入了 str 中,那么实际上被改变的就是原先对象中的字符串内容。
2. 字符串逆置
程序清单35:
public class Test35 { public static void main(String[] args) { StringBuilder str = new StringBuilder("abcde"); System.out.println(str); System.out.println(str.reverse()); } }
输出结果:
3. StringBuilder 类 和 StringBuffer 类 的其他一些方法
在程序清单36中,通过 delete( ) 方法,我们删除对应索引下标的字符,遵循左闭右开原则。
程序清单36:
public class Test36 { public static void main(String[] args) { StringBuffer str = new StringBuffer("abcdefg"); System.out.println(str); System.out.println(str.delete(1,4)); } }
输出结果:
在程序清单37中,通过 insert( ) 方法,我们在对应的索引下标处添加对应的数据。
程序清单37:
public class Test37 { public static void main(String[] args) { StringBuffer str = new StringBuffer("abcde"); System.out.println(str); System.out.println(str.insert(0, "你好")); System.out.println(str.insert(2, "hello")); } }
输出结果:
4. String、StringBuffer、StringBuilder 三者的区别
① String的内容不可修改,StringBuffer与StringBuilder的内容可以修改
② StringBuffer与StringBuilder大部分功能是相似的
③ StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
5. String 和 StringBuffer、StringBuilder 之间的转换
在这里,我们需要理解一点:String 表示 String 类,而 StringBuilder 是另一种类,StringBuffer 同样也是另一种类,这三者性质不同,但是可以互相转换。
在程序清单38中,我演示了两种转换形式:
① StringBuilder / StringBuffer 类 =》String 类 调用 toString( ) 方法 ② String 类 =》StringBuilder / StringBuffer 类 利用 StringBuilder / StringBuffer 的构造方法
程序清单38:
public class Test { public static String transform1(){ StringBuilder strb = new StringBuilder(); strb.append(111); strb.append("hello"); return strb.toString(); //return strb; //error } public static StringBuilder transform2(){ String str = "world"; return new StringBuilder(str); //return str; //error } public static void main(String[] args) { System.out.println(transform1()); System.out.println(transform2()); } }
输出结果:
十三、其他类型与字符串类型之间的转换
1. 整型转字符串
程序清单39:
public class Test { public static String transform1(){ return Integer.toString(123); } public static String transform2(){ return Character.toString('c'); } public static void main(String[] args) { System.out.println(transform1()); System.out.println(transform2()); } }
输出结果:
在上面,我演示了 toString( ) 方法
当然,我们也可以使用 String.valueOf ( ) 方法,此外,我们可以进行转换的类型有很多很多。
2. 字符串转整型
在下面的程序中,我先将整个字符串分割为一个数组元素中的三个元素,我将数组的第一个元素转换成了整数,使用了【 Integer.parseInt() 】方法,后两个元素直接赋值给了字符串。后来我又将刚刚数组的第一个元素转换成了字符串,通过给其加上一个空字符即可。
程序清单40:
public class Test { public static void main(String[] args) { String str = "123;opq;xyz"; String[] result = str.split(";"); for (String s : result) { System.out.print(s +" "); } int a = Integer.parseInt(result[0]); String b = result[1]; String c = result[2]; System.out.print("\n"+a +" "); System.out.print(b +" "); System.out.print(c +" \n"); System.out.println("------------------"); String str2 = result[0] + ""; System.out.println(str2); } }
输出结果: