String、StringBuffer、StringBuilder源码解析

Easter79
• 阅读 547

String源码分析

一:实现接口。

public final class String
    implements java.io.Serializable, Comparable, CharSequence {

  • java.io.Serializable

    这个序列化接口没有任何方法和域,仅用于标识序列化的语意。

              解读:这是一个里面什么都没有的接口;

如果一个接口里面什么内容都没有,那么这个接口是一个标识接口,比如,一个学生遇到一个问题,排错排了几天也没解决,此时,她举手了(示意我去帮他解决),然后我过去,帮他解决了,那么这个举手其实就是一个标识,自己不能解决的问题标示我去帮他解决,在Java中的这个Serializable接口是给JVM看的,告诉JVM,我不做这个类的序列化了,你(JVM)给我序列化,序列化就是变成二进制流,比如云计算、Hadoop,特别是Hadoop完全就是分布式环境,那么就要涉及到对象要在网络中传输,里面的全是二进制流,当然你来做这个序列化操作也可以,但是这个类里面可能还有一个类,如果你把外面的类对象Person变成二进制,那么里面也要序列化(这要用到深度遍历,很麻烦),干脆告诉JVM,让他来帮你做。
serializable接口就是Java提供用来进行高效率的异地共享实例对象的机制,实现这个接口即可。

序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

  • Comparable

    这个接口只有一个compareTo(T 0)接口,用于对两个实例化对象比较大小。

  • CharSequence

    这个接口是一个只读的字符序列。包括length(), charAt(int index), subSequence(int start, int end)这几个API接口,值得一提的是,StringBuffer和StringBuild也是实现了改接口。

String、StringBuffer、StringBuilder源码解析

可以看出 String 不变性的原因

final 修饰class,说明 String 类不能被继承,即 String 的方法,都不会被重写final 修饰 String 中保存数据的 char 的数组 value,value一旦被赋值,内存地址不可更改,而且 value 的权限是 private 的,外部绝对访问不到,String 也没有提供可以对 value 赋值的对外方法,所以 value 一旦产生,内存地址根本无法被修改。就是,充分利用了 final 的特性。因为 String 具有不变性,所以 String 的大多数操作方法,都会返回新的 String。

StringBuffer源码分析

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ...
}

StringBuffer类被 final 所修饰,因此不能被继承。

StringBuffer类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuilder也是继承于AbstractStringBuilder类。

StringBuffer类实现了2个接口:

  • Serializable 序列化接口,表示对象可以被序列化。
  • CharSequence 字符序列接口,提供了几个对字符序列进行只读访问的方法,比如:length()、charAt()、subSequence()、toString()方法等。

总结

StringBuffer类将所有操作字符序列的方法都添加了 synchronized 关键字来修饰,因此,StringBuffer类是线程安全的。

  • StringBuffer类使用了一个char数组来存储字符。该数组是一个动态的数组,当存储容量不足时,会对它进行扩容。
  • StringBuffer对象是一个可变的字符序列。
  • StringBuffer类是线程安全的。

StringBuilder源码分析

StringBuilder类被 final 所修饰,因此不能被继承。

StringBuilder类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuffer也是继承于AbstractStringBuilder类。

StringBuilder类实现了2个接口:

  • Serializable 序列化接口,表示对象可以被序列化。
  • CharSequence 字符序列接口,提供了几个对字符序列进行只读访问的方法,比如:length()、charAt()、subSequence()、toString()方法等。

总结

  • StringBuilder类使用了一个char数组来存储字符。该数组是一个动态的数组,当存储容量不足时,会对它进行扩容。
  • StringBuilder对象是一个可变的字符序列。
  • StringBuilder类是非线程安全的。

小结

三者的区别

  • StringBuffer和StringBuilder类型的字符串可以改变,且不会产生新的未使用字符串对象
  • String类型的字符串不能在原字符串上做修改
  • StringBuffer效率低,线程安全
  • StringBuilder(JDK5提出)效率高,非线程安全

如何选择

  • 如果程序运行过程中不需要对字符串进行改变则使用String
  • 多线程操作字符串缓冲区下操作大量数据则使用StringBuffer
  • 单线程操作字符串缓冲区下操作大量数据则使用StringBuilder

一个小案例 证明StringBuffer和StringBuilder的线程安全问题

StringBuffer sbuf = new StringBuffer();
sbuf.append(1);

StringBuilder sbui = new StringBuilder();
sbui.append(1);

sbuf的append方法会调用:

@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(int i) {
    toStringCache = null;
    super.append(i);
    return this;
}

sbui的append方法会调用:

@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(int i) {
    super.append(i);
    return this;
}

可以看到,StringBuffer类的append方法前加上了synchronized关键字保证了线程安全。但是这两个append方法最终调用的都是父类AbstractStringBuilder中的append方法:

public AbstractStringBuilder append(String str) {
    if (str == null) {
        return appendNull();
    }
    int len = str.length();
    ensureCapacityInternal(count + len);
    putStringAt(count, str);
    count += len;
    return this;
}

下面通过Java多线程编程测试下StringBuffer和StringBuilder的线程安全问题:

public class RunnableTest implements Runnable {
    public StringBuilder str = new StringBuilder("");
//    public StringBuffer str = new StringBuffer("");
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        str.append(Thread.currentThread().getName());
        System.out.println(str+"--"+str.length());
    }
}


public class StringTest {
    public static void main(String[] args) {
        RunnableTest rt = new RunnableTest();

        new Thread(rt, "aa").start();
        new Thread(rt, "bb").start();
        new Thread(rt, "cc").start();
        new Thread(rt, "dd").start();
        new Thread(rt, "ee").start();
        new Thread(rt, "ff").start();
    }
}

使用线程不安全的StringBuilder类时的结果,可以看到字符串的长度计算是有问题的,这就是因为字符串拼接和长度计算的代码块没有保障线程安全:
String、StringBuffer、StringBuilder源码解析
使用线程安全的StringBuffer类时的结果:
String、StringBuffer、StringBuilder源码解析

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k