String、StringBuffer、StringBuilder详细介绍、如何使用及区别

引言:相信很多朋友初学Java的字符串中,对于String、StringBuffer还有StringBuilder的使用区分不清。在后续使用多线程以及程序的执行速度,对于使用三者的方法尤为重要,下面我将为大家介绍三者的使用细节、方法和区别

😶‍🌫️String

String的特性

1.String是一个final类,代表不可变的字符序列
2.字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
这里之所以说String是final类,我们需要深究String底层的源码
在这里插入图片描述
由上图我们可以看出在Java8后String的底层是通过char数组实现的,并且使用final修饰

String为什么不可变 – 源码分析

相信很多小伙伴在学习Java的过程中,也会有人告诉这会是面试官问到的内容

String的不可变特性,由上面String的底层的源码可以看出,有final修饰。那么为什么是final呢?

  • 线程安全,同一个字符串实现可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
  • 支持hash映射和缓存,因为Srting的hash值经常会使用到,比如作为Map的键,不可变的特性使得hash值也不会变,不需要重新计算。
  • 字符传常量池优化。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用

String的方法

equals方法和 ==

关于==和equals,两者启着判断是否相等的作用,这里关于二者的细节不多赘述,之后我会发博客详细介绍,下面我们来介绍一下equals方法的使用。

我们先来看一个练习题👇

public class test01 {
    public static void main(String[] args) {
        String abc = new String("qby");
        String abc1 = new String("qby");
        System.out.println(abc == abc1); //false
        System.out.println(abc.equals(abc1)); //true
    }
}
public class test01 {
    public static void main(String[] args) {
        Cat a = new Cat("a");
        Cat b = new Cat("b");
        System.out.println(a.name == b.name); //false
        System.out.println(a.name.equals(b.name));  //false
    }
}
class Cat {
    public String name; //实际开发中,我们设置private,这里设置public是因为方便调用

    public Cat(String name) {
        this.name = name;
    }
}

equals源码分析

通过上述代码,我们能发现什么呢?
大家都知道使用 == 我们new对象时,jvm会分配给我们一个内存地址,所以==判断的结果为false。
那么为什么equals方法判断一个为false,而另一个为true呢?
我们一起来看String中equals的底层源码👇
在这里插入图片描述
这里我把equals源码分为两部分

  • 第一个是传入Object类,就是我们上述的Cat类,this是a类object是b类,因为是new方法实现,所以地址不一样,返回false
  • 第二个是传入的方法是否为String类型,将他们变成数组存储一个一个逐一判断,都相等返回true,不相等返回false

String常用方法

package StringMethod;

public class Method {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "Hello";
        System.out.println(str1.equals(str2)); //false

        //1.忽略大小写判断内容是否相等
        String username = "johN";
        if ("john".equalsIgnoreCase(username)) {
            System.out.println("success!");
        } else {
            System.out.println("Failure!");
        } //success!

        System.out.println("qwdqwd".length()); //6

        //2.indexOf 获取字符在字符串对象中的第一次出现的索引,索引从0开始,找不到返回-1
        String s1 = "wdqwd@dwa";
        int index = s1.indexOf("@");
        int index1 = s1.indexOf("w", 5); //从第五个位置开始查找
        System.out.println(index); //5
        System.out.println(index1); //7

        //3.lastIndexOf 获取字符在字符串最后一次出现的索引,索引从0开始 找不到返回-1
        index = s1.lastIndexOf("d");
        System.out.println(index); //6

        //4.截取指定范围字符串   左闭右开
        String name = "hello,张三";
        System.out.println(name.substring(6)); //张三

        //5.从索引0开始截取到第5个
        System.out.println(name.substring(0, 5)); //hello

        //6.全部转为大小写
        String a = "hello";
        System.out.println(a.toUpperCase()); //HELLO
        System.out.println(a.toLowerCase()); //hello

        //7.拼接
        String s2 = "123456";
        s2 = s2.concat("dawd").concat("qwe");
        System.out.println(s2); //123456dawdqwe

        //8.替换  原来的 teststr没有影响  replaceAll replaceFirst
        String teststr = "abcabcabcabcabc";
        String teststr2 = teststr.replace("a", "k");
        System.out.println(teststr); //abcabcabcabcabc
        System.out.println(teststr2); //kbckbckbckbckbc

        //9.split 分割
        //String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
        //String[] split = poem.split(",");

