String,StringBuilder与StringBuffer

String类

常用构造方法

 1.无参构造

 public String() {
      this.value = "".value;
  }


2.常用的有参构造

public String(String original) {
       this.value = original.value;
       this.hash = original.hash;
   } 
public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
   }
public String(byte bytes[]) {
       this(bytes, 0, bytes.length);
   }
public String(StringBuilder builder) {
       this.value = Arrays.copyOf(builder.getValue(), builder.length());
  }
public String(StringBuffer buffer) {
       synchronized(buffer) {
           this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
       }
 }

String可以通过传入byte数组和char数组来创建String类型对象,也可以在其中指定要传入的byte数组和char数组的起始位置和长度

String还可以通过传入StringBuffer和StringBuilder对象去创建String类型的对象

3.通过字面量定义的方式

String str = "123";

String也可以通过字面量的定义方式去创建,这种情况创建的String会在字符串常量池中存储(区别于new给一个字符串赋值,此时的字符串值声明在字符串常量池中)

String类的特点

1.底层为char数组

        jdk1.8之后为byte数组

2.内容不可变

首先,从上图可以看到String内部用 final 修饰 char 数组,这个char数组就是String在底层的存储方式。final修饰的引用数据类型的地址值是不可变的,即使该char数组的内容可以被修改,但由于String类也是final修饰的,所以该类不可被继承的,并且,Java 作者在 String 的所有方法里都避免去修改 char 数组中的数据,涉及到对 char 数组中数据进行修改的操作全部都会重新创建一个 String 对象

3.内容可以共享

        String内容可以共享的原因是String有常量池,通过字面量定义的方式生成的String会在字符串常量池中存储,而字符串常量池中是不会存储相同内容(使用String类的equals()比较,返回true)的字符串的。

字符串常量池:

                如果是new出来的对象,则在堆中开辟空间(与常量池无关)

                如果是直接用字面量定义的方式生成的,则存在常量池中,可以共享(使用==比较时地址值相同)

 StringBuilder

常用构造方法

//构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符   
public StringBuilder() {
        super(16);
}
//构造一个不带任何字符的字符串生成器,其初始容量由 capacity 参数指定
public StringBuilder(int capacity) {
        super(capacity);
    }
//构造一个字符串生成器,并初始化为指定的字符串内容
public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
//构造一个字符串生成器,它包含与指定的 CharSequence 相同的字符
public StringBuilder(CharSequence seq) {
       this(seq.length() + 16);
       append(seq);
   }

底层结构

Stringbuilder和Stringbuffer继承自同一个顶层类AbstractStringBuilder,所以成员变量不是finall修饰的

相比于String类,StringBuilder底层实现也是char数组,但是它并没有用final修饰,这意味着它的长度是可变的

 因此,StringBuilder在追加字符串时,将不再去创建新的对象

扩容机制

如果我们用无参的构造方法去创建一个StringBuilder对象

//StringBuilder的无参构造
public StringBuilder() {
        super(16);    //调用了父类的无参构造方法
    }
//父类的方法
AbstractStringBuilder(int capacity) {
        value = new char[capacity];    //将数组长度设置为16
    }

我们可以看到StringBuilder将  16 传入到了父类方法中并创建了一个长度为16的char数组,名字叫value

当我们通过append方法去添加元素时

@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
 
@Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
 public AbstractStringBuilder append(String str) {
        if (str == null)    //当添加的字符串为空时
            return appendNull();    //进入该方法
        int len = str.length();    //不为空时,用len变量来承接字符串的长度
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

 由上面的源码我们可以知道,分两种情况

        1.当添加的字符串为 null 时候则会执行以下内容,即将null转为字符串添加进 value数组中,此时数组中的元素时 “null” 四个字符

private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

        2.当添加的字符串不为null时则执行:

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

 将添加的字符串的长度+ 原数组元素的个数(此时为第一次添加,原数组元素个数为0,以下将字符串长度即说明为两者之和)和value[ ]的长度作比较

                ①如果添加的字符串长度小于初始化数组的长度。(即小于16)。直接要添加的字符串添加入数组中。

                ②如果添加的字符串长度大于当前数组的长度(16)则会执行以下方法

 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;
    }

进行数组长度扩容

由代码value.length << 1 + 2可以看到,先将  数组长度左移1位,再+2。即将初始的数组长度✖2 +2(即位34)。扩容后再与要添加的字符串长度相比较

如果新扩容的数组长度小于要添加的字符串的长度。则将要添加的字符串长度直接当作数组新长度。newCapacity = minCapacity;

最后对这个新长度做个判断,如果该长度小于0或者该长度大于最大限制长度,否则返回该新长度,将添加的字符串逐个添加进数组元素中,并返回copyOf[ ] 

其他

        jdk1.8以后,String的“+”拼接底层优化了,也用的StringBuilder,但String“+”拼接的每次“=”赋值,都会创建一个StringBuilder对象,因此多次拼接建议用StringBuilder

        String“+”操作拼接常量时,在javac时就已经完成了拼接,不会进行StringBuilder

StringBuffer

常用构造方法

//构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。   
 public StringBuffer() {
        super(16);
    }
//构造一个不带字符,但具有指定初始容量的字符串缓冲区。即对 char[ ] 大小进行指定
    public StringBuffer(int capacity) {
        super(capacity);
    }
//构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }
//构造一个字符串生成器,它包含与指定的 CharSequence 相同的字符
    public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

将StringBuffer与StringBuilder构造方法对比,我们不难发现他们是相似的

底层结构

  与StringBuilder类似,继承自同一个父类  AbstractStringBuilder

 与StringBuilder不同的是,StringBuffer的方法中添加了 synchronized 锁,可以保证其线程安全,其余的实现逻辑与StringBuilder相同

StringBuilder 与 String、StringBuffer的比较

同属于java.lang包下

String :jdk1.0,不可变,效率低,但是复用率高。(即一个常量池中不能创建两个一样的字符串常量)

StringBuilder与StringBuffer 非常类似,均代表可变的字符序列,其方法也一样

StringBuffer :jdk1.0,可变字符序列,效率较高(增删)、线程安全

StringBuilder :jdk1.5,可变字符序列,效率最高、线程不安全

 

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
StringStringBuilderStringBufferJava中用于处理字符串的三种类。 String是不可变的,也就是说一旦创建就不能被修改。每次对String进行操作(如拼接、替换)都会生成一个新的String对象,原始的String对象则会被丢弃。这使得在频繁操作字符串的情况下,会产生大量的临时对象,造成内存浪费。因此,当字符串不需要频繁修改时,可以使用String来提高性能。 StringBuilderStringBuffer是可变的字符串类,它们可以进行修改操作而不会创建新的对象。两者的主要区别在于线程安全性和性能上的差异。 StringBuffer是线程安全的,所有的方法都使用synchronized关键字进行同步,保证了多线程环境下的安全性。然而,这也导致了性能上的损失,因为同一时间只能有一个线程访问StringBuffer对象。 StringBuilder是非线程安全的,它的方法没有使用synchronized关键字进行同步。这使得在单线程环境下,StringBuilder的性能比StringBuffer更好。因此,如果在单线程环境下进行字符串操作,建议使用StringBuilder。 如果需要将StringBufferStringBuilder对象转换为String对象,有两种方法可以实现。一种是通过调用toString()方法,将其转换为String对象。另一种是通过String的构造器String(StringBuffer buffer)来创建一个新的String对象,该构造器会将StringBuffer的内容复制到新的String对象中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [StringStringBufferStringBuilder类详解](https://blog.csdn.net/weixin_38568503/article/details/113794751)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值