首页 > 编程语言 >【JAVA基础】String、StringBuilder和StringBuffer的区别——巨详细

【JAVA基础】String、StringBuilder和StringBuffer的区别——巨详细

时间:2024-01-26 17:32:38浏览次数:34  
标签:JAVA String int StringBuffer System str StringBuilder append

先给答案

String是不可变的,StringBuilderStringBuffer是可变的。而StringBuffer是线程安全的,而StringBuilder是非线程安全的。

源码

先看看jdk1.8中关于String、StringBuilder和StringBuffer部分的源码,我们看某个类或者某个属性是否不可变首先要看修饰类的关键字是什么,final表示不可改变也不可继承。

String

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    // String内部使用char数组来存储数据
    private final char value[];

    // ...
}

源码中String类和String类的值都采用final修饰,因此String类型是不可变的。

StringBuilder

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence {
    
    // 数组最大长度
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8

    // ... 其他方法

    // 示例方法:添加字符串
    public StringBuilder append(String str) {
        // 这里的super指向AbstractStringBuilder类
        super.append(str);
        return this;
    }
    
    // ... 其他方法继续
}

abstract class AbstractStringBuilder {
    char[] value;
    int count;

    // 实际扩展数组和添加内容在这个类中实现
    public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }
    
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
    
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }
    
    private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }

    public AbstractStringBuilder append(String str) {
        if (str == null) {
            // ... 处理null字符串的情况
        }
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    
    // ... 其他方法
}

实际上在append的过程中调用的是抽象类AbstractStringBuilder中的append方法,从这里可以看出append方法首先根据这个字符串的长度对当前的字符数组进行扩容,可以看到StringBuilder存储字符串类型用的也是char[] value,但是这里的修饰符是缺省,因此可以对其进行扩容,即可变。

StringBuffer

StringBufferStringBuilder的差异不大,唯一的区别就是加上了关键字synchronized

public final class StringBuffer
    extends AbstractStringBuilder
    implements Serializable, CharSequence
{
    ...
    private transient char[] toStringCache;
    ...
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    ...
}

String的“+”操作

反编译

long t1 = System.currentTimeMillis();
String str = "hollis";
for (int i = 0; i < 50000; i++) {
    String s = String.valueOf(i);
    str += s;
}
long t2 = System.currentTimeMillis();
System.out.println("+ cost:" + (t2 - t1));
long t1 = System.currentTimeMillis();
String str = "hollis";
for(int i = 0; i < 50000; i++)
{
    String s = String.valueOf(i);
    str = (new StringBuilder()).append(str).append(s).toString();
}

long t2 = System.currentTimeMillis();
System.out.println((new StringBuilder()).append("+ cost:").append(t2 - t1).toString());

测试demo

package org.example;

public class Main {
    public static void main(String[] args) {
        testStringAdd();
        testStringBuilderAdd();
    }

    static void testStringAdd() {
        Runtime runtime = Runtime.getRuntime();

        long usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
        long startTime = System.currentTimeMillis();

        String result = "";
        for (int i = 0; i < 10000; i++) {
            result += "some text";
        }

        long endTime = System.currentTimeMillis();
        System.out.println("String concatenation with + operator took: " + (endTime - startTime) + " milliseconds");
        long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
        System.out.println("Memory used for String concatenation: " + (usedMemoryAfter - usedMemoryBefore) + " bytes");
    }

    static void testStringBuilderAdd() {
        Runtime runtime = Runtime.getRuntime();

        long usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
        long startTime = System.currentTimeMillis();

        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            builder.append("some text");
        }

        long endTime = System.currentTimeMillis();
        System.out.println("String concatenation with StringBuilder took: " + (endTime - startTime) + " milliseconds");

        long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
        System.out.println("Memory used for String concatenation: " + (usedMemoryAfter - usedMemoryBefore) + " bytes");
    }
}

从控制台上可以看到两者的性能差异十分明显。

String concatenation with + operator took: 669 milliseconds
Memory used for String concatenation: 619980944 bytes
String concatenation with StringBuilder took: 0 milliseconds
Memory used for String concatenation: 0 bytes

testStringAdd中的“+”部分反编译后,得到如下代码:

String result = "";
for (int i = 0; i < 10000; i++) {
    result = (new StringBuilder()).append("some text").toString();
}

