八(一)、常用类之String、StringBuffer、StringBuilder
一、String类:
字符串,使用一对“”引起来;
1.String 声明为final 不可被继承;
2.实现了
- Seriablizable:表示字符串是支持序列化的;
- Compareble :可比较大小
- CharSequence接口:提供对多种不同类型的统一只读访问 序列。
3.String内部声明了final char[] value数组,用于存储char 数组;
4.String:代表不可变的字符序列;简称:不可变性;
- 体现1:当对字符串重新赋值时,需要重新制定内存区域赋值,不能使用原有的value进行赋值
- 体现2:当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value值进行赋值
- 体现3.当调用replace方法时,也需要重新指定内存区域赋值,不能使用原有的value值进行赋值
说明:
1 @Test 2 public void test1() { 3 String s1 = "abc";// 自面量方式创建 4 String s2 = s1; 5 System.out.println(s1 == s2); 6 s1 = "hello"; 7 System.out.println(s1 == s2); 8 System.out.println(s1); 9 System.out.println(s2); 19 }
这里 第一个 System.out.println(s1 == s2);这里是true;
s1 = "hello";
System.out.println(s1 == s2);
这里是false,因为s1就是hello,s2还是abc,这里区别于其他引用类型对象的赋值;也就是当对字符串重新赋值时,需要重新制定内存区域赋值,不能使用原有的value进行赋值
1 @Test 2 public void test4() { 3 String s1 = "abcdef"; 4 String s2 = "abc"; 5 s2 += "def";// abcdef 6 System.out.println("s1:" + s1); 7 System.out.println("s2:" + s2); 8 System.out.println(s1 == s2); 9 }
s1:abcdef;
s2:abcdef;
s1== s2的结果是false
体现了当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value值进行赋值;
1 @Test 2 public void test4() { 3 String s4 = "abc"; 4 String s5 = s4.replace("a", "m"); 5 System.out.println("s4:" + s4); 6 System.out.println("s5:" + s5); 7 }
s4:abc
s5:mbc
体现了当调用replace方法时,也需要重新指定内存区域赋值,不能使用原有的value值进行赋值
二、String 创建的方式
1.通过new+构造器的方式
说明:
给一个字符串赋值,此时的字符串声明在方法区的字符串常量池中;
问:String s = new String("abc");方式创建对象,在内存中创建了几个对象
答:一个是堆空间的new 对象,另一个 char 数组 对应常量池中的数据
1 String s = new String("0"); 2 for(int i=0;i<=5;i++){ 3 s+=i; 4 } 5 System.out.println(s)
内存解析:
再看一个例子:
@Test public void test2() { String s1 = "ABC";// 在方法区 中的 字符串常量池中 String s2 = "ABC";// 在方法区 中的 字符串常量池中 String s3 = new String("ABC");// 在 堆空间中 String s4 = new String("ABC");// 在 堆空间中 System.out.println(s1 == s2);// true System.out.println(s3 == s4);// false; System.out.println(s1 == s4);// false; Person p1 = new Person("Tom",1); Person p2 = new Person("Tom",1); System.out.println(p1.getName() == p2.getName()); System.out.println(p1.getAge() == p2.getAge()); }
1 class Person { 2 private String name; 3 private int age; 4 5 public String getName() { 6 return name; 7 } 8 9 public void setName(String name) { 10 this.name = name; 11 } 12 13 public int getAge() { 14 return age; 15 } 16 17 public void setAge(int age) { 18 this.age = age; 19 } 20 21 public Person(String name, int age) { 22 super(); 23 this.name = name; 24 this.age = age; 25 } 26 27 }
2.通过字面量方式(区别于new)
说明:
通过字面量方式给一个字符串赋值,此时的字符串声明在方法区的字符串常量池中;
注意:字符串常量池中不保存相同内容的字符串;也就是String的不可变性;
1 String s = "0"; 2 for(int i=0;i<=5;i++){ 3 s+=i; 4 } 5 System.out.println(s)
三、字符串的特性:
常量与常量的拼接结果在常量池;而且常量池中不会存在相同内容的常量;
字符串拼接:只要其中一个是变量,结果就在堆中;如果拼接的结果调用 intern(),返回值就在常量池中
1 @Test 2 public void test3() { 3 String s1 = "javaEE"; 4 String s2 = "hadoop"; 5 String s3 = "javaEEhadoop"; 6 String s4 = "javaEE"+"hadoop"; 7 String s5 = s1 +"hadoop"; 8 String s6 = "javaEE"+s2; 9 String s7 = s1+s2; 10 String s8 = s7.intern(); 11 System.out.println(s3 == s4); 12 System.out.println(s3 == s5); 13 System.out.println(s3 == s6); 14 System.out.println(s5 == s6); 15 System.out.println(s3 == s8); 16 final String s9 = "hadoop"; 17 String s10 = "javaEE"+s9; 18 System.out.println(s3 == s10);//这里 s9是常量 19 20 21 22 }
s3 == s4:结果是true
s3 == s5:结果是false;
s3 == s6:结果是false;
s5 == s6:结果是false;
s3 == s8:结果是true
s3 == s10这里也是true
原因就是我们这里的特性;
五、String的常用方法:
-
int length():返回字符串长度;(从1开始数的,如 Helloworld,那么length 就是10)
-
char charAt(int index):返回某索引处的字符串 return value[index]; (index 从0开始)
-
boolean isEmpty():判断空字符串 return value.length == 0;
-
String toLowerCase():使用默认语言环境,将String中的所有字符串转化为小写
-
String toUpperCase():使用默认语言环境,将String中的所有字符串转化为大写
-
String trim():去除字符串 首尾的空格;
-
boolean equals(Object obj)比较字符串内容是否相同
-
boolean equalsIgnoreCase(String anOtherString)与equals方法类似,忽略大小写比较字符串内容是否相同
-
String Concat(String str):将指定字符串连接到此字符串的结尾,等价于 “+”
-
int compareTo(String anotherString),比较两个字符串的大小;
-
String subString(int beginIndex):返回一个新的字符串,他是此字符串的重beginindex 开始截取
-
String subString(int beginIndex,int endIndex):返回一个新的字符串,他是此字符串的重beginindex 开始截取 endIndex结束的[beginIndex,endIndex) beginIndex从0开始算的
-
boolean endsWith(String suffix):测试字符串是否以指定的后缀结束
-
boolean startWith(String prefix):测试此字符串是否以指定的前缀开始;
-
boolean startWith(string prefix,int toffset)测试此字符串从指定索引开始的子字符串是否以指定的前缀开始
-
boolean contains(charSequence s):当且仅当此字符串包含指定的char值序列时,返回true;
-
int indexOf(String str):返回指定子字符串在此字符串中第一次出现的索引,如果存在,则从0开始的,如果不存在,则是-1;
-
int indexOf(String str,int fromIndex);返回指定字符串在此字符串中第一次出现的索引
-
int lastIndexOf(String str,int fromIndex);返回指定字符串在此字符串中最右边出现的索引
-
int lastIndexOf(String str,int fromIndex);返回指定字符串在此字符串中最右边出现的索引
-
注意:indexOf 和 lastIndexOf方法如果未找到,都是返回-1
-
String replace(char oldChar,char newChar):返回一个新的字符串,他是通过newChar替换此字符串中出现的国有oldChar;
-
String replace(charSequence target,charSequence replacement):使用指定的字面值替换此字符所有匹配字面值目标序列的子字符串
-
String replaceAll(String regex,String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的子字符串
-
String replaceFirst(String regex,String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的第一个子字符串
-
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式
-
String[] split(string regex);根据给定正则表达式的匹配拆分此字符串;
-
String[] split(string regex,int limit);根据给定正则表达式的匹配拆分此字符串;最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中
-
String format(String format, Object... args);字符串拼接;eg:static String str = "%s和%s的%s.一刻也不能%s,无论%s走到那里,都留下一首赞歌";
1 @Test 2 public void test1() { 3 String s1 = "Helloworld"; 4 String testNull = null; 5 String testEmpty = ""; 6 System.out.println(s1.length());//获取数组长度 7 System.out.println(s1.charAt(0));//获取指定位置的字符 8 System.out.println(s1.isEmpty());//判断字符串是否为空 9 /// System.out.println(testNull.isEmpty());//报错 10 System.out.println(testEmpty.isEmpty());//判断字符串是否为空 11 System.out.println(s1.toUpperCase());//转换为大写 12 System.out.println(s1);//Helloworld 体现了不可变性 13 System.out.println(s1.toLowerCase());//转换为小写 14 15 String s2 = " Hello World "; 16 String s3 = s2.trim();//去除字符串首尾的空格 17 System.out.println("----"+s2+"------");//体现了不可变性 18 System.out.println("----"+s3+"------"); 19 20 String s4 = "HELLOWORLD"; 21 System.out.println(s1.equals(s4)); 22 System.out.println(s1.equalsIgnoreCase(s4));//忽略大小写比较字符串内容 23 24 String s5 = "abc"; 25 System.out.println(s5.concat("efg"));//拼接字符串 26 27 String s6 = "abf"; 28 System.out.println(s5.compareTo(s6));//涉及到字符串排序 29 30 System.out.println(s6); 31 System.out.println(s6.substring(1)); 32 System.out.println(s1.substring(2,5));//左闭右开 从2开始 到5结束 不包括5 33 34 boolean str1 = s1.endsWith("rld"); 35 System.out.println(str1); 36 37 boolean str2 = s1.startsWith("he"); 38 System.out.println(str2); 39 40 boolean str3 = s1.startsWith("He"); 41 System.out.println(str3); 42 43 44 boolean str4 = s1.startsWith("He",0); 45 System.out.println(str4); 46 47 boolean str5 = s1.startsWith("el",1); 48 System.out.println(str5); 49 50 boolean str6 = s1.contains("w"); 51 System.out.println(str6); 52 53 int str7 = s1.indexOf("lo"); 54 System.out.println(str7); 55 56 int str8 = s1.indexOf("lol"); 57 System.out.println(str8); 58 59 int str9 = s1.indexOf("lo",5); 60 System.out.println(str9); 61 62 int str10 = s1.lastIndexOf("lo",5); 63 System.out.println(str10); 64 65 int str11 = s1.lastIndexOf("w"); 66 System.out.println(str11); 67 } 68 69 @Test 70 public void test2() { 71 String str1 = "Hello World"; 72 String str2 = str1.replace("Hello", "Byby"); 73 System.out.println(str1); 74 System.out.println(str2); 75 String str3 = str1.replace('H', 'B'); 76 System.out.println(str3); 77 78 String str = "12hello34world5java789mysql456"; 79 String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");//\\d匹配数字, +表示有多个数字,^,|,$ 开头或者结尾有逗号 80 System.out.println(string); 81 82 str = "12345"; 83 boolean matches = str.matches("\\d+"); 84 System.out.println(matches); 85 String tel = "0571-4534289"; 86 boolean result = tel.matches("0571-\\d{7,8}");//0571开头 ,数字是7-8位 87 System.out.println(result); 88 89 90 str = "HELLO|WORLD|JAVA"; 91 String[] strs = str.split("\\|"); 92 for(int i = 0;i<strs.length;i++) { 93 System.out.println(strs[i]); 94 } 95 str2 = "hellow.world.java"; 96 String[] strs2 = str2.split("\\."); 97 for(int i = 0;i<strs2.length;i++) { 98 System.out.println(strs2[i]); 99 } 100 101 102 }
1 static String str = "%s和%s的%s.一刻也不能%s,无论%s走到那里,都留下一首赞歌"; 2 public static void main(String[] args) { 3 String format = String.format(str, "我", "我", "祖国", "分隔", "我","啦啦啦的马蒂"); System.out.println(format); //输出结果:我和我的祖国.一刻也不能分隔,无论我走到那里,都留下一首赞歌 4 }
★★★相关面试题:
1 package commonmethod; 2 3 public class Day20StringInterviewTest9 { 4 5 String str = new String("good");
7 char[] ch = {'t','e','s','t'}; 8 public void change(String str,char ch[]) { 9 str = "test ok";
11 ch[0] = 'b'; 12 } 13 public static void main(String[] args) { 14 Day20StringInterviewTest9 ex = new Day20StringInterviewTest9(); 15 ex.change(ex.str, ex.ch); 16 System.out.println(ex.str);18 System.out.println(ex.ch); 19 20 } 21 22 }
这里的输出结果 :
good
best
对于这个结果,前两个解释一下;想看ex.str为什么结果还是good呢?
首先需要两个知识点:
①在对象的方法中,值传递的规则是:如果形参是基本数据类型时,方法参数传递的是参数本事的值,如果参数是引用数据类型时,那个参数传递的是地址值(不是对象本身);(以下内存解析中0X开头的部分);
②String 值的不可变性;
ex.change(ex.str, ex.ch);==》形参:ex.str,其实是0x002;
str = "test ok";赋值一个新的值;常量池中没有 “test ok”,那么常量池中创建一个value值是 “test ok”的对象(这个原则,就是String不可变性的体现(即体现1));此时在该方法中产生一个 str的副本,该str副本这里称 strx吧;strx 指向test ok ;最后方法结束自动销毁(由jvm垃圾回收);最后打印出ex.str,就是打印传入的对应地址值的value值即:good;
这里的ch就是普通的引用类型变量;如果要做扩展,那么他可以换成某个对象,那么结果是什么呢?
附上内存解析图:(参考文章https://zhuanlan.zhihu.com/p/266885142)
这里补充例子:
1 public static String changeString(String str){ 2 return str+"ABC"; 3 } 4 public static int changInt(int a){ 5 a = 0; 6 return a; 7 } 8 @Test 9 public void testString1() { 10 String str1="abc123"; 11 int a = 23; 12 System.out.println(str1); 13 System.out.println(changInt(a)); 14 System.out.println(a); 15 }
这里的结果是 ?
abc123
abc123ABC
23
System.out.println(str1):这个还是跟上面那个套路是一样的;
System.out.println(a);是23原因是在ChangeInt方法中 a 其实是 a·,它在方法结束后自动销毁了;所以最后a仍然是 23;
六、String的转换:
1.String与基本数据类型的转换:
String-->基本数据类型、包装类:调用包装类的静态方法;parstXXX(str);
基本数据类型、包装类--》string 调用S挺重载的valueOf(xxx);
1 @Test 2 public void test1() { 3 String str1 = "123"; 4 int num = Integer.parseInt(str1); 5 String str2 = String.valueOf(num); 6 String str3 = num+""; 7 System.out.println(str2); 8 System.out.println(str3 == str1); 9 }
2.String 其他数据机构的转换
string 与char数组的转换 调用string toCharArray方法
string 与byte数组的转换 调用string getBytes方法
编码:String-->byte[]:调用string getBytes方法
解码:byte[]-->string:调用String的构造器;
说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码
1 @Test 2 public void test1() throws UnsupportedEncodingException { 3 String str1 = "abc123中国";//题目 把 abc123 变成 a321cb,那么就需要变成字符串再反转; 4 char[] charStr = str1.toCharArray(); 5 for(int i=0;i<charStr.length;i++) { 6 System.out.println(charStr[i]); 7 } 8 9 char[] arr = new char[] {'h','e','l','l'}; 10 String str2 = new String(arr); 11 System.out.println(str2); 12 System.out.println(arr); 13 System.out.println(Arrays.toString(arr)); 14 byte[] toByteArr = str1.getBytes();//可以设置编码 15 for(int i=0;i<toByteArr.length;i++) { 16 System.out.println(toByteArr[i]); 17 } 18 System.out.println(toByteArr); 19 System.out.println(Arrays.toString(toByteArr)); 20 byte[] toByteArr1 = str1.getBytes("gbk");//可以设置编码 21 System.out.println(Arrays.toString(toByteArr1)); 22 23 String str3 = new String(toByteArr1);//设置解码 24 System.out.println(str3); 25 26 String str4 = new String(toByteArr1,"gbk");//解码没有出现乱码。原因是:编码集和节目集一致 27 System.out.println(str4); 28 }
七、String、StringBuilder 、StringBuffer
1.String、StringBuilder 、StringBuffer的区别
String:不可变的字符序列 底层使用char[]存放数据;
StringBuffer:可变的字符序列,线程安全,效率低;底层使用char[]存放数据;
StringBuilder:可变的字符序列,线程不安全,效率高;底层使用char[]存放数据;
源码分析
String str = new String()//new char[0];
String str1 = new String("abc")//new char[]{'a','b','c'}
StringBuffer sb1 = new StringBuffer();//new char[16];
StringBuffer sb1 = new StringBuffer("abc");//new char[19]; value[0] = 'a',value[1] = 'b',value[2] = 'c'
问题1.System.out.println(sb1.length)//3
问题2,扩容问题,若果要添加的数据底层数组承载不了,那就需要扩容底层数组;把原来的数组复制到新的value数组;默认情况下扩容为原来容量的2倍+2(详情可参考源码)
指导建议:开发中建议大家使用 StringBuilder(int capacity) 或者StringBuffer(int capacity)
2.String 转换成 StringBuilder 、StringBuffe
StringBuffer sb = new StringBuffer("aaa");
StringBulider sb = new StringBulider("aaa");
String str = new String(sb);
or
String str = sb.toString();
3.StringBuffer的常用方法:
StringBuffer append(xxx):提供了很多append方法,用于字符串拼接
StringBufferdelete(int start,int end)删除指定位置的内容(左闭右开)
StringBuffer replace(int start,int end,String str)把[start,end)位置替换为
StringBuffer insert(int offset,xxx),在指定位置插入XXX
reverse(),把当前字符串倒叙
public int indexOf(String str)
public String subString(int start,int end)
public int length();
public char charAt(int n)
public void setCharAt(int n,char ch);
总结:
增:append()
删:delete(int start,int end)
改:public void setCharAt(int n,char ch);/StringBuffer replace(int start,int end,String str)
查:public int indexOf(String str)
插:StringBuffer insert(int offset,xxx)
长度length();
1 @Test 2 public void test1() { 3 StringBuffer sb1 = new StringBuffer(); 4 sb1.append(1); 5 sb1.append("1"); 6 System.out.println(sb1.toString()); 7 sb1.delete(0, 1); 8 System.out.println(sb1.toString()); 9 sb1.insert(0, "112123"); 10 System.out.println(sb1.replace(0, 2, "AA")); 11 System.out.println(sb1.toString()); 12 sb1.insert(0, false); 13 System.out.println(sb1.toString()); 14 System.out.println(sb1.reverse()); 15 System.out.println(sb1.length()); 16 System.out.println(sb1.indexOf("eslaf")); 17 System.out.println(sb1.charAt(10)); 18 sb1.setCharAt(10, 'x'); 19 System.out.println(sb1.toString()); 20 }
补充面试题:
1.字符串反转:abcdefg==>abfedcg
1 public void testInterview1() { 2 String str = "abcdefg"; 3 String str1 = str.substring(0,2); 4 String str2 = str.substring(2,6); 5 String str3 = str.substring(6); 6 StringBuffer sb = new StringBuffer(str2); 7 String res = str1+sb.reverse()+str3; 8 System.out.println("字符串反转:"+res); 9 }
2.abkkcadkabfkabkskab 问,ab一共出现过几次?
1 @Test 2 public void testInterview2() { 3 String str = "abkkcadkabfkabkskab"; 4 int count = 0; 5 int index = -1; 6 for(int i=0;i<str.length();i++) { 7 if(i == 0) { 8 index = str.indexOf("ab",0); 9 }else { 10 index = str.indexOf("ab",index+1); 11 } 12 13 14 if(index >-1) { 15 count = count+1; 16 17 }else { 18 break; 19 } 20 } 21 System.out.println(str.length()); 22 System.out.println(count); 23 24 }
3.获取两个字符串中的最大相同子串;str1 = "abcwerthelloyuiodef";str2 = "cvhellobnm";
1 @Test 2 public void testInterviwq3() { 3 String str1 = "abcwerthelloyuiodefn"; 4 String str2 = "cvhellodefnm"; 5 String min = ""; 6 String max = ""; 7 StringBuffer sb = new StringBuffer(); 8 if(str1.length()<= str2.length()) { 9 min = str1; 10 max = str2; 11 }else { 12 min = str2; 13 max = str1; 14 } 15 for(int i=0;i<min.length();i++) { 16 for(int x=0,y=min.length()-i;y<=min.length();x++,y++) { 17 String tem = min.substring(x,y); 18 System.out.println(tem); 19 if(max.contains(tem)) { 20 sb.append(tem+"|"); 21 } 22 } 23 if(sb.length()>1) { 24 break; 25 } 26 } 27 System.out.println("最大相同子串:"+sb.toString()); 28 }
4.StringBuffer中注意的一个地方:
1 @Test 2 public void testInterview4() { 3 String str = null; 4 StringBuffer sb = new StringBuffer(); 5 sb.append(str);//appendNull方法把 ‘null’ append进去了; 6 System.out.println(sb.length()); 7 StringBuffer sb2 = new StringBuffer(str);//这里有一个 str.length;这里str是null,所以报错 8 System.out.println(sb.length()); 9 10 }