p86 StringBuilder类的引入
首先提到了一个方法:long time = System.currentTimeMillis();
该方法的作用是返回1970年1月1日0时0分0秒到现在时间的毫秒值。
在一段代码的前后各放置一个此方法随后经过计算可以得出这段代码的运行时间。
经过StringBuilder类和字符串的+=运算的对比后,可以得出结论:
StringBuilder类的作用是提高字符串的操作效率。
对于一个StringBuilder类(此处以对象名为sb举例),可以使用子方法append(),给方法传入一个字符串参数(例如sb.append(i) 其中i是一个字符串)来进行字符串的拼接,效率比字符串的+=运算快非常多。
p87 StringBuilder介绍和构造方法
由api文档可知,StringBuilder类归属于java.lang包,所以使用时不需要导入包;
StringBuilder类代表一个可变的字符序列,String类代表一个不可变的字符序列,这是二者最大的区别。
StringBuilder是字符串缓冲区,可以将其理解为一个容器。该容器可以存储任意类型的数据,但是数据进入了这个容器就会被转化为String类型。
StringBuilder类的几个常用构造方法:
StringBuilder() 无参数构造方法,建立一个初始容量为16个字符的空的字符串缓冲区。
初始容量16不代表超过16就会出问题,它会随着字符串长度而自动添加容量。
StringBuilder(int capacity) 带参数,其中的参数代表建立的空字符串生成器的初始容量。
实际运用中基本不需要用到这个带参方法,因为它会自动扩容。
StringBuilder(String sty) 带参数,参数为一个String类型字符串,效果为创建一个字符串缓冲区,创建好之后它将会带有参数传入的内容。
带有字符串参数的方法和不带参的方法也可以理解为StringBuilder类的实际底层层面的char[]类型数组的静态初始化和动态初始化。
p88 StringBuilder常用的成员方法
append()方法--追加内容
添加数据并且返回对象本身,也就是说如果用新的对象去接收,新对象和旧对象地址不同但是二者会指向同一块内存,也就是实际的内核是相同的。
这里提到一个叫做链式编程的编码思想,即调用对象的方法后如果返回的也是对象就可以继续向下调用方法。
多次调用方法的运行顺序是从左到右。
举例:
StringBuilder sb = new StringBuilder(); StringBuilder sb2 = sb.append("111"); sb = sb2.append("222");
在该举例情况下出现了一个叫做sb2的StringBuilder类对象用来做两次调用append()方法的中间变量,但是可以通过链式编程的思想来省略这一过程,即将该段代码可以修改为:
StringBuilder sb = new StringBuilder(); sb.append("111").append("222");
两种情况的作用是相同的,但后者明显比前者更简便、也更节省空间(不用开辟sb2的空间了)。
这里举例多次调用相同方法实际很多情况是徒增代码量的,但是这种思想在连续调用多个不同的方法时候也可以使用,只要方法返回的是一个对象,就可以接着向下调用该对象的子方法。
reverse()方法--反转内容
可以将一个StringBuilder类的内容进行前后翻转,例如"abc"可以反转成"cba","你好"可以反转成"好你"。这里不多赘述。
length()方法--返回长度
很明显是返回StringBuilder类包含的字符串的长度。
toString()方法--转换为String
返回该StringBuilder类中的字符串,也就是说内容不会发生变化,但是类型会转换为String类。
什么时候需要用到这个方法?存储的数据在一个StringBuilder类中,你需要调用一个方法对这个数据进行处理,但是String类有这个方法,StringBuilder类中没有,这时候就可以使用toString方法转化成String类型然后调用那个方法。
配合前面链式编程的思路可以使得String类、StringBuilder类、以及其他前面提到过和未提到过的类的方法调用更加便捷。
p89 StringBuilder类使用案例和原理
练习1:键盘接受一个字符串,程序判断出该字符串是不是对称字符串(前后颠倒之后整体仍然不变),并在控制台中打印结论。
public static void main(String[] args) { System.out.println("请输入一个字符串后回车(只记录第一个空格或回车之前的内容)"); Scanner scanner = new Scanner(System.in); String textIn = scanner.next(); StringBuilder text = new StringBuilder(textIn); if(textIn.equals(text.reverse().toString())){ System.out.println("这个字符串是前后对称的"); }else{ System.out.println("这个字符串不是前后对称的"); } }
非常简单,回头使用到前面提到过的链式编程的思想,将String类转化为StringBuilder类,然后调用反转方法,再转换回String类与原来的字符串进行比较,如果内容相同就输出相同的语句,不同就输出不同的语句。
(这里用的是next()而不是nextLine(),所以不会录入第一个空格开始往后的内容,所以实际运用中类似情况的时候这里应该再加一个是否空输入的检测,但是这里懒了)
练习2:定义一个方法,把int数组中的数据按照指定格式拼接成一个字符串返回。调用该方法并输出结果。
例如:数组为int[] arr = {1,2,3};
执行方法后的输出结果为[1,2,3](包括方括号,整体是一个字符串)
public static void main(String[] args) { int[] arr = {15,20,25}; String string = getString(arr); System.out.println(string); } private static String getString(int[] arr) { StringBuilder stringBuilder = new StringBuilder("["); for (int i = 0; i < arr.length; i++) { stringBuilder.append(arr[i]); if (i != arr.length - 1) { stringBuilder.append(", "); } } stringBuilder.append("]"); return stringBuilder.toString(); }
编写的代码里用到了定义的时候就被写死的数组,如果完善的话需要再编写一个方法用来输入数组。以前的案例写过太多所以这里不写。
StringBuilder的原理:它是如何提高字符串拼接效率的?
这里举例两种用拼接方法产生"abc"字符串的方法
第一种方法是字符串直接使用加号,让系统进行拼接:
//第一种------------------------ String s1 = "a"; String s2 = s1+"b"; String s3 = s2+"c";
按照内存的视角来看,在第一行中用到了双引号的字符串,系统先去检查字符串池里有没有相同的字符串,没有就创建;
在第二行里同样用到了双引号字符串,所以再次搜索、再次创建;创建完后,由于用到了String类的加法运算,系统层面上看是系统自动创建了一个新的StringBuilder类的空对象,调用其中的append()方法,先append(s1),再append("b"),完成字符串的拼接,再使用toString()方法将其转化为String类型,最后再把这个转化后的String类对象的地址返回给s2;
第三行的情况和第二行相似,这里不做赘述。需要指出的是,这里同样创建了一个新的StringBuilder类对象,增大了占用的空间。
由此可见字符串的加号拼接操作按照系统层面看是非常低效且缓慢的,而且它占用了字符串池和堆内存中的许多空间来存放数据。
下来是第二种方法,也就是用StringBuilder类来进行拼接:
StringBuilder sb = new StringBuilder(); sb.append("a"); sb.append("b"); sb.append("c"); String string = sb.toString();
以内存的视角来看,我们先创建了一个StringBuilder类对象,随后使用了append()方法添加了三个双引号字符串,该过程仍然需要这三个双引号字符串在字符串池中被检查和创建,但是相较于第一个方法,这里只需要创建一次StringBuilder类对象和一个String类对象,而且append()方法的使用次数也更少,整体运行速度和占用内存都比前一个方法更为优秀。
String和相关的类的初步学习到这里就告一段落了,但是在最开始的时候提到过有一个和StringBuilder类放在一起说明的StringBuffer类。事实上,这两个类在使用上几乎没有区别。除了二者的最初出现版本不同(StringBuilder类出现在jdk1.5版本开始,StringBuffer类出现在jdk1.0版本开始)以外,二者最大的区别就是StringBuilder类的实例用于多个线程是不安全的,在这种情况下更建议使用StringBuffer类。
这一小章节的学习目标总结:
- 能够使用API帮助文档查看已经学习过的类并使用
- 清楚String类的特点
- 掌握提到过的String 的相关面试题
- 清楚StringBuilder的作用
- 能够独立完成前面的案例(可以使用API)