Javajavase

String 类

java.lang.String类的使用

概述

String类代表字符串,使用一对 “” 引起来,Java程序中的所有字符串字面值(如”abc”)都作为此类的实例实现

  • String声明为final的,不可被继承
  • String实现了Serializable接口:表示字符串是支持序列化的
  • String实现了Comparable接口:表示String可以比较大小
  • String内部定义了final char[] value用于存储字符串数据
  • String代表不可变的字符序列。简称:不可变性
  • 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
  • 字符串常量池中是不会存储相同内容的字符串的

    1. public final class String
    2. implements java.io.Serializable, Comparable<String>, CharSequence {
    3. /** The value is used for character storage. */
    4. private final char value[];
    5. /** Cache the hash code for the string */
    6. 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

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1379492/1589298853642-c7be6f10-3562-4aa5-b94e-1556cac3e357.png#height=383&id=L3Hgn&margin=%5Bobject%20Object%5D&originHeight=766&originWidth=1440&originalType=binary&ratio=1&size=0&status=done&style=shadow&width=720)<br />**为什么String要设计成不可变的**
  2. - 运行时常量池的需要
  3. 多个相同的String共用一个对象,若是可以修改,会导致其他引用同时改变
  4. - 同步
  5. - 允许String对象缓存hashcode
  6. 字符串不变性保证了hash码的唯一性
  7. - 安全性
  8. String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,<br />还有反射机制所需要的String参数等,假若String不是固定不变的,将会引起各种安全隐患
  9. <a name="f01d80f10f60e7e74884f79092521223"></a>
  10. #### String实例化方式说明
  11. 方式一:通过字面量定义的方式<br />方式二:通过new + 构造器的方式
  12. ```java
  13. //通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
  14. String s1 = "javaEE";
  15. String s2 = "javaEE";
  16. //通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
  17. String s3 = new String("javaEE");
  18. String s4 = new String("javaEE");
  19. System.out.println(s1 == s2); // true
  20. System.out.println(s1 == s3); // false
  21. System.out.println(s1 == s4); // false
  22. System.out.println(s3 == s4); // false
  23. System.out.println("***********************");
  24. Person p1 = new Person("Tom", 12);
  25. Person p2 = new Person("Tom", 12);
  26. System.out.println(p1.name.equals(p2.name)); // true
  27. System.out.println(p1.name == p2.name); // true
  28. p1.name = "Jerry";
  29. System.out.println(p2.name); // Tom

字符串类 - 图2

面试题
String s = new String(“abc”); 方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:”abc”
String str1=“abc”; 与 String str2= new String(“abc”); 的区别
字符串类 - 图3

字符串拼接方式赋值对比

说明

  • 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量
  • 只要其中一个是变量,结果就在堆中
  • 如果拼接的结果调用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

  1. ```java
  2. String s1 = "javaEEhadoop";
  3. String s2 = "javaEE";
  4. String s3 = s2 + "hadoop";
  5. System.out.println(s1 == s3); // false
  6. final String s4 = "javaEE"; // s4:字符常量
  7. String s5 = s4 + "hadoop";
  8. System.out.println(s1 == s5); // true

字符串类 - 图4
String使用陷阱

  1. 1. String s1 = "a";
  2. 在字符串常量池中创建了一个字面量为 "a" 的字符串
  3. 2. s1 = s1 + "b"
  4. 实际上原来的“a”字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s1 + "b"(也就是 "ab")。
  5. 如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。
  6. 如果这样的操作放到循环中,会极大影响程序的性能
  7. 3. String s2 = "ab";
  8. 4. String s3 = "a" + "b";
  9. s3指向字符串常量池中已经创建的 "ab" 的字符串。
  10. 5. String s4 = s1.intern();
  11. 堆空间的S1对象在调用 intern () 之后,会将常量池中已经存在的 "ab" 字符串赋值给s4
  1. //一道面试题
  2. public class StringTest {
  3. String str = new String("good");
  4. char[] ch = { 't', 'e', 's', 't' };
  5. public void change(String str, char ch[]) {
  6. str = "test ok";
  7. ch[0] = 'b';
  8. }
  9. public static void main(String[] args) {
  10. StringTest ex = new StringTest();
  11. ex.change(ex.str, ex.ch);
  12. System.out.println(ex.str); // good
  13. System.out.println(ex.ch); // best
  14. }
  15. }

JVM中涉及字符串的内存结构

  1. jdk 1.6:字符串常量池存储在方法区(永久区)
  2. jdk 1.7:字符串常量池存储在堆空间
  3. jdk 1.8:字符串常量池存储在方法区(元空间)

image.png
image.png
image.png

构造器

**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)**
image.png
image.png

