【Java基础】11.String类,StringBuffer和StringBuilder拼接字符串


前言

字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

一、String类

1.创建字符串

创建字符串最简单的方法:

String str = "Runoob";

其他创建字符串的方法:

public class StringDemo2 {
    public static void main(String[] args) {
        // 创建方法1:直接使用双引号得到字符串对象(最常用)
        String s1 = "Hello World";
        System.out.println("s1 = " + s1); // s1 = Hello World

        // 创建方法2:public String(String): 根据传入的字符串内容,来创建字符串对象(几乎不用)
        String s2 = new String("Java");
        System.out.println("s2 = " + s2); // s2 = Java

        // 创建方法3:public String(char[] c): 根据字符数组的内容,来创建字符串对象(几乎不用)
        char[] chars = {'我', '爱', 'J', 'a', 'v', 'a'};
        String s3 = new String(chars);
        System.out.println("s3 = " + s3); // s3 = 我爱Java

        // 创建方法4:public String(byte[] b):  根据字节数组的内容,来创建字符串对象(几乎不用)
        byte[] bytes = {97, 98, 99, 65, 66, 67};
        String s4 = new String(bytes);
        System.out.println("s4 = " + s4); // s4 = abcABC

    }
}

2.创建字符串的位置

String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆内存上:

public class StringDemo2 {
    public static void main(String[] args) {
        System.out.println("----创建String位置(常量池、堆内存)----");
        String ss1 = "abc";
        String ss2 = "abc";
        // 相同内容在字符串常量池中只存储一份,所以引用地址相同
        System.out.println("ss1 == ss2 = " + (ss1 == ss2)); // ss1 == ss2 = true

        char[] chars1 = {'a', 'b', 'c'};
        // 通过构造器new对象,每次new都会产生新对象,放在堆内存中,所以引用地址不同
        String ss3 = new String(chars1);
        String ss4 = new String(chars1);
        System.out.println("ss3 == ss4 = " + (ss3 == ss4)); // ss3 == ss4 = false
    }
}

3.String常用的API

下面是 String 类支持的方法,更多方法,可以查看API文档。

public class StringAPIOtherDemo5 {
    public static void main(String[] args) {
        // 1、length(): 获取字符串的长度
        String name = "我爱你中国";
        System.out.println("变量name的长度 = " + name.length()); // 变量name的长度 = 5

        // 2、charAt(int index): 获取某个索引位置处的字符
        char c = name.charAt(1); // 0我 1爱 2你 3中 4国
        System.out.println("变量name索引1处的字符 = " + c); // 变量name索引1处的字符 = 爱

        String s1 = "Hello";
        String s2 = "hello";
        String s3 = "Hello";
        // 3、equals比较区分大小写
        System.out.println("s1.equals(s2) = " + s1.equals(s2)); // s1.equals(s2) = false
        System.out.println("s1.equals(s3) = " + s1.equals(s3)); // s1.equals(s3) = true

        // 4、equalsIgnoreCase比较不区分大小写
        System.out.println("s1.equalsIgnoreCase(s2) = " + s1.equalsIgnoreCase(s2)); // s1.equalsIgnoreCase(s2) = true
        System.out.println("s1.equalsIgnoreCase(s3) = " + s1.equalsIgnoreCase(s3)); // s1.equalsIgnoreCase(s3) = true

        // 5、hashCode返回此字符串的哈希码
        System.out.println("s1.hashCode() = " + s1.hashCode()); // s1.hashCode() = 69609650
        System.out.println("s2.hashCode() = " + s2.hashCode()); // s2.hashCode() = 99162322
        System.out.println("s3.hashCode() = " + s3.hashCode()); // s3.hashCode() = 69609650

        // 6、toCharArray():把字符串转换成字符数组
        System.out.println("~~~~把字符串转换成字符数组再遍历~~~~");
        char[] chars = name.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char ch = chars[i];
            System.out.println(ch);
        }

        // 7、substring(int beginIndex, int endIndex) :截取内容,(包前不包后的)
        System.out.println("~~~~截取字符串~~~~");
        String name2 = "Java是最厉害的编程语言!";
        // 0J 1a 2v 3a 4是 5最 6厉 7害 8的 9编 10程 11语 12言 13!
        String rs = name2.substring(0, 9);
        System.out.println(rs); // Java是最厉害的
        String rs1 = name2.substring(4, 9);
        System.out.println(rs1); // 是最厉害的

        // 8、substring(int beginIndex):从当前索引一直截取到末尾
        System.out.println("~~~~截取从中间到最后~~~~");
        String rs2 = name2.substring(4);
        System.out.println(rs2); // 是最厉害的编程语言!

        // 9、replace(CharSequence target, CharSequence replacement):字符串替换
        System.out.println("~~~~字符串替换~~~~");
        String name3 = "孙悟空有金箍棒,孙悟空会法术!我好爱孙悟空";
        String rs3 = name3.replace("孙悟空", "***");
        System.out.println(rs3); // ***有金箍棒,***会法术!我好爱***

