Loading...
墨滴

HB

2021/11/27  阅读:33  主题:绿意

String、StringBuffer、StringBuilder 全总结

String、StringBuffer、StringBuilder 全总结

Java提供了String、StringBuffer 和 StringBuilder 类来封装字符串,并提供了一系列操作字符串对象的方法。它们的相同点是都用来封装字符串;都实现了CharSequence 接口。

StringDetails
StringDetails

String

  1. String 声明为 final 的,也就是不可以被继承,因而 String 也不是 Java 的基本数据类型
  2. String 实现了 Serializable 接口,说明字符串支持可序列化;实现了 Comparable 接口,表示可以比较大小
  3. String 在内部定义了 final 的 char 型数组,用于存储字符串数组
  4. String 不可变性体现在: 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的 value 进行赋值。当对现有字符串进行连接操作时,需要重新赋值,不能在原有基础上赋值
  5. 通过字面量给一个字符串赋值,此时的字符串值声明在字符串常量池中

String 的实例化方式

方法1:通过字面量的方法,也就是直接给所定义的字符串赋值; 方法2:通过 new + 构造器 的方法;

两种实例化方式的区别:

  • 对于方法1:当使用双引号创建一个字符串时,JVM 首先在字符串池中寻找具有相同值的字符串,如果找到了,它将返回字符串池中的字符串对象的引用。否则,它会存在字符串池中创建字符串对象并返回引用。JVM 通过在不同的线程中使用相同的字符串,节省了大量的内存。

  • 对于方法2:使用 new 创建字符串对象时,每 new 一次都会申请一个内存空间,虽然内容相同,但是地址不同,会在堆中进行创建。

  • Example 1

public static void main(String[] args) {
    
    //通过字面量的方法:此时的s1和s2是声明在方法区中的字符串常量池中,保存在字符串常量池中,栈中存放形参变量 
    String s1 = "JavaEE";
    String s2 = "JavaEE";
    
    //通过new + 构造器的方法: 字符串在堆空间中开辟空间后,此时的s3和s4保存的是地址值,即保存在堆空间中
    String s3 = new String("JavaEE");
    String s4 = new String("JavaEE");

    System.out.println(s1.equals(s2));   //输出 true,比较的是本身值的大小
    System.out.println(s3.equals(s4));   //输出 true,比较的是本身值的大小
    System.out.println(s3 == s4);        //输出 false,比较的是地址值
    
}
  • Example 2
public static void main(String[] args) {

    String s1 = "JavaEE";
    String s2 = "hadoop";


    String s3 = "JavaEEhadoop";
    String s4 = "JavaEE" + "hadoop";   //相当于字面量的连接,在常量池中
    String s5 = s1 + "hadoop";    //有变量 s1 的参与,需要在堆空间中参与

    String s6 = "JavaEE" + s2;    //有变量 s2 的参与,需要在堆空间中参与
    String s7 = s1 + s2;    //有变量的参与,需要在堆空间中参与
    
    System.out.println("s4 = "+s4+", "+"s5 = "+s5+", "+"s6 = "+s6+", "+"s7 = "+s7);
    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(s6 == s7);  //输出 false
    
}

String 的常用方法

1、s1.length() 获取字符串的长度 2、s1.charAt() 获取字符串中指定索引的字符 3、s1.isEmpty() 判断字符串是否为空 4、s1.toLowerCase() 将字符串中所有大写字母转换为小写 5、s1.toUpperCase() 将字符串中所有小写字母转换为大写 6、s1.trim() 返回删除字符串中前后空格后新的字符串 7、s1.equals() 判断两个字符串是否相等 8、s1.equalsIgnoreCase() 忽略大小写的情况下判断两个字符串是否相等 9、s1.concat() 做字符串的拼接,相当于 ”+“ 10、s1.substring() 从指定索引处开始截取字符串,直到末尾 11、s1.substring(index1, index2) 采用 包含索引1,不包含索引2 形式的截取字符串 12、s1.endsWith() 判断字符串是否以指定的后缀结束 13、s1.startsWith() 判断字符串是否以指定的前缀开始 14、s1.contains() 当且仅当此字符串包含指定的char值序列时,返回true 15、s1.indexOf() 返回指定子字符串在此字符串中第一次出现处的索引,未找到返回 -1 16、s1.lastIndexOf() 返回指定子字符串在此字符串中最右边出现处的索引,未找到返回 -1 17、s1.replace(char oldChar, char newChar) 返回一个经过替换后新的字符串 18、s1.matches(String regex) 判断此字符串是否匹配给定的正则表达式

  • Example 1
public class StringMethodsTest {
    
    @Test
    public void test1() {
        String s1 = "   Hello,   World!   ";
        System.out.println("字符串的长度是:" + s1.length());   //输出 21
        System.out.println("字符串中第四个字符是:" + s1.charAt(4));  //输出 e
        System.out.println("字符串是否为空?" + s1.isEmpty());  //输出 false
        System.out.println("将字符串中所有大写字母转换为小写为:" + s1.toLowerCase());
        System.out.println("将字符串中所有小写字母转换为大写为:" + s1.toUpperCase());
        System.out.println("返回删去字符串中前后空格后新的字符串:" + s1.trim());//中间空格不会消失
    }

    @Test
    public void test2() {
        String s2 = "helloWorld";
        String s3 = "HelloWorlD";
        System.out.println(s2.equals(s3));   //输出 false
        System.out.println(s2.equalsIgnoreCase(s3));   //输出 true,忽略大小写
        
        String s4="abc";
        String s5=s4.concat("def");    //字符串的拼接,
        System.out.println(s5);  //输出 abcdef
    }