        String poem = "E:\\aaa\\bbb";
        String[] split = poem.split("\\\\");
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]); //E:  aaa   bbb
        }

        //10.toCharArry 转成字符数组
        char[] chs = s2.toCharArray();
        for (int i = 0; i < chs.length; i++) {
            System.out.print(chs[i]); //123456dawdqwe
        }

        //11.compareTo 比较两个字符串大小    源码
        //1. jac和jack时 返回 -1  长度-长度
        //2. Hello 和 HEllo时 返回32 ASCLL表差值
        String s4 = "jchn";
        String s5 = "jack";
        System.out.println(s4.compareTo(s5)); // 2

        //12.判断字串是否存在
        String s6 = "helloworld";
        if (s6.contains("hello")) {
            System.out.println("查询成功"); //查询成功
        }

        //13.判断开头结尾
        String s7 = "##@@hello**";
        System.out.println(s7.startsWith("##")); //true
        System.out.println(s7.startsWith("@", 2));//索引第2个位置是否已@开头 //true
        System.out.println(s7.endsWith("**")); //true

        //14.判断是否为空 isEmpty
        String s8 = "";
        System.out.println(s8.isEmpty()); //true


        //15.trim 去掉字符串左右两边空格 中间保留
        String s9 = "   hello  world     ";
        System.out.println("[" + s9.trim() + "]"); //[hello  world]


        //16.去掉全部空格
        System.out.println(s9.replaceAll(" ", "")); //helloworld


        //17.format
        int age = 18;
        String MyName = "qby";
        String info = MyName.format("我的名字是%s,年龄是%d", MyName,age);
        System.out.println(info); //我的名字是qby,年龄是18
    }
}

String其他注意事项

  • 第一个
public class test01 {
    public static void main(String[] args) {
        String s1 = "abc";
        s1 = "qby";
        System.out.println(s1); //qby
    }
}

我们都知道最后s1得出的答案是qby,但是重点是String底层怎么分配的,在String重新赋值时,不会覆盖之前的内容,而是重新指向常量池的另一个内容,在此过程中String创建了2个对象
在这里插入图片描述

  • 第二个
    当调用intern方法时,如果池中已经包含一个等于此String对象的字符串,则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。

public class test01 {
    public static void main(String[] args) {
        String a = "qby";
        String b = new String("qby");
        System.out.println(a == b); //false
        System.out.println(a == b.intern()); //true
        System.out.println(a.equals(b)); //true
    }
}
  • 第三个

public class test01 {
    public static void main(String[] args) {
        String a = "qby";
        String b = "hello";
        String c = b + a;
        System.out.println(c); //helloqby
    }
}

在此过程中,创建了三个对象,重点是其底层实现的原理,底层是StringBuilder ab = new StringBuilder(); ab.append(a); ab.append(b); ab在堆中,并且append是在原来字符串的基础上追加的。
如果是String c = “hello” + “qby”; 则是常量相加,是在池中;String c = a + b;是在堆中

🧐StringBuffer

常用方法

package StringBufferMethod;

public class StringBuffer1 {
    public static void main(String[] args) {
        StringBuffer s = new StringBuffer("hello");
        //增
        s.append(",");
        s.append("张三丰").append("100").append("true");
        System.out.println(s); //hello,张三丰100true

        //删
        /*
            删除 [)
        */
        s.delete(9,12);
        System.out.println(s); //hello,张三丰true

        //修改
        s.replace(9,13,"666");
        System.out.println(s); //hello,张三丰666

        //查
        int index = s.indexOf("张三丰");
        System.out.println(index); //6

        //插入  原来索引为9的内容自动后移
        s.insert(9,"赵敏");
        System.out.println(s); //hello,张三丰赵敏666


        //替换 replace  左开右闭
        s.replace(11,14,"123");
        System.out.println(s); //hello,张三丰赵敏123

        //reverse
        s.reverse();
        System.out.println(s); //321敏赵丰三张,olleh


    }
}

注意


public class test01 {
    public static void main(String[] args) {
        String str = null;
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(str);
        System.out.println(stringBuffer.length()); //4
    }
}

源码分析
在这里插入图片描述

直接在new StringBuilder() 中使用,报错空指针异常


public class test01 {
    public static void main(String[] args) {
        String str = null;
        StringBuffer stringBuffer= new StringBuffer(str);
        System.out.println(stringBuffer.length()); //java.lang.NullPointerException
    }
}

🤓StringBilder

StringBuilder的方法和StringBuffer一样,在这里就不再重复

  • StringBuilder继承AbstractStringBuilder类
  • 实现了Serializable ,说明StringBuilder对象是可以串行化(对象可以网络传输,可以保存到文件)
  • StringBuilder是final类不能被继承(线程安全)
  • StringBuilder对象字符序列仍然是存放在其父类** AbstractStringBuilder的 char[] value;**
    因此,字符序列是堆中
  • StringBuilder 的方法,没有做互斥的处理,即没有synchronized关键字,因此在单线程的情况下使用
    stringBuilder

👻String、StringBuffer、StringBuilder的区别

名字字符序列效率安全性
String不可变效率低安全
StringBuffer可改变效率高安全
StringBuilder可改变效率最高不安全

选择String、StringBuffer、StringBuilder总结:

  • 如果字符串存在大量的修改操作,一般使用 StringBuffer或StringBuilder
  • 如果字符串存在大量的修改操作,并在单线程的情况,使用 StringBuilder
  • 如果字符串存在大量的修改操作,并在多线程的情况,使用 StringBuffer
  • 如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等

👦结语

最后我也想告诉大家,以及告诫自己,双非并不是自己进不了大厂的借口,或许只是缺乏逼自己一把的勇气吧。少研究别人,多提升自己,不是成功来的慢,而是自己努力的不够狠,努力只能及格,拼命才能优秀。加油吧!

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Javaer.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值