【我与Java的成长记】之String类详解

本文介绍了Java中String类的基础构造、不可变性原理,以及StringBuilder和StringBuffer的使用,强调了避免直接修改String的原因和StringBuilder与StringBuffer的性能优势,特别是线程安全性的区别。
摘要由CSDN通过智能技术生成

在这里插入图片描述

系列文章目录

能看懂文字就能明白系列
C语言笔记传送门
Java笔记传送门
🌟 个人主页古德猫宁-

🌈 信念如阳光,照亮前行的每一步



前言

String 类是 Java 中用于表示字符串的核心类之一。它提供了丰富的方法来操作字符串,包括连接、拆分、替换、查找、截取等,使得字符串处理变得简单和高效。

本节重点:

  • 理解String为什么具有不可变性
  • StringBuffer、StringBuilder和String的区别
  • 为什么StringBuilder比StringBuffer总是快了一毫秒
  • 模拟简单String类方法的实现

一、字符串构造

常用的字符串构造有以下三种:

public static void main(String[] args) {
        //new一个String对象
        String s1 = new String("hello");
        
        //使用常量串进行构造
        String s2 = "hello";
        
        //使用字符数组进行构造
        char[] arr = {'h','e','l','l','o'};
        String s3 = new String(arr);
    }

在这里特别注意的是:

  1. s1,s2,s3存储的是一个地址,由地址指向所引用的对象,可是当我们打印s1,s2,s3的时候,却发现输出的结果是“hello”
    在这里插入图片描述
    原因是String调用了toString的构造方法,我们打开String的源码看看
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    最后是print输出“hello”。

  2. String类是引用类型,内部并不存储字符串本身,在String类的实现源码中,String类实例变量如下:
    在这里插入图片描述
    我们可以看到,String类中有两个成员,一个是数组value[],一个是整型hash(默认为0),当我们调试的时候,我们可以看到是value[]存储的“hello”
    在这里插入图片描述
    如下图所示,我们可以这样理解:
    在这里插入图片描述

  3. 字符串常量后面没有以\0标记结尾,我们可以调用length看下字符串的长度。
    在这里插入图片描述
    在这里插入图片描述

二、String类的特性

String 对象具有不可变性的,即一旦创建就不能被修改。这种不可变性使得字符串在多线程环境下更安全,也可以被用作常量,有助于提高代码的可读性和可维护性。
我们先来看以下代码:

public static void main(String[] args) {
        String s = "hello";
        s = s.concat(" java");
        System.out.println(s);
    }

输出结果:
在这里插入图片描述
这里很多人可能就有疑问了,不是说String对象具有不可变性吗,为什么这里还可以被改变呢?
虽然字符串的内容看起来变了,从“hello”变成了“hello java”,但实际上,原来的字符串还是没有改变,而是得到了一个新的字符串了,它的内容是“hello java”。

具体如图所示:
在这里插入图片描述

为什么字符串具有不可变性呢?
一方面,String类中的字符实际保存在内部维护的value字符数组中,另一方面,String类被final修饰,表明该类不能被继承,而且value也被final修饰,表明value自身的值不能改变,既不能引用其他字符数组,但是其引用空间中的内容可以修改。

特别注意的是:字符串不可变是因为其内部保存字符的数组被final修饰了,因此不能改变,这句话是错误的,不是因为String类自身,或者其内部value被final修饰而不能被修改。

而是因为value[]被private修饰,我们无法拿到value,自然不能通过value去修改值。
在这里插入图片描述

为什么String要设计成不可变的?

  1. 方便实现字符串对象池,如果String可变,那么对象池就需要考虑写深拷贝的问题了
  2. 不可变对象是线程安全的
  3. 不可变对象更方便缓存hash code,作为key时可以更高效的保存到HashMap中

三、StringBuilder和StringBuffer

注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低。
所以为了方便字符串的修改,java中又提供了StringBuilder和StringBuffer类。

StringBuffer和StringBuilder虽然也封装了一个字符数组,但与String不同,其定义如下:

char[] value;

与String不同的是,它并不是被final所修饰,所以是可以修改的。

我们可以做个简单的测试:

public static void main(String[] args) {
        long start = System.currentTimeMillis();
        String s = "";
        for(int i = 0; i < 10000; ++i){
            s += i;
     }
        long end = System.currentTimeMillis();
        System.out.println("String:"+(end - start));

        start = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer("");
        for(int i = 0; i < 10000; ++i){
                sbf.append(i);
     }
        end = System.currentTimeMillis();
        System.out.println("StringBuffer:"+(end - start));

        start = System.currentTimeMillis();
        StringBuilder sbd = new StringBuilder();
        for(int i = 0; i < 10000; ++i){
            sbd.append(i);
    }
        end = System.currentTimeMillis();
        System.out.println("StringBuilder:"+(end - start));
    }

在这里插入图片描述
从结果可以看出,在对String类进行修改时,效率是非常慢的,因此:尽量避免对String的直接修改,如果要修改尽量使用StringBuffer或者StringBuilder。

注意:String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:

  • String变为StringBuilder:利用StringBuilder的构造方法或者append()方法。
  • StringBuilder变为String:利用toString()方法。

总结

为什么StringBuilder和StringBuffer比String快那么多呢?

简单来说String 是不可变的,每次对 String 进行操作(如连接、拼接、替换等),都会生成一个新的 String 对象,原始的 String 对象不变。这样会频繁地创建新的对象,产生大量的临时对象,导致内存开销和垃圾回收压力增大。
StringBuffer 和 StringBuilder 是可变的,它们提供了修改字符串内容的方法,而不是创建新的对象。这样可以减少对象的创建和销毁,提高了性能。
StringBuffer 和 StringBuilder 内部都是使用可变长度的字符数组(char[])来存储字符串内容。它们的方法都是基于字符数组的操作,如扩容、复制、移动等。这种直接操作字符数组的方式可以提高性能,避免了额外的对象创建和拷贝操作。

为什么StringBuilder比StringBuffer总是快了一毫秒呢?

原因是StringBuilder 是非线程安全的,而StringBuffer 是线程安全的,StringBuffer 中的方法是同步的,即它们被设计为可以安全地在多线程环境中使用。这是通过在每个方法上使用 synchronized 关键字来实现的,以确保同一时刻只有一个线程可以访问 StringBuffer 的方法。
在这里插入图片描述
当方法被 synchronized 修饰时,就意味着该方法在同一时间只能被一个线程执行,其他线程需要等待。

三、StringBuilder一些常见的方法

由于StringBuilder和StringBuffer两大类大部分功能是相同的,这里简单介绍StringBuilder一些常用的方法,其他方法可自行浏览:StringBuilder在线文档

方法说明
StringBuilder append(String str)在尾部追加,相当于String的+=,可以追加:boolean,char,char[],double,float,int,long,Object,String,StringBuilder的变量
char charAt(int index)获取index位置的字符
int length()获取字符串的长度
int capacity()获取底层保存字符串空间总的大小
void ensureCapacity(int mininmunCapacity)扩容
void setChar(int index,char ch)将index位置的字符设置为ch
int indexOf(String str)返回str第一次出现的位置
int indexOf(String str,int fromIndex)从fromIndex位置开始查找str第一次出现的位置
StringBuffer deleteCharAt(int index)删除index位置的字符
StringBuffer delete(int start,int end)删除[start,end)区间内的字符
StringBuffer replace(int start,int end,String str)将[start,end)位置的字符替换为str
String substring(int start)从star开始一直到末尾的字符以String的形式返回
StringBuffer reverse()反转字符串

评论 58
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值