【Java】字符串类String、StringBuffer、StringBuilder

java.lang.String类

概述

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0

String:代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现

  • String声明为final,不可被继承
  • 字符串是常量,书写时用双引号""修饰。值在创建之后不能更改。
  • String实现了Serializable接口:表示字符串是支持序列化的。
  • 实现了Comparable接口:表示String可以比较大小
  • String内部定义了private final char[] value用于存储字符串数据
  • 通过字面量的方式(区别于用new String()给字符串赋值)声明的字符串值在保存在字符串常量池中)。
  • 字符串常量池中是不会存储相同内容(使用String类的equals()比较)的字符串的。

String的不可变 性

  1. 当对字符串重新赋值时,需要重新指定内存区域赋值
  2. 当对现在的字符串进行连接操作时,也需要重新指定内存区域赋值
  3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

总结就是只要字符串值有更改,都需要重新指定内存区域赋值。若新值在内存区域已存在,则直接指向该区域,若没有则重新开辟空间

String对象的创建

建议去看API,以下展示集几种常用的

//字面量方式
String str = "hello";	

//本质上this.value = new char[0];
String s1 = new String(); 

//this.value = s.value;
String s2 = new String(String s); 

//this.value = Arrays.copyOf(value, value.length);
String s3 = new String(char[] a); 

//从字符数组a的startIndex位置截取count个字符
String s4 = new String(char[] a,int startIndex,int count);

从上面代码可以看出String实例化主要有两种方式

  • 方式一:通过字面量定义的方式
  • 方式二:通过new + 构造器的方式
//通过字面量定义的方式:此时的s1和s2的数据声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2 = "javaEE";

//通过new + 构造器的方式:此时的s3和s4保存的是地址值,指向堆空间中的数据。
String s3 = new String("javaEE");
String s4 = new String("javaEE");

System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false

面试题

  1. String s = new String("abc");方式创建对象,在内存中创建了几个对象?
    答:两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:"abc"

  2. String str1 = "abc"String str2 = new String("abc");的区别?

  3. 字符串拼接方式赋值的对比

String s1 = "javaEE";
String s2 = "hadoop";

String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop"; 
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
String s9 = s1 + s2;
 
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false  
System.out.println(s6 == s7);//false
System.out.println(s7 == s9);//false

String s8 = s6.intern();//返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
System.out.println(s3 == s8);//true
****************************
String s1 = "javaEEhadoop";
String s2 = "javaEE";
String s3 = s2 + "hadoop";
System.out.println(s1 == s3);//false

final String s4 = "javaEE";//s4:常量
String s5 = s4 + "hadoop";
System.out.println(s1 == s5);//true
  1. 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
  2. 只要其中一个是变量,结果就在堆中,且不管变量名是否相同开辟的空间都不同。
  3. 如果拼接的结果调用intern()方法,返回值就在常量池中
  4. 若变量用final修饰就变成常量
  1. 下列程序运行结果
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'b'; }
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.print(ex.str);	//good 不可变性
System.out.println(ex.ch);	//best
} }

StringBuffer、StringBuilder

  • StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
  • StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]

StringBuffer与StringBuilder的内存解析

//以StringBuffer为例:
String str = new String();//char[] value = new char[0];
String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};

StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度是16的数组。
System.out.println(sb1.length());//0
sb1.append('a');//value[0] = 'a';
sb1.append('b');//value[1] = 'b';

StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];
System.out.println(sb2.length());	// 3

扩容问题

如果要添加的数据长度大于16,那就需要扩容底层的数组。默认情况下,扩容为原来容量的2倍 + 2,同时将原数组中的元素复制到新的数组中。

开发中建议指定长度:StringBuffer(int capacity) 或 StringBuilder(int capacity)

三者的执行效率
从高到低排列:StringBuilder > StringBuffer > String

三者的对比


abstract class AbstractStringBuilder implements Appendable, CharSequence {
   
    char[] value;	//value没有final修饰,可以扩容
    
    int count;		//count记录有效字符的个数
    AbstractStringBuilder() {
    }
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
  • StringBuilder 和 StringBuffer可用方法一致
  • StringBuilder 和 StringBuffer都继承于AbstractStringBuilder
  • StringBuffer之所以是线程安全的,是因为方法用synchronized修饰
  • StringBuilder 和 StringBuffer 支持链式调用

StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器

StringBuffer()		//初始容量为16的字符串缓冲区
StringBuffer(int size)		//构造指定容量的字符串缓冲区
StringBuffer(String str)	//将内容初始化为指定字符串内容

StringBuffer 和 StringBuilder的常用方法

StringBufferappend(xxx)	//提供了很多的append()方法,用于进行字符串拼接
StringBufferdelete(int start,int end)	//删除指定位置的内容
StringBufferreplace(intstart,intend,Stringstr)	//把[start,end)位置替换为str 
StringBufferinsert(int offset,xxx)	//在指定位置插入xxx
StringBufferreverse()	//把当前字符序列逆转
posted @ 2021-09-21 23:22  至安  阅读(47)  评论(0编辑  收藏  举报