String 类
概述
String类代表字符串,使用一对 “” 引起来,Java程序中的所有字符串字面值(如”abc”)都作为此类的实例实现
- String声明为final的,不可被继承
- String实现了Serializable接口:表示字符串是支持序列化的
- String实现了Comparable接口:表示String可以比较大小
- String内部定义了final char[] value用于存储字符串数据
- String代表不可变的字符序列。简称:不可变性
- 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
字符串常量池中是不会存储相同内容的字符串的
public final class Stringimplements 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
String的不可变性
体现
当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
- 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
- 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值 ```java String s1 = “abc”; // 字面量的定义方式 String s2 = “abc”; System.out.println(s1 == s2); // true 比较s1和s2的地址值
s1 = “hello”; System.out.println(s1 == s2); // false System.out.println(s1); // hello System.out.println(s2); // abc
System.out.println(“*“);
String s3 = “abc”; s3 += “def”; System.out.println(s3); // abcdef System.out.println(s2);
System.out.println(“*“);
String s4 = “abc”; String s5 = s4.replace(‘a’, ‘m’); System.out.println(s4); // abc System.out.println(s5); // mbc
<br />**为什么String要设计成不可变的**- 运行时常量池的需要多个相同的String共用一个对象,若是可以修改,会导致其他引用同时改变- 同步- 允许String对象缓存hashcode字符串不变性保证了hash码的唯一性- 安全性String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,<br />还有反射机制所需要的String参数等,假若String不是固定不变的,将会引起各种安全隐患<a name="f01d80f10f60e7e74884f79092521223"></a>#### String实例化方式说明方式一:通过字面量定义的方式<br />方式二:通过new + 构造器的方式```java//通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。String s1 = "javaEE";String s2 = "javaEE";//通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。String s3 = new String("javaEE");String s4 = new String("javaEE");System.out.println(s1 == s2); // trueSystem.out.println(s1 == s3); // falseSystem.out.println(s1 == s4); // falseSystem.out.println(s3 == s4); // falseSystem.out.println("***********************");Person p1 = new Person("Tom", 12);Person p2 = new Person("Tom", 12);System.out.println(p1.name.equals(p2.name)); // trueSystem.out.println(p1.name == p2.name); // truep1.name = "Jerry";System.out.println(p2.name); // Tom
面试题
String s = new String(“abc”); 方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:”abc”
String str1=“abc”; 与 String str2= new String(“abc”); 的区别
字符串拼接方式赋值对比
说明
- 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量
- 只要其中一个是变量,结果就在堆中
- 如果拼接的结果调用intern()方法,返回值就在常量池中
- 调用 “ab”.intern()方法的时候会返回 “ab”,但是这个方法会首先检查字符串池中是否有 “ab”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用 ```java String s1 = “javaEE”; String s2 = “hadoop”;
String s3 = “javaEEhadoop”; String s4 = “javaEE” + “hadoop”; String s5 = s1 + “hadoop”; String s6 = “javaEE” + s2; String s7 = s1 + s2; String s8 = s6.intern(); // 返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
System.out.println(s3 == s4); // true System.out.println(s3 == s5); // false System.out.println(s3 == s6); // false System.out.println(s3 == s7); // false System.out.println(s5 == s6); // false System.out.println(s5 == s7); // false System.out.println(s6 == s7); // false System.out.println(s3 == s8); // true
```javaString s1 = "javaEEhadoop";String s2 = "javaEE";String s3 = s2 + "hadoop";System.out.println(s1 == s3); // falsefinal String s4 = "javaEE"; // s4:字符常量String s5 = s4 + "hadoop";System.out.println(s1 == s5); // true

