本文介绍了String类对字符串进行拼接的方法 和拼接字符串的效率分析 以及能对字符串内容进行修改的StringBuilder和StringBuffer类其常用方法和区别 , 最后介绍了两个字符串经典面试题
StringBuilder和StringBuffer的学习
String常用方法学习->2.String的不可变和字符串常量池->StringBuilder和StringBuffer
String类是Java中用来描述操作字符串的类
上两篇博客介绍了介绍了常用的方法,以及String的特性:String内容是不可变的以及字符串常量池,感兴趣的可以去看看…
StringBulder和StringBuffer是和String一样都是对字符串进行操作的类
而学了String后知道,String内容是不可变的,对String的内容进行修改都会创建一个新的字符串返回
而创建新的String对象比原字符串修改所耗费的时间空间更多,效率低很多
因此,java中提供的StringBuilder和StringBuffer是专门用来对字符串进行原地修改的类,并且它们都可以和String类之间进行相互转换
通过源码看到 这两个类的方法大致都相同,且都extends继承了AbstractStringBuilder类
而AbstractStringBuilder类 底层是存在一个value 数组引用和count变量的,其没有被private和final修饰 能够在StringBuilder和StringBuffer中访问到
value数组即维护的是字符串内容 , 而count记录的是value内有效的字符个数即真正的字符串长度
(因为value指向的字符数组长度是当前存放字符串的空间,还有预留的空间用来新增字符等, count记录的才是字符串真正的长度)
由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的
1.字符串拼接
在String方法中提供concat和join方法 可以使字符串之间进行拼接
concat是成员方法,使一个字符串对象内容与另一个字符串对象进行连接成一个新的字符串对象返回
join是静态成员方法, 方法第一个参数 是连接的分隔字符串, 后续2~n个是用来连接的字符串, 会将其拼接在一起 用一个参数做分隔(分隔连接的每段字符串,如果只有一段则不显示分隔符),最后返回此拼接的字符串对象
但上面这些方法并不具备将其他数据类型的数据拼接进字符串的功能因此使用这些方法还要拼接其他非String类型的数据还要将其先转换为字符串再进行拼接
而在Java中,String类型 用 + 连接 String字符串数据和任意类型数据 都可以使其简单快捷直接拼接为一个新字符串对象
示例:
public static void main(String[] args) { System.out.println("12"+3); System.out.println(3+"12"); System.out.println(1+2+"12"); System.out.println("12"+1+2); System.out.println(false+"12"+true); //代码输出结果是什么? }
字符串类型数据和任何数据类型数据 之间使用+运算,都可以拼接成一个新的字符串对象,
但注意要运算符的优先级, 1+2+“12” 是先计算1+2 结果为3 再3+“12"拼接成字符串"312”
“12”+1+2,是先"12"+1拼接成字符串"121" 再+2 拼接成字符串"1212"
boolean类型数据可以参与字符串拼接
“”+12 可以使 12数字拼接为字符串"12"
通过+号可以快速实现字符串拼接,这种用法很方便让我们使基本数据类型快速变为字符串类型
StringBuilder和StringBuffer也可以实现字符串拼接, 需要调用其内部的append方法:
public static void main(String[] args) { StringBuilder stringBuilder=new StringBuilder(); stringBuilder.append(1+2); stringBuilder.append(12); System.out.println(stringBuilder); stringBuilder.append(3); System.out.println(stringBuilder); stringBuilder.append(false); System.out.println(stringBuilder); }
可以看到通过StringBuilder对象 使用其append方法可以将任意数据类型拼接成一个字符串
而append方法内部最后返回的都是StringBuilder对象本身,并没有创建新对象…
从运行效率上String类型使用+和其他数据拼接 和StringBuiler以及的append方法哪种更好?
此时可以使用Java内一种计算内部运行时间的方法System.currentTimeMillis()采取差值运算, 来分别测试这三个的运行时间…
示例:
public static void main(String[] args) { long start = System.currentTimeMillis(); // 计算当前内部运行时间 String s = ""; for(int i = 0; i < 10000; ++i){ s += i; //字符串类型 加任意基本数据类型 都会拼接成一个新字符串再给s引用接收 } // 每+=一次 会额外创建三个对象 long end = System.currentTimeMillis(); System.out.println(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(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(end - start); }
为什么String类型使用+进行字符串拼接效率比StringBuilder和StringBuffer慢很多? 通过 javap -c 字节码文件 反汇编此文件, 能看到使用+号底层做了些什么事 作用是 创建一个String对象 其内容是 value指向的数组对象 0~count之间的内容 (注意:String内的value是指向的新value数组) 可见:使用+号拼接字符串,在底层是使用了StringBuilder对象的append方法,最后返回的也是一个新String对象… 那在上述代码中, 使用for循环通过+号拼接了10000个i, 每次拼接都会创建一个StringBuilder对象和String对象, 可见完成此操作需要new如此多的对象,而且还要回收掉未被指向的String对象, 消耗非常大 而直接实例StringBuilder对象通过append追加字符串内容, 全部追加完成后最后只需要将StringBuilder对象转换String对象 进行一次操作即可, 在此操作里只额外实例化了两个对象, 开销比+号少的多 故:在使用大量字符串追加操作时, 不建议使用+号操作符进行拼接, 其性能远远低于用StringBuilder进行append拼接, 而使用StringBuilder效率比其他拼接方法效率更高 (“1”+“2” 字符串常量用+号拼接不会进行上述append在底层直接看成"12"字符串) StringBuilder和StringBuffer都能对字符串进行修改, 其内部大部分方法也是相同 以上是StringBulider和StringBuffer常用到的对字符串内容进行修改的方法,其内还有一些和Stirng类一样的方法如 indexOf(),具体可自己去了解 既然大部分方法都是相同的,那这两个类的具体区别在哪? 因为String类不能对字符串进行修改,其设计可以使线程更安全,每个线程对方法的调用都是创建新字符串对象, 而 StringBuilder能对字符串内容进行修改,那就会导致多个线程同时调用一个方法会产生一些问题… 此时正是要解决这个线程不安全的问题 而在上面StringBuffer 和StringBuilder 对象 对10000个i进行拼接时, StringBuilder比其效率高一点,也是因为 StringBuffer 执行方法时 还要 进行加锁解锁操作,此也是要消耗资源的… 故StringBuffer类用在多线程情况下,对字符串对象进行修改 而StringBuilder用在单线程情况下 在程序运行时,并 str代码 一个"123"对象并存放在字符串常量池里,new String()一个对象; 最后 创建了两个String对象 str1处 首先new了一个StringBuilder对象 再 new String() 一个对象 , “1”+“2” 底层直接编程成"12" 是一个String对象 , 再被拼接到StringBuilder中, 然后在new String对象, 然后"3"一个对象 , 再被拼接到StringBuilder对象中 , 最后 StringBuilder调用了toString又new了一个String对象 返回 , 最后 new了1个StringBuilder对象和五个String对象 注意str1最后接受的字符串内容是123 但是其和字符串常量池中的"123" 是不同的 三者都是用来描述操作字符串的类 String类的内容是不可变的所有对String内容进行修改的操作都是创建新String对象 ,StringBuilder和StringBuffer的内容都是可变的,常用来对字符串内容进行频繁修改时使用, StringBuffer与StringBuilder大部分功能是相似的 StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作 本文介绍了字符串拼接的方法 (String的成员方法concat和类成员方法join +号运算符拼接 StringBuilder和StringBuffer的append拼接 )以及各种拼接方法的效率 和底层反汇编分析, StringBuffer和StringBuilder的大部分对内容进行修改的方法,和其不同点, 以及两大字符串方面的面试题 及其分析 来源地址:https://blog.csdn.net/lch1552493370/article/details/130654863
通过运行结果可大概估计:拼接效率:String
使用+进行拼接, 本质上是先new了一个StringBuilder对象,然后调用append方法将s字符串追加进去,再调用append 将i 追加进去, 最后调用了toString方法返回~
而StringBuilder重写了toString方法,其作用是 实例一个新String对象,
将StringBuilder继承的AbstractStringBuilder的value数组引用指向的数组 和 ,0 ,count作为参数传参调用其构造方法,
2.StringBuilder和StringBuffer常用方法和区别
以下是一些常用的其内能对字符串内容进行修改的方法如:方法 作用 append() 在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、double、float、int、long、Object、String、StringBuff的变量 void setCharAt(int index,char ch) 将index位置的字符设置为ch insert(int offset, String str) 在offset位置插入:八种基类类型 & String类型 & Object类型数据 deleteCharAt(int index) 删除index位置字符 delete(intstart, int end) 删除[start, end)区间内的字符 replace(intstart, int end, String str) 将[start, end)位置的字符替换为str reverse() 将字符串进原地逆置 toString() 将内容以String对象形式返回
通过源码可以看到,StringBuffer内大部分的方法都有synchronized(同步) 关键字修饰 ,被此关键字修饰的方法即为同步方法, 其是线程安全的, 表示在多线程情况下, 不同线程同时调用这个方法时会将其上锁,同一时间只能有一个线程执行此方法,其它线程只能等待执行完这个方法解锁后才能一一执行此方法3. 面试题:以下代码共创建多少个对象?
public static void main(String[] args) { String str=new String("123"); // 创建多少个对象 String str1=new String("1"+"2")+new String("3"); //创建多少个对象//以上程序在运行时字符串常量都是第一次出现字符串常量池并不存在程序中的常量字符串 }
字符串常量池是在编译时将其字符串常量"123"存放进去的, 而str1接受的是后续拼接出来的字符串1234.面试题: String 、StringBuilder和StringBuffer的异同