JavaEE - 09常用类
(1)字符串相关类
(1.1)String
(1.1.1)String特性
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L;
(1.1.2)String对象创建
String str = "hello"; // 本质上 this.value = "".value; String s1 = new String(); // this.value = original.value; String s2 = new String(String original); // this.value = Arrays.copyOf(value, value.length); String s3 = new String(char[] a); // this.value = Arrays.copyOfRange(value, offset, offset+count); String s4 = new String(char[] a, int startIndex, int count);
面试题: String str1 = "abc" 与 String str2 = new String("abc") 的区别
字符串常量存储在字符串常量池,目的是共享; 字符串非常量对象存储在堆中。
@Test public void test(){ String s1 = "abc"; String s2 = "abc"; String s3 = new String("abc"); String s4 = new String("abc"); // s1 和 s2 的数据"abc",字符串常量池;s3 和 s4,在堆中开辟空间后对应的地址值。 System.out.println(s1 == s2); // true System.out.println(s1 == s3); // false System.out.println(s1 == s4); // false System.out.println(s3 == s4); // false }
@Test public void test2(){ Person p1 = new Person("Tom",12); Person p2 = new Person("Tom",12); System.out.println(p1.name == p2.name); // true
System.out.println(p1.name.equals(p2.name)); // true }
面试题:String s = new String("abc");方式创建对象,内存中创建了几个对象?
两个: 一个是堆空间中new结构,另一个是char[]对应的常量池中数据:"abc"。
面试题: String类是否可以被继承?
不可以被继承。继承后子类可重写父类的方法,那么
(1.1.3)字符串拼接计算
@Test public void test3(){ String s1 = "abc"; String s2 = "DEF"; String s3 = "abcDEF"; String s4 = "abc" + "DEF"; String s5 = s1 + "DEF"; String s6 = "abc" + s2; String s7 = s1 + s2; String s8 = (s1 + s2).intern(); System.out.println(s3 == s4); // true System.out.println(s3 == s5); // false System.out.println(s3 == s6); // false System.out.println(s5 == s6); // false System.out.println(s3 == s7); // false System.out.println(s5 == s7); // false
System.out.println(s3 == s8); // true
}
测试题:
public class StringTest { String str = new String("good"); char[] ch = {'t','e','s','t'}; public void change(String str, char ch[]){ str = "test ok"; ch[0]= 'b'; } public static void main(String[] args) { StringTest st = new StringTest(); st.change(st.str, st.ch); System.out.println(st.str); // good System.out.println(st.ch); // best } }
测试题:
@Test public void test4(){ String s = "0"; for(int i =1; i<=5;i++){ s += i; } System.out.println(s); // 常量池:"0" ,堆空间: "01","012","0123","01234","012345" String str = new String("0"); for(int i =1; i<=5;i++){ str += i; } System.out.println(str); // 常量池:"0", 堆空间: "0" ,"01","012","0123","01234","012345"
}
(1.1.4)String常用方法
- int length(): 返回字符串的长度: return value.length
- char charAt(Int index): 返回某索引处的字符 return value[index]
- boolean isEmpty(): 判断字符串是否为空。return value.length == 0
- String toLowerCase(): 使用默认语言环境的规则,将此 String 中的所有字符都转换为小写。
- String toLowerCase(Locale locale): 使用给定 Locale 的规则将,此 String 中的所有字符都转换为小写。
- String toUpperCase(): 使用默认语言环境的规则,将此 String 中的所有字符都转换为大写。
- String toUpperCase(Locale locale): 使用给定 Locale 的规则,将此 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(不包含)的一个子字符串。
- boolean endsWith(String suffix): 测试此字符串是否以指定的后缀结束。
- boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始。
- boolean startsWith(String prefix, int toffset): 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
- boolean contains(CharSequence chars): 判断是否包含指定的字符系列。包含返回true。
- int indexOf(String str): 返回指定子字符串在此字符串中第一次出现处的索引。未找到返回-1。
- int indexOf(String str, int fromIndex): 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
- int lastIndexOf(String str): 返回指定子字符串在此字符串中最右边出现处的索引。未找到返回-1。
- int lastIndexOf(String str, int fromIndex): 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
- 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个,超过的话,剩余全部放在最后一个元素中。
replace() / replaceAll()
@Test public void test6(){ String str1 = "北京厂面积大"; String str3 = str1.replace("北京","上海"); System.out.println(str1); // 北京厂面积大 System.out.println(str3); // 上海厂面积大 String str = "12hello34world56java78mysql34"; String string = str.replaceAll("\\d+",",").replaceAll("^,|,$",""); System.out.println(string); // hello,world,java,mysql }
matches() / split()
@Test public void test7(){ String str = "123456"; boolean matches = str.matches("\\d+"); System.out.println(matches); // true String tel = "0571-54789999"; boolean matches1 = tel.matches("0571-\\d{7,8}"); System.out.println(matches1); //true } @Test public void test8(){ String str = "abc|def|ghi"; String[] strs = str.split("\\|"); for(int i =0;i < strs.length; i++){ System.out.println(strs[i]); } String str2 = "abc.def.ghi"; String[] strs2 = str2.split("\\."); for(int i =0;i < strs2.length; i++){ System.out.println(strs2[i]); } }
(1.1.5)String 与基本数据类型、数组转换
- 字符串 -> 基本数据类型、包装类
- Integer包装类的public static int parseInt(String s): 可以将由"数字"组成的字符串转换成整型。
- 类似地,使用java.lang包中的Byte、Short、Long、Float、Double类使用相应的方法,将字符串转换为对应基本数据类型。
- 基本数据类型、包装类 -> 字符串
- 调用String类的 public String valueOf(int n) 可将int型转换为字符串。
- 相应的valueOf(byte b)、valueOf(long l)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)可进行由数据类型到字符串的转换
- static String valueOf(primitive data type x): 返回给定data type类型x参数的字符串表示形式。
- 字符串 <-> 字符数组
- char[] toCharArray(): 将此字符串转换为一个新的字符数组。
- 数组转字符串: 调用String的构造器
- 字符串 <-> 字节数组(编码、解码)
- 字符串转数组: 调用String的getBytes();字符集
- 数组转字符串: 调用String的构造器
@Test public void test(){ String str1 = "123"; int num = Integer.parseInt(str1); String str2 = String.valueOf(num); String str3 = "abc123"; char[] charArray = str3.toCharArray(); // String -> char[] for (int i = 0; i<charArray.length; i++){ System.out.println(charArray[i]); } char[] arr = new char[]{'h','e','l','l','o'}; String str4 = new String(arr); // char[] -> String System.out.println(str4); }
getBytes() / Arrays.toString()
@Test public void test3(){ String str1 = "abc123"; byte[] bytes = str1.getBytes(); System.out.println(Arrays.toString(bytes)); // [97, 98, 99, 49, 50, 51] }
getBytes(字符集)
@Test public void test3() throws UnsupportedEncodingException { String str1 = "abc123中国"; byte[] bytes = str1.getBytes(); // 使用默认的字符集进行转换, 字符串-> 二进制 【utf8 一个汉字三个字节; gbk 一个汉字两个字节】
System.out.println(Arrays.toString(bytes)); //[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67]
byte[] gbks = str1.getBytes("gbk");
System.out.println(Arrays.toString(gbks)); //[97, 98, 99, 49, 50, 51, -42, -48, -71, -6]
String str2 = new String(bytes); // 使用默认的字符集进行解码, 二进制 -> 字符串
System.out.println(str2);
String str3 = new String(gbks,"gbk"); // 不指定字符集,编码和解码字符集不一致,导致乱码
System.out.println(str3);
}
字符串拼接比较
@Test public void test4(){ String s1 = "javaEESpring"; String s2 = "javaEE"; String s3 = s2 + "Spring"; System.out.println(s1 == s3); //false final String s4 = "javaEE"; // 常量 String s5 = s4 + "Spring"; System.out.println(s1 == s5); //true }
(1.1.6)字符串是如何存储的
JDK 1.6(JDK 6.0, Java 6.0): 字符串常量池存储在方法区(永久区)
JDK 1.7: 字符串常量池存储在 堆空间
JDK 1.8: 字符串常量池存储在方法区(元空间)
(1.2)StringBuffer、StringBuilder
(1.2.1)String、StringBuffer、StringBuilder三者的异同
- String: 不可变的字符序列;底层使用char[]存储
- StringBuffer: 可变的字符序列;线程安全,效率低;底层使用char[]存储
- StringBuilder: 可变的字符序列;jdk5.0新增,线程不安全,效率高。底层使用char[]存储
- String 与 StringBuffer、StringBuilder 转换: 构造器
- 都是final类,都不允许被继承;
- String长度是不可变的,StringBuffer和StringBuilder长度是可变的。
- StringBuffer是线程安全的,StringBuilder是线程不安全的,但他们两个中的所有方法都是相同的,StringBuffer在StringBuilder的方法之上加了synchronized修饰,保证线程安全。
- StringBuilder比StringBuffer拥有更好的性能。
- 如果一个String类型的字符串,在编译时就可以确定是一个字符串常量,则编译完成之后,字符串会自动拼接成一个常量。此时String的速度比StringBuilder和StringBuffer的性能更好。
String s = new String(); StringBuilder stringBuilder = new StringBuilder(s); StringBuffer stringBuffer = new StringBuffer(s); s = new String(stringBuilder); s = new String(stringBuffer);
@Test public void test(){ StringBuffer sb1 = new StringBuffer("abc"); sb1.setCharAt(0,'m'); System.out.println(sb1); // mbc }
@Test public void test4(){ String str = null; StringBuffer sb = new StringBuffer(); sb.append(str); System.out.println(sb.length()); //4 System.out.println(sb); //"null" StringBuffer sb1 = new StringBuffer(str); System.out.println(sb1); //java.lang.NullPointerException }
(1.2.2)StringBuffer源码分析
@Test public void test1(){ String str = new String(); // char[] value = new char[0]; String str1 = new String("abc");// char[] value = new char[]{'a','b','c'}; StringBuffer sb1 = new StringBuffer();// char[] value = new char[16]; 底层创建一个长度为16的字符数组 sb1.append('a');// value[0] = 'a'; sb1.append('b');// value[1] = 'b'; System.out.println(sb1.length());// 2 sb1.capacity(); 16 StringBuffer sb2 = new StringBuffer("abc");// char[] value = new char["abc".length() + 16] System.out.println(sb2.length());// 3 sb2.capacity(); 19 }
StringBuffer源码
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence { public StringBuffer() { super(16); } public StringBuffer(String str) { super(str.length() + 16); append(str); } @Override public synchronized int length() { return count; } @Override public synchronized int capacity() { return value.length; } }
扩容问题: 底层数组扩容: 默认情况下,扩容为原来容量的2倍+2, 同时将原有数组的元素复制到新的数组中。
abstract class AbstractStringBuilder implements Appendable, CharSequence { public void ensureCapacity(int minimumCapacity) { if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity); } private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } private int newCapacity(int minCapacity) { // overflow-conscious code int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; } private int hugeCapacity(int minCapacity) { if (Integer.MAX_VALUE - minCapacity < 0) { // overflow throw new OutOfMemoryError(); } return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE; } }
- StringBuffer append(String s): 将指定的字符串追加到此字符序列。
- StringBuffer reverse(): 将此字符序列用其反转形式取代。
- StringBuffer delete(int start, int end): 移除此序列的子字符串中的字符。
- StringBuffer insert(int offset, xxx): 在指定位置插入xxx。
- StringBuffer replace(int start, int end, String str): 使用给定 String 中的字符替换此序列的[start,end)子字符串中的字符。
> 当append和insert时, 如果原来value数组长度不够,可扩容。
> 如上这些方法支持方法链操作。
> 方法链原理:
@Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }
- public int indexOf(String str)
- public String substring(int start, int end): 返回一个从start开始到end结束的左闭右开区间的子字符串。
- public int lenth()
- public char charAt(int n)
- public void setCharAt(int n, char ch)
@Test public void test2(){ StringBuffer sb1 = new StringBuffer("abc"); sb1.append(1); sb1.append('2'); System.out.println(sb1); // abc12 // sb1.delete(2,4); // ab2 // sb1.replace(2,4,"hello"); // abhello2 // sb1.insert(2,false); // abfalsec12 // sb1.reverse(); // 21cba String s2 = sb1.substring(1,3); //bc System.out.println(sb1.length()); //5 }
String、StringBuffer、StringBuilder 性能测试
@Test public void test3(){ long startTime = 0L; long endTime = 0L; String text = ""; StringBuffer buffer = new StringBuffer(""); StringBuilder builder = new StringBuilder(""); startTime = System.currentTimeMillis(); for (int i=0; i<20000; i++){ buffer.append(String.valueOf(i)); } endTime = System.currentTimeMillis(); System.out.println("StringBuffer执行时间:" + (endTime - startTime)); startTime = System.currentTimeMillis(); for (int i=0; i<20000; i++){ builder.append(String.valueOf(i)); } endTime = System.currentTimeMillis(); System.out.println("StringBuilder执行时间:" + (endTime - startTime)); startTime = System.currentTimeMillis(); for (int i=0; i<20000; i++){ text = text + i; } endTime = System.currentTimeMillis(); System.out.println("String执行时间:" + (endTime - startTime)); }
StringBuffer执行时间:5 StringBuilder执行时间:4 String执行时间:1474
(1.3)场景题目
1. 模拟一个trim方法,去除字符串两端的空格。
2. 将一个字符串进行反转。将字符串中指定部分进行反转。如"abcdefg" 反转为"abfedcg"。
//将一个字符串进行反转。将字符串中指定部分进行反转。如"abcdefg" 反转为"abfedcg"。 public String reverse(String str, int startIndex, int endIndex){ char[] c = str.toCharArray(); int mid = (endIndex - startIndex) / 2 + 1; for(int i = 0; i< mid ; i ++){ char tmp = c[endIndex-i]; c[endIndex - i] = c[startIndex+i]; c[startIndex + i] = tmp; } return new String(c); } @Test public void testReverse(){ String str = "abcdefg"; String newStr = reverse(str,1,5); System.out.println("原字符串:" + str); //原字符串:abcdefg System.out.println("新字符串:" + newStr); //新字符串:afedcbg }
for(int x = startIndex,y=endIndex; x<y; x++,y--){ char tmp = c[y]; c[y] = c[x]; c[x] = tmp; }
// 使用String 拼接; public String reverse1(String str, int startIndex, int endIndex){ if(str != null){ String newStr = str.substring(0,startIndex); for(int i = endIndex; i>= startIndex; i--){ newStr += str.charAt(i); } newStr += str.substring(endIndex +1); return newStr; } throw new RuntimeException("字符串为空"); }
//使用StringBuffer/StringBuilder替换String public String reverse2(String str, int startIndex, int endIndex){ if(str != null){ StringBuilder builder = new StringBuilder(str.length()); builder.append(str.substring(0,startIndex)); for(int i = endIndex; i>= startIndex; i--){ builder.append(str.charAt(i)) ; } builder.append(str.substring(endIndex +1)); return builder.toString(); } throw new RuntimeException("字符串为空"); }
3. 获取一个字符串在另一个字符串中出现的次数。如:获取"ab"在"abkkcadabkebkskab"中出现的次数。
public int subCount(String str, String subStr){ int subLen = subStr.length(); int count=0; for(int i=0; i< str.length(); ){ if(i+subLen > str.length()){ break; } if(str.substring(i,i+subLen).equals(subStr)){ count++; i = i + subLen; }else { i++; } } return count; } @Test public void test(){ String str = "aabcabrwasdaab"; String substr = "ab"; int count = subCount(str, substr); System.out.println(count); }
public int subCount2(String mainStr, String subStr){ int mainLength = mainStr.length(); int subLength = subStr.length(); int count = 0; int index; if(mainLength >= subLength){ while((index = mainStr.indexOf(subStr)) != -1){ count++; mainStr = mainStr.substring(index + subLength); } return count; } return count; }
while((index = mainStr.indexOf(subStr, index)) != -1){ count++; index += subLength; }
4. 获取两个字符串中最大相同子串。如: str1="abcwethyuiodef" str2="chyuodef"
思路: 将短的那个串进行长度依次递减的子串与较长的串比较。
//获取两个字符串中最大相同子串。如: str1="abcwethyuiodef" str2="chyuodef" public String getMaxSubStr(String str1, String str2){ String maxStr = (str1.length() >= str2.length()) ? str1 : str2; String minStr = (str1.length() < str2.length()) ? str1 : str2; String maxSubStr = ""; for(int i = 0; i < minStr.length(); i++){ for(int j = i; j <= minStr.length(); j++){ String compareStr = minStr.substring(i,j); if(maxStr.contains(compareStr)){ maxSubStr = (maxSubStr.length() <= compareStr.length() ? compareStr : maxSubStr); } } } return maxSubStr; } @Test public void test(){ String s1 = "abcdefg1212abc"; String s2 = "e2abc4d"; String maxStr = getMaxSubStr(s1,s2); System.out.println(maxStr); }
字符串长度从len、len-1、len-2......进行查找
public String getMaxSubStr2(String str1, String str2){ if(str1 != null && str2 != null){ String maxStr = (str1.length() >= str2.length()) ? str1 : str2; String minStr = (str1.length() < str2.length()) ? str1 : str2; int length = minStr.length(); for(int i =0 ; i< length; i++){ for(int x = 0, y = length -i; y <= length; x++,y++){ String subStr = minStr.substring(x,y); System.out.println(subStr); if(maxStr.contains(subStr)){ return subStr; } } } } throw new RuntimeException("字符串为空"); }
结果:
e2abc4d
e2abc4
2abc4d
e2abc
2abc4
abc4d
e2ab
2abc
2abc
多个相同长度的最大相同子串
// 如果存在多个长度相同的最大相同子串,返回String[] public String[] getMaxSubStr3(String str1, String str2){ if(str1 != null && str2 != null){ StringBuffer buffer = new StringBuffer(); String maxStr = (str1.length() >= str2.length()) ? str1 : str2; String minStr = (str1.length() < str2.length()) ? str1 : str2; int length = minStr.length(); for(int i =0 ; i< length; i++){ for(int x = 0, y = length -i; y <= length; x++,y++){ String subStr = minStr.substring(x,y); // System.out.println(subStr); if(maxStr.contains(subStr)){ buffer.append(subStr + ","); } } if(buffer.length() != 0){ break; } } String[] split = buffer.toString().replaceAll(",$","").split("\\,"); return split; } throw new RuntimeException("字符串为空"); }
5. 对字符串中字符进行自然排序。
思路:1) 字符串变成字符数组。 2) 对数组排序,选择,冒泡,Arrays.sort(); 3) 将排序后的数组变成字符串。