这里可以看出来实际上“+”做的操作就是new StringBuilder()

使用场景

  • 如果字符串不需要修改,或者只是偶尔修改,使用String
  • 如果在单线程环境中需要频繁修改字符串,使用StringBuilder
  • 如果在多线程环境中需要频繁修改字符串,使用StringBuffer

标签:JAVA,String,int,StringBuffer,System,str,StringBuilder,append
From: https://blog.51cto.com/u_15891973/9436813

相关文章

  • 基于Java+Vue开发的企业Ehr数智化人力管理系统源码+配套文档(提升人力资源管理效率的利
    写在前面:随着企业规模的不断扩大和人力资源管理的日益复杂,传统的人力资源管理方式已经无法满足现代企业的需求。为了提高管理效率、优化资源配置、降低人力成本,越来越多的企业开始引入eHR人力资源管理系统。本文将重点介绍eHR系统在招聘管理、人事管理、考勤管理、绩效管理、社保......
  • Java 应用部署包优化经验分享
    背景最近接手了一个2018年的老项目,因为太久远了,功能上的代码不敢乱动。但是这个项目还有一个问题,打包模块打出的全量包部署不起来。拿到这个项目的部署包,400多兆,网速慢的情况下,下载、上传都得好半天。分析了一下部署包,决定先优化一下,本文记录这个Java应用的部署包优化过程。优......
  • Java web 应用程序的部署方式有哪些
    当我们开发完一个JavaWeb应用程序后,接下来需要将其部署到服务器上,以便用户可以通过浏览器访问。本文将介绍几种常见的JavaWeb应用程序的部署方式。一、独立容器独立容器是最常见的部署方式之一,它是指将JavaWeb应用程序打包成一个独立的WAR文件,然后将该WAR文件部署到独立的Servle......
  • 使用Java读取Excel文件数据
    通过编程方式读取Excel数据能实现数据导入、批量处理、数据比对和更新等任务的自动化。这不仅可以提高工作效率还能减少手动处理的错误风险。此外读取的Excel数据可以与其他系统进行交互或集成,实现数据的无缝传输和共享,满足特定项目的需求。本文将从以下三个方面介绍如何通过Java......
  • IDEA基于maven创建Java web项目
    idea:2022.2.3 IDEA新建项目,更改项目名称,有需要的可以更改项目地址,将Buidsystem从intelliJ更改到Maven 在pom.xml中添加依赖<dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><......
  • Java_5 字符串
    Java字符串title:(在线学习平台)link:(https://www.acwing.com/)cover:(https://cdn.acwing.com/media/activity/surface/log.png)1.字符与整数的联系——ASCII码每个常用字符都对应一个-128~127的数字,二者之间可以相互转化。注意:目前负数没有与之对应的字符。import......
  • javascript replaceall 正则表达式
    varstr="dogdogdog";varstr2=str.replace(/dog/g,"cat");console.log(str2);参考:https://www.jb51.net/article/23762.htm?tdsourcetag=s_pcqq_aiomsgstr="dogdogdog12";str=str.replace(newRegExp("[d]","g......
  • 深度解析Java8社招面试题:Lambda序列化到底行不行?
    大家好,我是小米,一个热爱技术分享的小伙伴。今天,我们来聊一个关于Java8的话题,一个颇具技术深度的问题:“社招面试题:Java8中的Lambda表达式可以序列化吗?”废话不多说,让我们一起揭开这个技术的神秘面纱!Lambda表达式的崛起在Java8之前,我们编写代码时常常要依赖匿名内部类,这使得代码显得......
  • 一个软件项目开发的流程汇总java版
    一个软件项目开发的流程汇总java版1.软件开发整体介绍软件开发流程角色分工软件环境2.xxx项目介绍项目介绍:功能架构(管理端,用户端):体现项目中的业务功能模块产品原型:用于展示项目的业务功能,一般由产品经理进行设计技术选型(用户层,网关层,应用层,数据层):展示项目中使用到的技术......
  • java 类默认构造方法
    Java类默认构造方法在学习Java面向对象编程时,我们经常会遇到类的构造方法。构造方法是一种特殊的方法,用于创建和初始化对象。当我们在定义类时没有显式地定义构造方法时,Java会为我们提供一个默认构造方法。本文将介绍Java类的默认构造方法以及其相关知识点。默认构造方法是什么......