    @Test
    public void test3() {
        String s1 = "helloWorld";
        System.out.println(s1.substring(5));   //输出 World 从第5个索引开始截取字符串,直到末尾
        System.out.println(s1.substring(1,6)); //输出 elloW 
        /*字符串的第一个字母的索引是从 0 开始*/
    }
    
}
  • Example 2
public class StringMethodsTest {
    
    @Test
    public void test1(){
        String s1="WoAi ni China";
        System.out.println(s1.endsWith("ina")); //输出 true
        System.out.println(s1.startsWith("wo")); //输出false,大小写限制
    }

    @Test
    public void test2(){
        String s2="WoAi ni China";
        boolean s3= s2.contains("China");
        System.out.println(s3);   //输出 true
        System.out.println(s2.indexOf(" "));   //输出 4
        System.out.println(s2.indexOf("ni"6)); //输出 -1
    }
    
    @Test
    public void test3(){
        String s1="Wo shi yi ge coder, I love my hometown";
        System.out.println(s1.replace("love""fall in love with"));
        System.out.println(s1.replace("coder""bian ma ren"));
    }
    
}

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

String -> 基本数据类型、包装类:调用包装类的静态方法(parseXxx) 基本数据类型、包装类 -> String :调用 String 重载的 ValueOf()方法

  • String 与 整型包装类间转换
public static void main(String[] args){
    
    /*将字符串转换为整型类型*/
    String s1 = "123";
    int number = Integer.parseInt(s1);
    System.out.println(number);  //输出 123,但是是整型类型

    String str2 = String.valueOf(number);
    System.out.println(str2);   //输出 123,但是是字符串类型
        
    String str3 = number + "";     //只要有变量参与,就存储在堆中
    System.out.println(str1 == str3);    //false
    
}

String 与 字符数组间的转换

String -> char[]:调用 String.toCharArray() 方法 char[] -> String:调用 String 的构造器

public static void main(String[] args){
   
    String s1 = "hello";
    char[] chars = s1.toCharArray();
    for (char c1 : chars) {
        System.out.print(c1 + ", ");
    }
    System.out.println();
    char[] c2 = new char[]{'h''e''l''l''o'};
    String s2 = new String(c2);
    System.out.println(s2);
    
}

String 与 byte[] 之间的转换

编码:String -> byte[] ,调用 String 的 getBytes() 方法 解码:byte[] -> String ,调用 String 的构造器

编码:字符串 > 字节(转换成二进制数据) 解码:编码的逆过程,字节 > 字符串

public static void main(String[] args){
    
    String s1 = "Hello,中国";
    byte[] bytes = s1.getBytes();    //使用默认的字符集编码
    System.out.println(Arrays.toString(bytes));

    byte[] gbks = s1.getBytes("GBK");//强制使用GBK编码集编码
    System.out.println(Arrays.toString(gbks));

    String s2 = new String(bytes);   //使用默认的字符集解码
    System.out.println(s2);

    String s3 = new String(gbks);   //使用GBK编码集解码
    System.out.println(s3);    //出现乱码,原因:编码集与解码集不一致
    
    String s4 = new String(gbks, "GBK");
    System.out.println(s4);
    
}

StringBuffer 与 StringBuilder

  1. 在 Java1.4 之前,StringBuffer 是字符串操作的唯一选择。但是它的一个缺点是所有公共方法都是同步的,StringBuffer 提供线程安全性,但以性能为代价;

  2. Java1.5 引入了一个新类 StringBuilder,除了线程安全和同步之外,它与 StringBuffer 类似;

  3. 假设在单线程环境中或无关线程安全,要使用 StringBuilder ,因为 StringBuilder 运行速度最快。反之,使用 StringBuffer 进行线程安全的操作;

  4. 和 String 类不同,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象;

String拓展小结
String拓展小结

StringBuffer 与 StringBuilder 的常用方法

1、sb.append() 提供了append()方法,用于进行字符串的拼接 2、sb.delete(int start, int end) 删除指定位置的内容 3、sb.replace(int start, int end, String str) 把 [start, end) 位置替换为 str 4、sb.insert(int offset, str) 在指定位置插入 xxx,插入位置在 offset 前边 5、sb.reverse() 把当前字符序列逆转

  • StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有
// 以 append 方法为例,源码
@Override
public synchronized StringBuffer append(String str){
    
    toStringCache = null;
    super.append(str);
    return this;

}

缓冲区

// StringBuffer 源码
@Override
public synchronized String toString() {
    
    if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache, true);
    
}
// StringBuilder 源码
@Override
public String toString(){
    
    //Create a copy, don't share the array
    return new String(value, 0, count);
    
}

String、StringBuffer 与 StringBuilder 之间转换


String str = "Hello";
StringBuffer sbf = new StringBuffer(str);  // String 转换成 StringBuffer
StringBuilder sbd = new StringBuilder(str); // String 转换成 StringBuilder

str = sbf.toString();  // StringBuffer 转换成 String
str = sbd.toString();  // StringBuilder 转换成 String

// StringBuffer 转换成 StringBuilder
StringBuilder bufferToBuilder = new StringBuilder(sbf.toString);
// StringBuilder 转换成 StringBuffer
StringBuffer builderToBuffer = new StringBuffer(sbd.toString);

Implements
Implements

小结

  • StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串
  • StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串
  • StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 没有对方法加锁同步,所以StringBuilder 的性能要远大于 StringBuffer

HB

2021/11/27  阅读:33  主题:绿意

作者介绍

HB