为什么 StringBuffer 有 toStringCache 而 StringBuilder 没有?
对于 StringBuilder 和 StringBuffer 的源码会发现,StringBuffer 中有一个叫 toStringCache 的成员变量,用来缓存 toString() 方法返回字符串对应的字符数组,而在 StringBuilder 里面却没有。
先看一下代码。
StringBuffer 中的 toString() :
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuilder 中的 toString() :
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
StringBuffer 调用了 String(char[] value, boolean share) 来构造一个 String 对象,它传入了一个字符数组,也就是缓存 toStringCache。Java 中定义,一个 String 对象是常量,是不能被改变的,因此 StringBuffer 的 toString() 返回的对象也应该是不能被改变。也就意味着传入的 toStringCache 数组的元素的值也不能被改变,否则由它构造的 String 对象就会改变。
如下,我们通过反射调用 String(char[] value, boolean share) 构造处一个字符串对象,然后修改 value 数组的值,会发现字符串对象发生了改变。
class ToStringCacheTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<String> constructor = String.class.getDeclaredConstructor(char[].class, boolean.class);
constructor.setAccessible(true);
char[] value = new char[]{'R', 'o', 'b', 'o', 't', 'h', 'y'};
String str = constructor.newInstance(value, true);
System.out.println(str); // 输出:Robothy
value[0] = 'A'; // 修改 str 绑定的字符数组 value
System.out.println(str); // 输出:Aobothy
}
}
StringBuffer 中的 toStringCache 是字符数组 value 复制的一个副本,每当 value 发生改变时,toStringCache 都会被置为空。这就保证了每次只要 StringBuffer 对象发生改变,再调用 toString() 方法就必然产生一个新的 toStringCache 数组,从而保证了引用了旧的 toStringCache 的字符串对象不会发生改变。即使多个线程同时访问 StringBuffer 对象,某一时刻也只有一个线程能够进入修改 toStringCache 和 value 的代码块,这通过修饰 StringBuffer 方法的 synchroinzed 关键字来保证。
如 StringBuffer 中的 append(String str) 方法:
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
而假设 StringBuilder 为了提高效率,专门设计为单线程环境使用,方法没有 synchronized 修饰。如果也和 StringBuffer 一样这么做,非常容易出现不一致的情况,使得已经产生的 String 对象发生改变。
那么 StringBuffer 中 toStringCache 存在的必要性如何?它调用的是下面这个构造方法来创建 String 对象,构造 String 对象时直接共享传入的字符数组 value,而不是像 public String(char value[]) 一样复制一份。
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
从源码中可以知道,StringBuffer 中使用 toStringCache 通过共享一个字符数组,提供构造 String 的速度,这是一个好处。另一个好处是连续多次调用 toString() 方法是不会产生多个内容相同的 String 对象。
但是,这些好处仅仅是在多次调用 toString() 方法且 StringBuffer 对象没有发生改变时才能体现。而实际编写代码的过程中,很少会在没有修改 StringBuffer 的情况下重复调用 toString() 方法,所以它并没有太大的实际作用。
之所以它存在 JDK 源码里,有人解释是由于历史代码遗留的原因,现在不修改是因为它没有什么坏处,修改了反而需要重新测试代码。
参考
stackoverflow.com, Why StringBuffer has a toStringCache while StringBuilder not?
为什么 StringBuffer 有 toStringCache 而 StringBuilder 没有?的更多相关文章
- 证明StringBuffer线程安全,StringBuilder线程不安全
证明StringBuffer线程安全,StringBuilder线程不安全证明StringBuffer线程安全StringBuilder线程不安全测试思想测试代码结果源码分析测试思想分别用1000个线 ...
- java中StringBuffer与String、StringBuilder的区别
在java中我们经常可以看到StringBuffer和String的用法,但是我自己在使用过程中,经常会将两者弄混淆,今天我们就来了解一下两者的区别: 我们首先来看一下我们的官方API中的简单介绍: ...
- [常用类]StringBuffer 类,以及 StringBuilder 类
线程安全,可变的字符序列. 字符串缓冲区就像一个String ,但可以修改. 在任何时间点,它包含一些特定的字符序列,但可以通过某些方法调用来更改序列的长度和内容. 字符串缓冲区可以安全地被多个线程使 ...
- java‘小秘密’系列(一)---String、StringBuffer、StringBuilder
java'小秘密'系列(一)---String.StringBuffer.StringBuilder 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现b ...
- 从源码角度简单看StringBuilder和StringBuffer的异同
概述 StringBuilder和StringBuffer是两个容易混淆的概念,本文从源码入手,简单看二者的异同. 容易知道的是,这两者有一个是线程安全的,而且线程安全的那个效率低. java doc ...
- [十四]基础类型之StringBuffer 与 StringBuilder对比
StringBuilder 和 StringBuffer是高度类似的两个类 StringBuilder是StringBuffer的版本改写,下面从几个方面简单的对比下他们的区别 类继承关系 上文中,我 ...
- StringBuffer 和 StringBuilder 的 3 个区别
StringBuffer 和 StringBuilder 它们都是可变的字符串,不过它们之间的区别是 Java 初中级面试出现几率十分高的一道题.这么简单的一道题,栈长在最近的面试过程中,却经常遇到很 ...
- 字符串之StringBuffer 与 StringBuilder的对比
StringBuilder 和 StringBuffer是高度类似的两个类 StringBuilder是StringBuffer的版本改写,下面从几个方面简单的对比下他们的区别 原文地址:[十四]基础 ...
- java基础解析系列(一)---String、StringBuffer、StringBuilder
java基础解析系列(一)---String.StringBuffer.StringBuilder 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bu ...
随机推荐
- 1、pytorch写的第一个Linear模型(原始版,不调用nn.Modules模块)
参考: https://github.com/Iallen520/lhy_DL_Hw/blob/master/PyTorch_Introduction.ipynb 模拟一个回归模型,y = X * w ...
- NET core 添加了新的nuget包,部署出现Could not load file or assembly
这个坑,今天整了一天,我添加了Microsoft.AspNetCore.Mvc.Versioning包,结果发布到服务器,我复制了dll过去出现了一直找不到加载不成功的问题 Startup.Confi ...
- AtCoder Grand Contest 017 (VP)
contest link Official Editorial 比赛体验--之前做题的时候感觉 AtCoder 挺快的,现在打了VP之后发现还是会挂的--而且不是加载缓慢或者载不出来,直接给你一个无法 ...
- 【题解】NOI 系列题解总集
每次做一道 NOI 系列的估计都很激动吧,对于我这种萌新来说( P1731 [NOI1999]生日蛋糕 练习剪枝技巧,关于剪枝,欢迎看我的垃圾无意义笔记 这道题是有一定难度的,需要运用各种高科技剪枝( ...
- AcWing 324. 贿赂FIPA
题目链接 大型补档计划 \(f[i][j]\) 表示第 \(i\) 个国家,获得 \(j\) 个国家支持,用的最少花费 \(f[i][0] = 0\) \(f[i][sz[i]] = w[i]\) 对 ...
- JavaScript:记录一些字符串和数组常用的方法
字符串: 字符串的方法:注意:都不会改变原来的字符串,返回值为新的字符串.如果(1,2),一般是包含小标1,不包含下标2 str.charAt(i): 取str的第i+1个字符,类似数组的arr[i] ...
- CentOS7搭建Hadoop-3.3.0集群手记
前提 这篇文章是基于Linux系统CentOS7搭建Hadoop-3.3.0分布式集群的详细手记. 基本概念 Hadoop中的HDFS和YARN都是主从架构,主从架构会有一主多从和多主多从两种架构,这 ...
- 2. 使用Shell能做什么
批处理 在批处理的过程中,能够实现脚步自动化,比GUI自动化速度高效 日常工作场景 服务端测试 移动端测试 持续集成与自动化部署,这是最最场景的场景,可以说离开了shell,持续集成和自动化部署也会遇 ...
- 图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析
图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析 大家好,我是洋仔,JanusGraph图解系列文章,实时更新~ 图数据库文章总目录: 整理所有图相关文章,请移步(超链):图数据 ...
- C++回调函数的理解与使用
一.回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数.回调函数不是由该函数的实现方直接调用,而是在 ...