方法

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个,如果超过剩下的全部都放到最后一个元素中

  1. String s1 = "helloworld";
  2. System.out.println(s1.length()); // 10
  3. System.out.println(s1.charAt(4)); // o
  4. System.out.println(s1.isEmpty()); // false
  5. String s2 = "HelloWorld";
  6. System.out.println(s2.toLowerCase()); // helloworld
  7. System.out.println(s2.toUpperCase()); // HELLOWORLD
  8. String s3 = " hello world ";
  9. System.out.println(s3.trim()); //hello world
  10. String s4 = "helloworld";
  11. System.out.println(s4.equals(s1)); // true
  12. System.out.println(s4.equalsIgnoreCase(s2)); // true
  13. String s5 = "hello";
  14. System.out.println(s5.compareTo(s4)); // -5 相等时返回0,小的时候返回负数
  15. System.out.println(s4.compareTo(s1)); // 0
  16. System.out.println(s4.substring(5)); // world
  17. System.out.println(s4.substring(5, 9)); // worl,取值范围左闭右开
  1. String s1 = "javaEE";
  2. System.out.println(s1.endsWith("EE")); // true
  3. System.out.println(s1.startsWith("a")); // false
  4. System.out.println(s1.startsWith("EE", 4)); // true
  5. String s2="hello word";
  6. System.out.println(s2.contains("o")); // true
  7. System.out.println(s2.indexOf("h")); // 0
  8. System.out.println(s2.indexOf("o", 5)); // 7 返回的是s2中的索引
  9. System.out.println(s2.lastIndexOf("o")); // 7
  10. System.out.println(s2.lastIndexOf("l", 2)); // 2
  11. System.out.println(s1.indexOf("lo"));
  12. System.out.println(s1.indexOf("lo",5)); // 返回的还是s2中从前往后出现位置的索引
  1. String str1 = "北京尚硅谷教育北京";
  2. String str2 = str1.replace('北', '东');
  3. System.out.println(str1); // 北京尚硅谷教育北京
  4. System.out.println(str2); // 东京尚硅谷教育东京
  5. String str3 = str1.replace("北京", "上海"); // 上海尚硅谷教育上海
  6. System.out.println(str3);
  7. System.out.println("*************************");
  8. String str = "12hello34world5java7891mysql456";
  9. // 把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉
  10. String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
  11. System.out.println(string); // hello,world,java,mysql
  12. System.out.println("*************************");
  13. str = "12345";
  14. // 判断str字符串中是否全部有数字组成,即有1-n个数字组成
  15. boolean matches = str.matches("\\d+");
  16. System.out.println(matches); // true
  17. String tel = "0571-4534289";
  18. // 判断这是否是一个杭州的固定电话
  19. boolean result = tel.matches("0571-\\d{7,8}");
  20. System.out.println(result); // true
  21. System.out.println("*************************");
  22. str = "hello|world|java";
  23. String[] strs = str.split("\\|");
  24. for (String value : strs) {
  25. System.out.println(value);
  26. }
  27. System.out.println();
  28. str2 = "hello.world.java";
  29. String[] strs2 = str2.split("\\.");
  30. for (String s : strs2) {
  31. System.out.println(s);
  32. }

String与其他结构的转换

String与基本数据类型、包装类之间的转换

String—>基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)也可以Xxx.valueOf()
基本数据类型、包装类—>String:调用String重载的valueOf(xxx)

  1. String str1 = "123";
  2. int num = Integer.parseInt(str1);
  3. String str2 = String.valueOf(num); // "123"
  4. String str3 = num + "";
  5. System.out.println(str1 == str3);

与字符数组之间的转换

