• Java【String】【StringBuilder】【StringBuffer】你都会用吗



    前言

    📕各位读者好, 我是小陈, 这是我的个人主页
    📗小陈还在持续努力学习编程, 努力通过博客输出所学知识
    📘如果本篇对你有帮助, 烦请点赞关注支持一波, 感激不尽
    📙 希望我的专栏能够帮助到你:
    JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
    Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
    JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)

    在校招和笔试过程中,字符串是相当频繁被问到的话题,在之前的文章【数据类型与变量】中提到了,Java中设计了一种引用数据类型:String,可以专门来存储字符串。

    今天主要介绍不可变的String,以及可变的StringBuilder和StringBuffer


    提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎评论区指点~ 废话不多说,直接上干货!

    一、常用的方法

    1、字符串构造

    我们常用的定义一个字符串的方式为:

            String str = "我爱你中国";
    
    • 1

    我们知道,String是一个引用数据类型,是一个类,类可以new对象,所以还可以这样定义一个字符串:

            String str = new String("我爱你中国");
    
    • 1

    实际上,正因为String是一个类,所以这样的写法才标准,只不过编译器提供了一种简化的写法,就是直接赋值

    要学习String这个类,首先要从这个类的构造方法学起

    在IDEA中——敲两次shift——导航栏搜索String——选择class——选择String java.lang
    就可以看到String这个类中所有的成员属性和方法了

    例如:
    在这里插入图片描述
    这只是一部分,也是最常用的三种定义字符串的方式,代码如下:

            // 1
            String str1 = new String();
            str1 = "我爱你中国";
            // 2
            String str2 = new String("我爱你中国");
            // 3
            char[] value = {'我','爱','你','中','国'};
            String str3 = new String(value);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    简单了解了String类的构造方法之后,需要注意的是:
    String 内部并不存储字符串本身

    我们打开调试窗口:
    在这里插入图片描述
    可以看到,String类new出来的三个对象中的成员变量都只有两个:value( 数组类型 ) 和 hash
    hash有什么作用咱先别管 ,只需看到value这个数组,可以推断,实际上 字符串是被存放在了一个数组中,而数组也是一个引用类型,可以理解为value的值就是数组的地址

    例如 str1 这个字符串,可以简单理解为:
    在这里插入图片描述


    2、字符串比较

    一般有两种场景:1,字符串是否相同 2,大小是否相等

    1,判断字符串是否相同:

            String str1 = new String("我爱你中国");
            String str2 = new String("我爱你中国");
            
            System.out.println(str1 == str2);
            // 输出:false
            System.out.println(str1.equals(str2));
            // 输出:true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    因为 str1 和 str2 存储的都是对象的地址,两个对象的地址不同
    equals 是用来比较两个引用指向的对象的内容是否一致

    2,比较字符串大小关系

            String str1 = new String("我爱你中国");
            String str2 = new String("我爱你中国");
            String str3 = new String("我爱你中国我爱你中国");
    
            System.out.println(str1.compareTo(str2));
            // 输出:0
            System.out.println(str2.compareTo(str3));
            // 输出:-5
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    compareTo 是用来比较两个引用指向的对象的内容大小关系
    比较方式:
    先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值
    如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值

            String str1 = new String("iloveyou");
            String str2 = new String("ILOVEYOU");
    
            System.out.println(str1.compareTo(str2));
            // 输出:32
            System.out.println(str1.compareToIgnoreCase(str2));
            // 输出:0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    compareToIgnoreCasecompareTo方式相同,但是忽略大小写比较


    3、字符串查找

    方法功能
    char charAt (int index)返回 index 位置上字符,如果 index 为负数或者越界,抛出 IndexOutOfBoundsException 异常
    int indexOf (int ch)返回 ch 第一次出现的位置,没有返回-1
    int indexOf (int ch, intfromIndex)从 fromIndex 位置开始找 ch 第一次出现的位置,没有返回-1
    int indexOf (String str)返回 str 第一次出现的位置,没有返回-1
    int indexOf (String str, intfromIndex)从 fromIndex 位置开始找str第一次出现的位置,没有返回-1
    int lastIndexOf (int ch)从后往前找,返回 ch 第一次出现的位置,没有返回-1
    int lastIndexOf (int ch, intfromIndex)从 fromIndex 位置开始找,从后往前找 ch 第一次出现的位置,没有返回-1
    int lastIndexOf (String str)从后往前找,返回 str 第一次出现的位置,没有返回-1
    int lastIndexOf (String str, intfromIndex)从 fromIndex 位置开始找,从后往前找 str 第一次出现的位置,没有返回-1

    1,char charAt (int index)

            String str = "我爱你中国";
            System.out.println(str.charAt(0));
            // 输出第一个字符:我
            System.out.println(str.charAt(10));
            // 找不到第10个字符,抛出 IndexOutOfBoundsException 异常 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    因为字符串存放在数组中,所以编号和数组下标一致,从0开始

    2, int indexOf(int ch)

            String str = "ababcabcdabcde";
            System.out.println(str.indexOf('c'));
            // 找到 str 这个字符串中第一次出现 c 的位置,在4下标处
            // 输出:4
    
    • 1
    • 2
    • 3
    • 4

    3,int indexOf (int ch, intfromIndex)
    多了一个参数,表示起始位置的下标,如果不写第二个参数,默认从0下标处开始找

            String str = "ababcabcdabcde";
            System.out.println(str.indexOf('c',4));
            // 从4位置开始找   输出:4
            System.out.println(str.indexOf('c',5));
            // 从5位置开始找   输出:7
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4,int indexOf (String str)

            String str = "ababcabcdabcde";
            System.out.println(str.indexOf("abc"));
            // 找字符串 "abc" 在 str 中出现的位置
            // 输出:2
    
    • 1
    • 2
    • 3
    • 4

    实现这个方法,有一种比较有难度的算法:KMP算法,感兴趣的小伙伴可以看看我写的这篇文章:大白话式解析KMP算法

    5,int indexOf (String str, intfromIndex)
    多了一个参数,表示起始位置的下标,如果不写第二个参数,默认从0下标处开始找

            String str = "ababcabcdabcde";
            System.out.println(str.indexOf("abc"));
            System.out.println(str.indexOf("abc",3));
            // 从3位置开始找 "abc"   输出:5
    
    • 1
    • 2
    • 3
    • 4

    6,int lastIndexOf (int ch)
    方法名多了一个单词:last
    这个方法表示从后往前找

            String str = "ababcabcdabcde";
            System.out.println(str.lastIndexOf('a'));
            // 从后往前找到第一次出现字符'a',返回'a'的下标
            // 输出:9
    
    • 1
    • 2
    • 3
    • 4

    7,int lastIndexOf (int ch, intfromIndex)

            String str = "ababcabcdabcde";
            System.out.println(str.lastIndexOf('a',8));
            // 从8位置处开始从后往前找,相当于把8位置当成最后一位
            // 输出:5
    
    • 1
    • 2
    • 3
    • 4

    剩下的就不再赘述啦,原理同上


    4、字符串转化

    1,将数字转字符串:

            String str = String.valueOf(123);
            System.out.println(str);
    
    • 1
    • 2

    valueOf能转化的类型有很多:
    在这里插入图片描述
    这就是重载的好处,不需要设计那么多方法名

    2、字符串转数字:

            String str = "123";
            int a = Integer.parseInt(str);
            System.out.println(a);
    
    • 1
    • 2
    • 3

    3,大小写转化:

            String str1 = "love";
            String str2 = "LOVE";
            System.out.println(str1.toUpperCase());
            // 输出:LOVE
            System.out.println(str2.toLowerCase());
            // 输出:love
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4,字符串转数组

            String str = "我爱你中国";
            char[] array = str.toCharArray();
    
    • 1
    • 2

    5,数组转字符串

            char[] array = {'我','爱','你','中','国'};
            String str = new String(array);
    
    • 1
    • 2

    5、字符串替换

            String str = "abababaabab";
            String newStr1 = str.replaceAll('a','b');
            // 将字符 'a' 全部替换为 'b'
            String newStr1 = str.replaceFirst('a','b');
            // 将首个字符 'a' 替换为 'b'
            System.out.println(newStr);
            // 输出:bbababaabab
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    6、字符串分割

    将一个完整的字符串按照指定的分隔符划分为若干个子字符串

    1,String[] split (String regex) 将字符串全部拆分

            String str = "I LOVE YOU";
            String[] strs = str.split(" ");
            // 按照空格分割 返回值类型为字符型数组
            for (String newstr : strs) {
            // 遍历数组
                System.out.println(newstr);
            }
    // 输出:
    // I
    // LOVE
    // YOU
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2,String[] split (String regex, int limit) 将字符串以指定的格式,拆分为 limit

            String str = "I LOVE YOU";
            String[] strs = str.split(" "2);
            // 多了一个参数,表示分割成两组
            for (String newstr : strs) {
            // 遍历数组
                System.out.println(newstr);
            }
    // 输出:
    // I
    // LOVE YOU
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意:
    字符" | ", " + ", " . ", " * " 都得加上转义字符,前面加上 " \ "
    而如果是 " \ " ,那么就得写成 " \\ "
    如果一个字符串中有多个分隔符,可以用 " | " 作为连字符,例如:

            String str = "I-LOVE=YOU";
            String[] strs = str.split("-|=");
            for (String newstr : strs) {
                System.out.println(newstr);
            }
    // 输出:
    // I
    // LOVE
    // YOU
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    7、字符串截取

    从一个完整的字符串之中截取出部分内容

    1,String substring (int beginIndex) 从指定索引截取到结尾

            String str = "我爱你中国";
            String newstr = str.substring(2);
            // 从2下标位置开始,截取之后的所有字符
            System.out.println(newstr);
            // 输出:你中国
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2,String substring (int beginIndex, int endIndex) 截取部分内容

            String str = "我爱你中国";
            String newstr = str.substring(2,3);
            // 从2下标位置开始,截取到3下标位置(不包含3下标)
            System.out.println(newstr);
            // 输出:你
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、字符串的不可变性

    为什么说字符串是不可变的呢?

    说到字符串的不可变性就要提出“ 常量池 ” 的概念了,这里暂不讲解,随着学习的深入,之后再做分享,现在先简单分析一下:

    在这里插入图片描述
    可以看到第114行 value 这个数组是被 private final 修饰的,那不可变性是因为 private 还是因为 final 呢?

    关于 final:
    final 修饰类表明该类不想被继承,final 修饰 引用类型 表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的

            final int[] array = {1,2,3,4,5};
            array[0] = 10;
            System.out.println(array[0]);
            // 可以改变array指向的对象的值
    
            array = new int[]{1,2,3,4,5};
            // 报错,不能再改变array的指向
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    真正使字符串不可变的是因为 private ,把 value 封装起来了,使得value只能在String这个类里面访问,并且String类中没有提供 setter 和 getter 方法,所以不能在String类外范围value

    了解即可:
    为什么 String 要设计成不可变的?(不可变对象的好处是什么?)
    方便实现字符串对象池,如果 String 可变,那么对象池就需要考虑写时拷贝的问题了
    不可变对象是线程安全的
    不可变对象更方便缓存 hash code,作为 key 时可以更高效的保存到 HashMap 中


    三、StringBuilder、StringBuffer

    在了解 StringBuilder 和 StringBuffer 之前呢,我们先了解一下字符串拼接

    Java提供了使用加号(+)拼接字符串的功能,例如:

    public class Test {
        public static void main(String[] args) {
            String str = "我爱你";
            str += "中国";
            System.out.println(str);// 输出:我爱你中国
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但刚刚说过,字符串是不可修改的,所以这种方式的拼接,并不是对str本身进行修改,而是又 new 了不止一个临时对象,所以这种方式的对字符串的拼接,效率十分低下

    如何去证明?打开汇编界面可以证明,博主才疏学浅,就不过多展示了,感兴趣的可以自己去查一下,这里只说结论:

        	str += "中国";
    
    • 1

    这一行代码,实际上是这四行:

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(str);
            stringBuilder.append("中国");
            str = stringBuilder.toString();
    
    • 1
    • 2
    • 3
    • 4

    我们知道,双引号括起来的就是一个字符串对象,并且我们摁住 ctrl 点击 toString 方法,就可以看到 toString 方法的返回值处,也 new 了一个对象
    在这里插入图片描述
    再加上 new 出来的 stringBuilder 对象,可以算出,在 “拼接” 这看似一行代码背后

        	str += "中国";
    
    • 1

    实际上创建了三个临时对象


    所以,如果实在循环当中使用加号对字符串进行拼接,效率可不是一般的低呀,接下来我们就要认识一下 StringBuilder 和 StringBuffer

    StringBuilder 和 StringBuffer 对比 String 最直接的区别就是,StringBuilder 和 StringBuffer new出来的字符串是可变的

    通过 StringBuilder 的 reverse(逆序)方法就可以证明 StringBuilder 的字符串可变性:

    public class Test {
        public static void main(String[] args) {
            StringBuilder stringBuilder = new StringBuilder("我爱你");
            stringBuilder.reverse();
            System.out.println(stringBuilder);// 输出:你爱我
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里我们并没有重新重新定义字符串类型的变量,但打印 stringBuilder 的时候却变成了逆序之后的“你爱我”,说明这个字符串本身确实是被修改了

    我们还可以通过StringBuilder 的 append(附加)方法再证明一下:

    public class Test {
        public static void main(String[] args) {
            StringBuilder stringBuilder = new StringBuilder("我爱你");
    //        stringBuilder.reverse();
            stringBuilder.append("中国").append("母亲");
            System.out.println(stringBuilder);// 输出:我爱你中国母亲
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里可以看到,append() 方法甚至可以连续使用

    这两个栗子就充分说明了,StringBuilder 实例化出来的对象,调用方法的返回值都是字符串本身(this),所以不会产生临时对象
    StringBuffer 同理

    Java 中提供的 StringBuilder 和 StringBuffer 这两个类大部分功能是相同的,这两个类有自己的一些方法是 String 类没有的(互补关系),具体在这里不多赘述了,感兴趣的可以自己查一下在线文档

    那么这两个类又有什么不同呢?
    功能基本都一样,刚刚用 StringBuilder 的代码 StringBuffer 也可以,他俩的区别就是就在于:
    说人话:在多线程时,StringBuffer 内置有 “一把锁” ,会相对安全

    但是!也不能无脑使用 StringBuffer ,频繁的 “上锁”,“开锁” 的过程也会消耗系统资源的呀,等学习到多线程的时候再做分享~


    总结

    以上就是今天要讲的关于【String类】的内容,还涉及到了【StringBuilder】【StringBuffer】,主要介绍了 String 类的一些方法,还希望看过本篇的小伙伴们自己练习一下噢

    如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦🤪🤪🤪


    上山总比下山辛苦
    下篇文章见

  • 相关阅读:
    01 顺序表
    纯 CSS 实现搜索框的展示与隐藏
    RK3568平台开发系列讲解(LCD篇)DRM 显示框架
    激光雷达与自动驾驶详解
    用户信息列表实现增删改查案例的实现【问题及解决过程记录】&【综合案例】
    「分享购」2022年独特的商业模式,让平台实现流量增长
    【微软漏洞分析】MS10-015 Windows 内核异常处理程序漏洞(CVE-2010-0232)
    Go channel被关闭时的广播机制,以及遍历未关闭channel时会导致死锁阻塞问题
    图书馆防盗的窍门
    3-egg-TS-通用后端管理注册系统-图形验证码
  • 原文地址:https://blog.csdn.net/yzhcjl_/article/details/127937201