String使用陷阱
1. String s1 = "a";在字符串常量池中创建了一个字面量为 "a" 的字符串2. s1 = s1 + "b"实际上原来的“a”字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s1 + "b"(也就是 "ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能3. String s2 = "ab";4. String s3 = "a" + "b";s3指向字符串常量池中已经创建的 "ab" 的字符串。5. String s4 = s1.intern();堆空间的S1对象在调用 intern () 之后,会将常量池中已经存在的 "ab" 字符串赋值给s4
//一道面试题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 ex = new StringTest();ex.change(ex.str, ex.ch);System.out.println(ex.str); // goodSystem.out.println(ex.ch); // best}}
JVM中涉及字符串的内存结构
- jdk 1.6:字符串常量池存储在方法区(永久区)
- jdk 1.7:字符串常量池存储在堆空间
- jdk 1.8:字符串常量池存储在方法区(元空间)
构造器
**public String()****public String(String original)**
字节数组转字符串**public String(byte[] bytes)**注意字节数组的格式,数值范围-128~127**public String(byte[] bytes, int offset, int length, String charsetName)**
字符数组转字符串**public String(char[] value)****public String(char[] value, int offset, int count)**
其他**public String(StringBuffer buffer)****public String(StringBuilder builder)**
方法
String类的判断功能**boolean equals(Object obj)**比较字符串的内容是否相同,区分大小写**boolean equalsIgnoreCase(String str)**比较字符串的内容是否相同,忽略大小写**boolean contains(String str)**判断大字符串中是否包含小字符串**boolean startsWith(String str)**判断字符串是否以某个指定的字符串开头**boolean startsWith(String str int toffset)**从指定索引位置以指定前缀开始**boolean endsWith(String str)**判断字符串是否以某个指定的字符串结尾**boolean isEmpty()**判断字符串是否为空
String类的获取功能**public int length()**返回此字符串的长度**char charAt(int index)**获取指定索引位置的字符
索引**int indexOf(int ch)**返回指定字符在此字符串中第一次出现处的索引,没有返回-1**int indexOf(int ch, int fromIndex)**返回指定字符在此字符串中从指定位置后第一次出现处的索引**int indexOf(String str)**返回指定字符串在此字符串中第一次出现处的索引**int indexOf(String str, int fromIndex)**返回指定字符串在此字符串中从指定位置后第一次出现处的索引**int lastIndexOf(String str)**返回指定子字符串在此字符串中最右边出现处的索引**int lastIndexOf(String str, int fromIndex)**
获取子串**String substring(int start)**从指定位置开始默认到末尾包含start这个索引**String substring(int start, int end)**从指定位置开始到指定位置结束截取字符串。包括start索引但是不包end索引
String的转换功能**byte[] getBytes()**把字符串转换为字节数组**char[] toCharArray()**把字符串转换为字符数组**void getChars(char dst[], int dstBegin)****static String valueOf(char[] chs)**把字符数组转成字符串**static String valueOf(Object i)**把其他类型的数据转成字符串
注意:String类的valueOf方法可以把任意类型的数据转成字符串**String toLowerCase()**把字符串转成小写**String toUpperCase()**把字符串转成大写
String的替换功能**String replace(char old, char new)**将字符串中的一个字符替换成另一个字符**String replace(String old, String new)**将一个小字符串替换成另一个字符串**String replaceAll(String regex, String replacement)**使用给定的replacement 替换此字符串所匹配给定的正则表达式的子字符串**String replaceFirst(String regex, String replacement)**替换第一个子字符串
String类的其他功能**String trim()**去除字符串两端空格 (只可以去除前后不能去除中间的空格)**boolean matches(String regex)**告知此字符串是否匹配给定的正则表达式**String concat(String str)**将指定字符串连接到此字符串的结尾。 等价于用“+”
按字典顺序比较两个字符串 **int compareTo(String str)**先比对应位置的字符的差,都相同再比长度差**int compareToIgnoreCase(String str)**
拆分**String[] split(String regex)**根据匹配给定的正则表达式来拆分此字符串,**String[] split(String regex, int limit)**
最多不超过limit个,如果超过剩下的全部都放到最后一个元素中
String s1 = "helloworld";System.out.println(s1.length()); // 10System.out.println(s1.charAt(4)); // oSystem.out.println(s1.isEmpty()); // falseString s2 = "HelloWorld";System.out.println(s2.toLowerCase()); // helloworldSystem.out.println(s2.toUpperCase()); // HELLOWORLDString s3 = " hello world ";System.out.println(s3.trim()); //hello worldString s4 = "helloworld";System.out.println(s4.equals(s1)); // trueSystem.out.println(s4.equalsIgnoreCase(s2)); // trueString s5 = "hello";System.out.println(s5.compareTo(s4)); // -5 相等时返回0,小的时候返回负数System.out.println(s4.compareTo(s1)); // 0System.out.println(s4.substring(5)); // worldSystem.out.println(s4.substring(5, 9)); // worl,取值范围左闭右开
String s1 = "javaEE";System.out.println(s1.endsWith("EE")); // trueSystem.out.println(s1.startsWith("a")); // falseSystem.out.println(s1.startsWith("EE", 4)); // trueString s2="hello word";System.out.println(s2.contains("o")); // trueSystem.out.println(s2.indexOf("h")); // 0System.out.println(s2.indexOf("o", 5)); // 7 返回的是s2中的索引System.out.println(s2.lastIndexOf("o")); // 7System.out.println(s2.lastIndexOf("l", 2)); // 2System.out.println(s1.indexOf("lo"));System.out.println(s1.indexOf("lo",5)); // 返回的还是s2中从前往后出现位置的索引
String str1 = "北京尚硅谷教育北京";String str2 = str1.replace('北', '东');System.out.println(str1); // 北京尚硅谷教育北京System.out.println(str2); // 东京尚硅谷教育东京String str3 = str1.replace("北京", "上海"); // 上海尚硅谷教育上海System.out.println(str3);System.out.println("*************************");String str = "12hello34world5java7891mysql456";// 把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");System.out.println(string); // hello,world,java,mysqlSystem.out.println("*************************");str = "12345";// 判断str字符串中是否全部有数字组成,即有1-n个数字组成boolean matches = str.matches("\\d+");System.out.println(matches); // trueString tel = "0571-4534289";// 判断这是否是一个杭州的固定电话boolean result = tel.matches("0571-\\d{7,8}");System.out.println(result); // trueSystem.out.println("*************************");str = "hello|world|java";String[] strs = str.split("\\|");for (String value : strs) {System.out.println(value);}System.out.println();str2 = "hello.world.java";String[] strs2 = str2.split("\\.");for (String s : strs2) {System.out.println(s);}
String与其他结构的转换
String与基本数据类型、包装类之间的转换
String—>基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)也可以Xxx.valueOf()
基本数据类型、包装类—>String:调用String重载的valueOf(xxx)
String str1 = "123";int num = Integer.parseInt(str1);String str2 = String.valueOf(num); // "123"String str3 = num + "";System.out.println(str1 == str3);
与字符数组之间的转换
String —> char[]:调用String的toCharArray() / getChars()
char[] —> String:调用String的构造器
String str1 = "abc123"; //题目:a21cb3char[] charArray = str1.toCharArray();for (int i = 0; i < charArray.length; i++) {System.out.println(charArray[i]);}char[] arr = new char[]{'h','e','l','l','o'};String str2 = new String(arr);System.out.println(str2);
与字节数组之间的转换
String —> byte[]:调用String的getBytes()
byte[]—> String:调用String的构造器
编码:字符串—>字节 (看得懂的数据—->二进制数据)
解码:编码的逆过程,字节—>字符串(二进制数据 —->看得懂的数据)
说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码
public void test() throws UnsupportedEncodingException {String str1 = "abc123中国";byte[] bytes = str1.getBytes(); // 使用默认的字符集,进行编码。System.out.println(Arrays.toString(bytes));byte[] gbks = str1.getBytes("gbk"); // 使用gbk字符集进行编码。System.out.println(Arrays.toString(gbks));String str2 = new String(bytes); // 使用默认的字符集,进行解码。System.out.println(str2);String str3 = new String(gbks);System.out.println(str3); // 出现乱码。原因:编码集和解码集不一致!String str4 = new String(gbks, "gbk");System.out.println(str4); // 没有出现乱码。原因:编码集和解码集一致!}
与StringBuffer、StringBuilder之间的转换
String —>StringBuffer、StringBuilder:
- 调用new 构造器
- append()方法 ```java String str1 =”helloword”; StringBuffer stringBuffer = new StringBuffer(str1); System.out.println(stringBuffer); // helloword StringBuilder stringBuilder = new StringBuilder(str1); System.out.println(stringBuilder); // helloword
stringBuffer.append(“isStringBuffer”); System.out.println(stringBuffer); stringBuilder.append(“isStringBuider”); System.out.println(stringBuilder);
StringBuffer、StringBuilder -->String:- 调用String构造器- StringBuffer、StringBuilder的 `**toString()**````javaStringBuffer sb1 = new StringBuffer("hello StringBuffer");StringBuilder sb2 = new StringBuilder("hello StringBuider");System.out.println(new String(sb1));System.out.println(new String(sb2));System.out.println(sb1.toString());System.out.println(sb2.toString());
常见算法题目的考查
模拟一个trim方法,去除字符串两端的空格
public String myTrim(String str) {if (str != null) {int start = 0; // 记录从前往后首次索引位置不是空格的位置索引int end = str.length() - 1; // 记录从后往前首次索引位置不是空格的位置索引while (start < end && str.charAt(start) == ' ') {start++;}while (start < end && str.charAt(end) == ' ') {end--;}if (str.charAt(start) == ' ') {return "";}return str.substring(start, end + 1);}return null;}
将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
// 方式一public String reverse1(String str, int start, int end) {if (str != null) {// 1.转换成char型数组char[] charArray = str.toCharArray();// 2.进行反转操作for (int i = start, j = end; i < j; i++, j--) {char temp = charArray[i];charArray[i] = charArray[j];charArray[j] = temp;}//3.返回值return new String(charArray);}return null;}// 方式二// 分析:整个字符串分为三部分不反转的、反转的、不反转的// 先将前面不反转的部分取出来,将反转的部分取出后进行拼接public String reverse2(String string, int start, int end) {if(string != null){//第一部分String newStr = string.substring(0, start);//第二部分for (int i = end; i >= start; i--) {newStr += string.charAt(i);}//第三部分newStr += string.substring(end + 1);//拼接操作return newStr;}return null;}//方式三,使用StringBuffer或StringBuilder替换String优化public String reverse3(String str, int start, int end) {if(str != null){//1.新建StringBufferStringBuffer stringBuffer = new StringBuffer(str.length());//2.第一部分stringBuffer.append(str.substring(0, start));//3.第二部分for (int i = end; i >= start; i--) {stringBuffer.append(str.charAt(i));}//4.第三部分stringBuffer.append(str.substring(end + 1));//5.拼接操作return stringBuffer.toString();}return null;}
获取一个字符串在另一个字符串中出现的次数
比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
public int count(String mainStr, String subStr) {// 1.判断主串和部分串的大小if (mainStr.length() >= subStr.length()) {int index = 0;int count = 0;//2.在主串中取出子串下标,并将新的下标赋值给主串,统计量加1// while ((index = mainStr.indexOf(subStr) )!= -1){// count++;// //从已经找到的子串的下一个开始// mainStr = mainStr.substring(index + subStr.length());// }//改进,不再新建字符串,只做位置比对while ((index = mainStr.indexOf(subStr, index)) != -1) {index += subStr.length();count++;}return count;} else {return 0;}}
获取两个字符串中最大相同子串
比如:str1 = “abcwerthelloyuiodef“;str2 = “cvhellobnm”
提示:将短的那个串进行长度依次递减的子串与较长的串比较
// 只存在一个子串的情况public String getMaxSameSubString(String str1, String str2) {// 1.判断两个字串的大小if (str1 != null && str2 != null) {String maxStr = (str1.length() >= str2.length()) ? str1 : str2;String minStr = (str1.length() < str2.length()) ? str1 : str2;int len = minStr.length();// 2.用小的依次去比对大的子串for (int i = 0; i < len; i++) { //这层for循环用来确定需要比对的字符次数for (int x = 0, y = len - i; y <= len; x++, y++) {if (maxStr.contains(minStr.substring(x, y))) {return minStr.substring(x, y);}}}}return null;}//存在多个相同子串的情况// 此时先返回String[],后面可以用集合中的ArrayList替换,较方便public String[] getMaxSameSubStrings(String str1, String str2) {// 1.先比较出两个子串的大小if (str1 != null && str2 != null) {StringBuffer stringBuffer = new StringBuffer();String maxStr = (str1.length() > str2.length()) ? str1 : str2;String minStr = (str1.length() > str2.length()) ? str2 : str1;// 2.用小的去依次匹配大的int len = minStr.length();for (int i = 0; i < len; i++) {for (int x = 0, y = len - i; y <= len; x++,y++ ){String subString = minStr.substring(x,y);// 3.取出匹配到的子串if (maxStr.contains(subString)){stringBuffer.append(subString+",");}}//System.out.println(stringBuffer);if (stringBuffer.length() != 0){break;}}String [] split = stringBuffer.toString().replaceAll(",$","").split("\\,");return split;}return null;}
对字符串中字符进行自然顺序排序。 提示:
- 字符串变成字符数组
- 对数组排序,择,冒泡,Arrays.sort();
- 将排序后的数组变成字符串
@Testpublic void charTest() {String str1 = "hello java";char[] charArray = str1.toCharArray();Arrays.sort(charArray);String str2 = new String(charArray);System.out.println(str2);}
StringBuffer 类

概述
java.lang.String.Buffer代表可变的字符序列,JDK1.0中声明,
- 是线程安全的可变字符串
- 可以对字符串内容进行增删,此时不会产生新的对象
- 很多方法与String相同 作为参数传递时,方法内部可以改变值
StringBuffer类不同于 String,其对象必须使用构造器生成
abstract class AbstractStringBuilder implements Appendable, CharSequence {/*** The value is used for character storage.*/char[] value; //value没有final声明,value可以不断扩容/*** The count is the number of characters used.*/int count; //count记录有效字符个数
构造器
**StringBuffer()**初始容量为16的字符串缓冲区**StringBuffer(int size)**构造指定容量的字符串缓冲区**StringBuffer(String str)**将内容初始化为指定字符串内容String s = new String("我喜欢学习");StringBuffer buffer = new StringBuffer("我喜欢学习");buffer. append("数学");

方法**public int capacity()**返回当前容量。理论值(水杯最多可以装多少水)**public int length()**返回长度(字符数)。实际值(实际上水杯有多少水)添加功能
**public StringBuffer append(String str)**可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身**public StringBuffer insert(int offset,String str)**在指定位置插入xxx
- 删除功能
**public StringBuffer deleteCharAt(int index)**删除指定位置的字符,并返回本身**public StringBuffer delete(int start, int end)**
- 替换功能
**public void setCharAt(int n ,char ch)**设置指定位置的字符**StringBuffer replace(int start, int end, String str)**从start开始到end用str替换(包括start不包括end)
- 反转功能(Stirng类没有)
**public StringBuffer reverse()** 字符串反转
- 截取功能
**public String substring(int start)****public String substring(int start, int end)**
- 转换功能
**String toString()**
**public int indexOf(String str)**返回子串的下标**public char charAt(int n)**返回指定位置的字符
当 append和insert时,如果原来vaue数组长度不够,扩容
如上这些方法支持方法链操作。 方法链的原理
@Overridepublic StringBuilder append(String str) {super.append(str);return this;}
public void stringBufferMethodTest(){StringBuffer s1 = new StringBuffer("abc");s1.append(1);s1.append('1');System.out.println(s1);// s1.delete(2,4);// s1.replace(2,4,"hello");// s1.insert(2,false);// s1.reverse();String s2 = s1.substring(1, 3);System.out.println(s1);System.out.println(s1.length());System.out.println(s2);}
StringBuilder 类
StringBuilder和 StringBuffer非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样,只是StringBuilder类没有加线程锁,执行效率更高
String、StringBuffer、StringBuilder三者的对比
- String:不可变的字符序列;底层使用char[]存储;占用内存(会不断的创建和回收对象)
- StringBuffer:可变的字符序列;线程安全的,效率低;线程安全;底层使用char[]存储;
- StringBuilder:可变的字符序列;jdk5.0新增,线程不安全,效率高;底层使用char[]存储
注意:作为参数传递的话,方法内部String不会改变其值, StringBuffer和 StringBuilder会改变其值
StringBuffer与StringBuilder的内存解析
以StringBuffer为例
String str = new String(); //char[] value = new char[0];String str1 = new String("abc"); //char[] value = new char[]{'a','b','c'};//char[] value = new char[16];底层创建了一个长度是16的数组。StringBuffer sb1 = new StringBuffer();System.out.println(sb1.length());sb1.append('a'); //value[0] = 'a';sb1.append('b'); //value[1] = 'b';//char[] value = new char["abc".length() + 16];StringBuffer sb2 = new StringBuffer("abc");
StringBuffer构造器源码
public StringBuffer(String str) {super(str.length() + 16);append(str);}
问题1. System.out.println(sb2.length()); //接上面例子,length为3
问题2. 扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组
默认情况下,扩容为原来容量的 2倍 + 2,同时将原有数组中的元素复制到新的数组中
如果扩容了还不够,就将value.length + 添加的长度作为扩容后数组长度
指导意义:开发中建议大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity)
对比String、StringBuffer、StringBuilder三者的执行效率
从高到低排列:StringBuilder > StringBuffer > String
//初始设置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));
IDEADebug
public class IDEADebug {@Testpublic void testStringBuffer(){String str = null;StringBuffer sb = new StringBuffer();sb.append(str); // null会增加"null"System.out.println(sb.length()); // 4System.out.println(sb); // "null"StringBuffer sb1 = new StringBuffer(str); // 抛异常NullPointerException// 因为要调用str.length();System.out.println(sb1);}}