String —> char[]:调用String的toCharArray() / getChars()
char[] —> String:调用String的构造器

  1. String str1 = "abc123"; //题目:a21cb3
  2. char[] charArray = str1.toCharArray();
  3. for (int i = 0; i < charArray.length; i++) {
  4. System.out.println(charArray[i]);
  5. }
  6. char[] arr = new char[]{'h','e','l','l','o'};
  7. String str2 = new String(arr);
  8. System.out.println(str2);

与字节数组之间的转换

String —> byte[]:调用String的getBytes()
byte[]—> String:调用String的构造器
编码:字符串—>字节 (看得懂的数据—->二进制数据)
解码:编码的逆过程,字节—>字符串(二进制数据 —->看得懂的数据)
说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码

  1. public void test() throws UnsupportedEncodingException {
  2. String str1 = "abc123中国";
  3. byte[] bytes = str1.getBytes(); // 使用默认的字符集,进行编码。
  4. System.out.println(Arrays.toString(bytes));
  5. byte[] gbks = str1.getBytes("gbk"); // 使用gbk字符集进行编码。
  6. System.out.println(Arrays.toString(gbks));
  7. String str2 = new String(bytes); // 使用默认的字符集,进行解码。
  8. System.out.println(str2);
  9. String str3 = new String(gbks);
  10. System.out.println(str3); // 出现乱码。原因:编码集和解码集不一致!
  11. String str4 = new String(gbks, "gbk");
  12. System.out.println(str4); // 没有出现乱码。原因:编码集和解码集一致!
  13. }