        // 10、contains(CharSequence s):判断是否存在
        System.out.println("~~~~判断是否存在~~~~");
        System.out.println(name3.contains("孙悟空")); // true
        System.out.println(name3.contains("猪八戒")); // false

        // 11、startsWith(String prefix):判断是否由prefix开始
        System.out.println("~~~~判断是否由prefix开始~~~~~");
        System.out.println(name3.startsWith("孙悟空")); // true
        System.out.println(name3.startsWith("孙悟空有")); // true
        System.out.println(name3.startsWith("孙悟空没有")); // false

        // 12、split(String s): 按照某个内容把字符串分割成字符串数组返回。
        System.out.println("~~~~分割字符串~~~~");
        String name4 = "王宝强,贾乃亮,陈羽凡";
        String[] names = name4.split(","); // 用,将字符串进行分割
        for (int i = 0; i < names.length; i++) {
            System.out.println("选择了:" + names[i]);
        }
        
    }
}

编译运行结果如下:

变量name的长度 = 5
变量name索引1处的字符 = 爱
s1.equals(s2) = false
s1.equals(s3) = true
s1.equalsIgnoreCase(s2) = true
s1.equalsIgnoreCase(s3) = true
s1.hashCode() = 69609650
s2.hashCode() = 99162322
s3.hashCode() = 69609650
~~~~把字符串转换成字符数组再遍历~~~~
我
爱
你
中
国
~~~~截取字符串~~~~
Java是最厉害的
是最厉害的
~~~~截取从中间到最后~~~~
是最厉害的编程语言!
~~~~字符串替换~~~~
***有金箍棒,***会法术!我好爱***
~~~~判断是否存在~~~~
true
false
~~~~判断是否由prefix开始~~~~~
true
true
false
~~~~分割字符串~~~~
选择了:王宝强
选择了:贾乃亮
选择了:陈羽凡

4.String类不可变

在 Java 中,String 底层不可变的原因是因为在String类的实现中,使用了byte数组(byte array)来存储字符串内容,并且在创建String对象时将字符数组声明为final,从而保证了字符串内容的不可修改性。
String类不可变
首先看这段代码:

public class StringDemo1 {
    public static void main(String[] args) {
        String name = "超级";
        System.out.println("name = " + name);
        System.out.println("name.hashCode() = " + name.hashCode());
        name += "赛亚人"; // name = name + "赛亚人"
        System.out.println("name = " + name);
        System.out.println("name.hashCode() = " + name.hashCode());
        name += "贝吉塔"; // name = name + "赛亚人" + "贝吉塔"
        System.out.println("name = " + name); // 根据地址找到内容
        System.out.println("name.hashCode() = " + name.hashCode());
    }
}

编译运行结果如下:

name = 超级
name.hashCode() = 1155522
name = 超级赛亚人
name.hashCode() = 99837177
name = 超级赛亚人贝吉塔
name.hashCode() = -2127590481

从运行结果可以看出,name的值的确改变了。其实不然,因为name只是一个String对象的引用,并不是String对象本身。
当运行给变量name赋值的代码之后,会先在方法区的运行时常量池创建一个String对象"超级",然后在Java栈中创建一个String对象的引用name,并让name指向"超级"。再在常量池创建String对象"超级赛亚人",再让name引用新的String对象的地址,所以每次name的哈希值都不一样。

二、拼接字符串

因为 String 类是不可变类,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String 。因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢。

StringBuffer 和 StringBuilder 特点及使用场景:

  • 应用于对字符串进行修改的时候,特别是字符串对象经常改变的情况下。
  • 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
  • 由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

1.“+” 拼接字符串

因为 String 类是不可变类,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,不经常改变字符串对象的时候,可以用 “+” 拼接字符串。

public class StringDemo {
    public static void main(String[] args) {
        String name = "小明";
        System.out.println("name.hashCode() = " + name.hashCode()); // name.hashCode() = 756703
        String name1 = "小明";
        System.out.println("name1.hashCode() = " + name1.hashCode()); // name1.hashCode() = 756703
        // 哈希值一样,说明引用地址一样,常量池只创建了一个对象
        String s1 = "喜欢学习";
        String s2 = "Java";
        String rs = name + s1 + s2; // 用 "+" 拼接字符串
        System.out.println("rs = " + rs); // rs = 小明喜欢学习Java
    }
}

编译运行结果如下:

name.hashCode() = 756703
name1.hashCode() = 756703
rs = 小明喜欢学习Java

2.StringBuilder 拼接字符串

StringBuilder 的API方法和基本用法与 StringBuffer 一样。
StringBuilder 是可变类,任何对它所指代的字符串的改变都不会产生新的对象。
单线程操作大量数据,用 StringBuilder。
StringBuilder 并没有对方法进行加 synchronized(同步锁),所以是非线程安全的。