与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);

  1. StringBufferStringBuilder -->String:
  2. - 调用String构造器
  3. - StringBufferStringBuilder `**toString()**`
  4. ```java
  5. StringBuffer sb1 = new StringBuffer("hello StringBuffer");
  6. StringBuilder sb2 = new StringBuilder("hello StringBuider");
  7. System.out.println(new String(sb1));
  8. System.out.println(new String(sb2));
  9. System.out.println(sb1.toString());
  10. System.out.println(sb2.toString());

常见算法题目的考查

模拟一个trim方法,去除字符串两端的空格

  1. public String myTrim(String str) {
  2. if (str != null) {
  3. int start = 0; // 记录从前往后首次索引位置不是空格的位置索引
  4. int end = str.length() - 1; // 记录从后往前首次索引位置不是空格的位置索引
  5. while (start < end && str.charAt(start) == ' ') {
  6. start++;
  7. }
  8. while (start < end && str.charAt(end) == ' ') {
  9. end--;
  10. }
  11. if (str.charAt(start) == ' ') {
  12. return "";
  13. }
  14. return str.substring(start, end + 1);
  15. }
  16. return null;
  17. }

将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”

  1. // 方式一
  2. public String reverse1(String str, int start, int end) {
  3. if (str != null) {
  4. // 1.转换成char型数组
  5. char[] charArray = str.toCharArray();
  6. // 2.进行反转操作
  7. for (int i = start, j = end; i < j; i++, j--) {
  8. char temp = charArray[i];
  9. charArray[i] = charArray[j];
  10. charArray[j] = temp;
  11. }
  12. //3.返回值
  13. return new String(charArray);
  14. }
  15. return null;
  16. }
  17. // 方式二
  18. // 分析:整个字符串分为三部分不反转的、反转的、不反转的
  19. // 先将前面不反转的部分取出来,将反转的部分取出后进行拼接
  20. public String reverse2(String string, int start, int end) {
  21. if(string != null){
  22. //第一部分
  23. String newStr = string.substring(0, start);
  24. //第二部分
  25. for (int i = end; i >= start; i--) {
  26. newStr += string.charAt(i);
  27. }
  28. //第三部分
  29. newStr += string.substring(end + 1);
  30. //拼接操作
  31. return newStr;
  32. }
  33. return null;
  34. }
  35. //方式三,使用StringBuffer或StringBuilder替换String优化
  36. public String reverse3(String str, int start, int end) {
  37. if(str != null){
  38. //1.新建StringBuffer
  39. StringBuffer stringBuffer = new StringBuffer(str.length());
  40. //2.第一部分
  41. stringBuffer.append(str.substring(0, start));
  42. //3.第二部分
  43. for (int i = end; i >= start; i--) {
  44. stringBuffer.append(str.charAt(i));
  45. }
  46. //4.第三部分
  47. stringBuffer.append(str.substring(end + 1));
  48. //5.拼接操作
  49. return stringBuffer.toString();
  50. }
  51. return null;
  52. }

获取一个字符串在另一个字符串中出现的次数
比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数

  1. public int count(String mainStr, String subStr) {
  2. // 1.判断主串和部分串的大小
  3. if (mainStr.length() >= subStr.length()) {
  4. int index = 0;
  5. int count = 0;
  6. //2.在主串中取出子串下标,并将新的下标赋值给主串,统计量加1
  7. // while ((index = mainStr.indexOf(subStr) )!= -1){
  8. // count++;
  9. // //从已经找到的子串的下一个开始
  10. // mainStr = mainStr.substring(index + subStr.length());
  11. // }
  12. //改进,不再新建字符串,只做位置比对
  13. while ((index = mainStr.indexOf(subStr, index)) != -1) {
  14. index += subStr.length();
  15. count++;
  16. }
  17. return count;
  18. } else {
  19. return 0;
  20. }
  21. }

获取两个字符串中最大相同子串
比如:str1 = “abcwerthelloyuiodef“;str2 = “cvhellobnm”
提示:将短的那个串进行长度依次递减的子串与较长的串比较

  1. // 只存在一个子串的情况
  2. public String getMaxSameSubString(String str1, String str2) {
  3. // 1.判断两个字串的大小
  4. if (str1 != null && str2 != null) {
  5. String maxStr = (str1.length() >= str2.length()) ? str1 : str2;
  6. String minStr = (str1.length() < str2.length()) ? str1 : str2;
  7. int len = minStr.length();
  8. // 2.用小的依次去比对大的子串
  9. for (int i = 0; i < len; i++) { //这层for循环用来确定需要比对的字符次数
  10. for (int x = 0, y = len - i; y <= len; x++, y++) {
  11. if (maxStr.contains(minStr.substring(x, y))) {
  12. return minStr.substring(x, y);
  13. }
  14. }
  15. }
  16. }
  17. return null;
  18. }
  19. //存在多个相同子串的情况
  20. // 此时先返回String[],后面可以用集合中的ArrayList替换,较方便
  21. public String[] getMaxSameSubStrings(String str1, String str2) {
  22. // 1.先比较出两个子串的大小
  23. if (str1 != null && str2 != null) {
  24. StringBuffer stringBuffer = new StringBuffer();
  25. String maxStr = (str1.length() > str2.length()) ? str1 : str2;
  26. String minStr = (str1.length() > str2.length()) ? str2 : str1;
  27. // 2.用小的去依次匹配大的
  28. int len = minStr.length();
  29. for (int i = 0; i < len; i++) {
  30. for (int x = 0, y = len - i; y <= len; x++,y++ ){
  31. String subString = minStr.substring(x,y);
  32. // 3.取出匹配到的子串
  33. if (maxStr.contains(subString)){
  34. stringBuffer.append(subString+",");
  35. }
  36. }
  37. //System.out.println(stringBuffer);
  38. if (stringBuffer.length() != 0){
  39. break;
  40. }
  41. }
  42. String [] split = stringBuffer.toString().replaceAll(",$","").split("\\,");
  43. return split;
  44. }
  45. return null;
  46. }

对字符串中字符进行自然顺序排序。 提示:

  1. 字符串变成字符数组
  2. 对数组排序,择,冒泡,Arrays.sort();
  3. 将排序后的数组变成字符串
    1. @Test
    2. public void charTest() {
    3. String str1 = "hello java";
    4. char[] charArray = str1.toCharArray();
    5. Arrays.sort(charArray);
    6. String str2 = new String(charArray);
    7. System.out.println(str2);
    8. }

    StringBuffer 类

    image.png
    概述
    java.lang.String.Buffer代表可变的字符序列,JDK1.0中声明,
  • 是线程安全的可变字符串
  • 可以对字符串内容进行增删,此时不会产生新的对象
  • 很多方法与String相同 作为参数传递时,方法内部可以改变值
  • StringBuffer类不同于 String,其对象必须使用构造器生成

    1. abstract class AbstractStringBuilder implements Appendable, CharSequence {
    2. /**
    3. * The value is used for character storage.
    4. */
    5. char[] value; //value没有final声明,value可以不断扩容
    6. /**
    7. * The count is the number of characters used.
    8. */
    9. int count; //count记录有效字符个数

    构造器

  • **StringBuffer()**初始容量为16的字符串缓冲区

  • **StringBuffer(int size)**构造指定容量的字符串缓冲区
  • **StringBuffer(String str)**将内容初始化为指定字符串内容

    1. String s = new String("我喜欢学习");
    2. StringBuffer buffer = new StringBuffer("我喜欢学习");
    3. buffer. append("数学");

    字符串类 - 图11
    方法
    **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数组长度不够,扩容
如上这些方法支持方法链操作。 方法链的原理

  1. @Override
  2. public StringBuilder append(String str) {
  3. super.append(str);
  4. return this;
  5. }
  1. public void stringBufferMethodTest(){
  2. StringBuffer s1 = new StringBuffer("abc");
  3. s1.append(1);
  4. s1.append('1');
  5. System.out.println(s1);
  6. // s1.delete(2,4);
  7. // s1.replace(2,4,"hello");
  8. // s1.insert(2,false);
  9. // s1.reverse();
  10. String s2 = s1.substring(1, 3);
  11. System.out.println(s1);
  12. System.out.println(s1.length());
  13. System.out.println(s2);
  14. }

StringBuilder 类

StringBuilder和 StringBuffer非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样,只是StringBuilder类没有加线程锁,执行效率更高
String、StringBuffer、StringBuilder三者的对比

  • String:不可变的字符序列;底层使用char[]存储;占用内存(会不断的创建和回收对象)
  • StringBuffer:可变的字符序列;线程安全的,效率低;线程安全;底层使用char[]存储;
  • StringBuilder:可变的字符序列;jdk5.0新增,线程不安全,效率高;底层使用char[]存储

    注意:作为参数传递的话,方法内部String不会改变其值, StringBuffer和 StringBuilder会改变其值

StringBuffer与StringBuilder的内存解析

以StringBuffer为例

  1. String str = new String(); //char[] value = new char[0];
  2. String str1 = new String("abc"); //char[] value = new char[]{'a','b','c'};
  3. //char[] value = new char[16];底层创建了一个长度是16的数组。
  4. StringBuffer sb1 = new StringBuffer();
  5. System.out.println(sb1.length());
  6. sb1.append('a'); //value[0] = 'a';
  7. sb1.append('b'); //value[1] = 'b';
  8. //char[] value = new char["abc".length() + 16];
  9. StringBuffer sb2 = new StringBuffer("abc");

StringBuffer构造器源码

  1. public StringBuffer(String str) {
  2. super(str.length() + 16);
  3. append(str);
  4. }

问题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

  1. //初始设置
  2. long startTime = 0L;
  3. long endTime = 0L;
  4. String text = "";
  5. StringBuffer buffer = new StringBuffer("");
  6. StringBuilder builder = new StringBuilder("");
  7. //开始对比
  8. startTime = System.currentTimeMillis();
  9. for (int i = 0; i < 20000; i++) {
  10. buffer.append(String.valueOf(i));
  11. }
  12. endTime = System.currentTimeMillis();
  13. System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
  14. startTime = System.currentTimeMillis();
  15. for (int i = 0; i < 20000; i++) {
  16. builder.append(String.valueOf(i));
  17. }
  18. endTime = System.currentTimeMillis();
  19. System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
  20. startTime = System.currentTimeMillis();
  21. for (int i = 0; i < 20000; i++) {
  22. text = text + i;
  23. }
  24. endTime = System.currentTimeMillis();
  25. System.out.println("String的执行时间:" + (endTime - startTime));

IDEADebug

  1. public class IDEADebug {
  2. @Test
  3. public void testStringBuffer(){
  4. String str = null;
  5. StringBuffer sb = new StringBuffer();
  6. sb.append(str); // null会增加"null"
  7. System.out.println(sb.length()); // 4
  8. System.out.println(sb); // "null"
  9. StringBuffer sb1 = new StringBuffer(str); // 抛异常NullPointerException
  10. // 因为要调用str.length();
  11. System.out.println(sb1);
  12. }
  13. }