/**
 * @ClassName: StringBuilderDemo1
 * @Description: StringBuilder 拼接字符串
 * 单线程操作大量数据,用StringBuilder。
 * StringBuilder 并没有对方法进行加synchronized(同步锁),所以是非线程安全的。
 * StringBuffer和StringBuilder这两个可变类的原理和操作基本相同。
 * @author: Zh
 * @date: 2024/4/8 16:41
 */
public class StringBuilderDemo1 {
    public static void main(String[] args) {

        // StringBuilder的字符串拼接.append
        StringBuilder sb = new StringBuilder();
        sb.append("a"); // 字符
        sb.append(1); // 整数
        sb.append(false); // 布尔类型
        sb.append(3.3); // 小数
        sb.append("abc"); // 字符串
        System.out.println("append的应用:" + sb); // append的应用:a1false3.3abc

        StringBuilder sb1 = new StringBuilder();
        // 支持链式编程
        sb1.append("a").append("b").append("c").append("我爱你中国");
        System.out.println("append支持链式编程:" + sb1); // append支持链式编程:abc我爱你中国

        // reverse反转
        sb1.reverse();
        System.out.println("reverse反转:" + sb1); // reverse反转:国中你爱我cba

        // 字符串长度length
        System.out.println("字符串长度length:" + sb1.length()); // 字符串长度length:8

        // 注意:StringBuilder只是拼接字符串的手段:效率好。
        // 最终的目的还是要恢复成String类型。
        StringBuilder sb2 = new StringBuilder();
        sb2.append("123").append("阿拉伯数字");
        // check(sb2); // 报错,因为sb2不是String类型
        System.out.println("拼接字符串:" + sb2); // 拼接字符串:123阿拉伯数字
        // 恢复成String类型
        String rs = sb2.toString();
        check(rs); // String类型:123阿拉伯数字
    }

    /**
     * 检查传入的参数是否是String类型
     *
     * @param abc
     */
    public static void check(String abc) {
        System.out.println("String类型:" + abc);
    }

}

编译运行结果如下:

append的应用:a1false3.3abc
append支持链式编程:abc我爱你中国
reverse反转:国中你爱我cba
字符串长度length:8
拼接字符串:123阿拉伯数字
String类型:123阿拉伯数字

3.StringBuffer 拼接字符串

StringBuffer 的API方法和基本用法与 StringBuilder 一样。
StringBuffer 是可变类,任何对它所指代的字符串的改变都不会产生新的对象。
多线程操作大量数据,用 StringBuffer。
StringBuffer 对方法加了synchronized(同步锁)或者对调用的方法加了同步锁,所以是线程安全的。

/**
 * @ClassName: StringBufferDemo1
 * @Description: StringBuffer 拼接字符串
 * 多线程操作大量数据,用StringBuffer。
 * StringBuffer 对方法加了synchronized(同步锁)或者对调用的方法加了同步锁,所以是线程安全的。
 * StringBuffer和StringBuilder这两个可变类的原理和操作基本相同。
 * @author: Zh
 * @date: 2024/4/8 16:56
 */
public class StringBufferDemo1 {
    public static void main(String[] args) {

        // 创建StringBuffer对象
        StringBuffer sb = new StringBuffer("孙悟空");

        // 在字符串后面追加新的字符串
        sb.append("会龟派气功");
        System.out.println("StringBuffer对象:" + sb); // StringBuffer对象:孙悟空会龟派气功

        // 删除指定位置上的字符串,从指定的下标开始和结束,下标从0开始
        // 0孙 1悟 2空 3会 4龟 5派 6气 7功
        sb.delete(2, 4); // 2空 3会
        System.out.println("delete删除:" + sb); // delete删除:孙悟龟派气功

        // 在指定下标位置上添加指定的字符串
        sb.insert(2, "123");
        System.out.println("insert新增:" + sb); // insert新增:孙悟123龟派气功

        // 将字符串翻转
        sb.reverse();
        System.out.println("reverse反转:" + sb); // reverse反转:功气派龟321悟孙

        //将StringBuffer转换成String类型
        String s = sb.toString();
        check(s); // String类型:功气派龟321悟孙
    }

    /**
     * 检查传入的参数是否是String类型
     *
     * @param abc
     */
    public static void check(String abc) {
        System.out.println("String类型:" + abc);
    }

}

编译运行结果如下:

StringBuffer对象:孙悟空会龟派气功
delete删除:孙悟龟派气功
insert新增:孙悟123龟派气功
reverse反转:功气派龟321悟孙
String类型:功气派龟321悟孙

4.StringBuilder 和 StringBuffer 区别

  • 单线程操作大量数据,用 StringBuilder。
  • 多线程操作大量数据,用 StringBuffer。
  • StringBuilder 并没有对方法进行加 synchronized(同步锁),所以是非线程安全的。
  • StringBuffer 对方法加了synchronized(同步锁)或者对调用的方法加了同步锁,所以是线程安全的。
  • 由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
  • 在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
  • 39
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值