目录
StringBuffer的append方法实际上调用的是其父类的append方法:
我们不管在学习什么语言的时候,字符串都是一个非常重要的知识点,每个语言的实现和操作都不完全相同,因此,在学习Java中,我们非常的有必要将String,StringBuffer,StringBuilder这三个与字符串相关的类好好地理一理。
String:
String类的源码分析:
通过查看String类的源码(下面所有的源码都是参考jdk1.8的源码)我们可以知道:
- String类实现了Comparable这个接口,因此,String对象可以彼此之间比较大小
- String创建出来的对象实际上是由底层的一个value[]字符型数组存储。
String的不变性:
- String类被final修饰,表明这个类不能被继承
- 用来存储字符串的字符型数组value[]被final修饰,value[]数组不能被修改,也就是说,String类对象一旦被创建,对应的字符串就不能被改变。
String的实例化:
创建String对象有两种方法:一种是直接通过“=”赋值运算符,另一种是通过new。
第一种:String str = "hello world";
第二种:java为我们提供了四种String类的构造器:
我们依次用着四中构造器来创建String类对象:
String str1 = new String(); //本质上是:this.value = new char[0];
String str2 = new String( String origian ); //本质上:this.value = origian.value
String str3 = new String( char[] a );//本质上://本质上:this.value = Arrays.copyOf( value , value.length )
String str4 = new String( char[] a , int startIndex , int count ); //本质上:Arrays.copyOfRange( value , startIndex , count );
String类对象的内存分析:
我们知道:字符串存放在方法区中的字符串常量池内,对象存放在堆中。
package hxz.commomclass;
public class StringTest_01 {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
System.out.println(str1==str2);//true
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str3==str4);//false
System.out.println(str1==str3);//false
student stu1 = new student("hello");
student stu2 = new student("hello");
System.out.println(stu1.name==stu1.name);//true
}
}
class student{
String name;
public student(String name) {
this.name = name;
}
}
字符串的不同拼接方式:
常量与常量的拼接在常量池中,如果其中一个时变量,结果就在堆中
public class StringTest_02 {
public static void main(String[] args) {
//常量与常量的拼接在常量池中
//如果其中一个时变量,结果就在堆中
String s1 = "javaEE";
String s2 = "hadhoop";
String s3 = "javaEEhadhoop";
String s4 = "javaEE"+"hadhoop";
String s5 = s1+"hadhoop";
String s6 = "javaEE"+s2;
String s7 = s1+s2;
System.out.println(s3==s4);
System.out.println(s3==s5);
System.out.println(s3==s6);
System.out.println(s3==s7);
System.out.println(s5==s6);
System.out.println(s5==s7);
System.out.println(s6==s7);
}
}
但是可以通过调用intern方法,把字符串放到字符串常量池中:
String s8 = s7.intern();
System.out.println(s8==s3);//true
将String与包装类和基本数据类型进行相互转换:
public class StringTest_03 {
public static void main(String[] args) {
String str = "1234";//String---->int包装类 调用包装类的静态方法 Xxx(str)
System.out.println(Integer.parseInt(str));
int n = 234;
//int包装类---->String类 调用String的重载方法valueOf()或者直接+" "
int i = 45;
String str1 = i+"";
System.out.println(String.valueOf(n));
//String--->char[] 调用String的toCharArray方法
String str3 = "java";
char[] chars = str3.toCharArray();
for(char temp:chars) {
System.out.println(temp);
}
//char[]---->String 调用String的重载构造器new String(chars)
String str4 = new String(chars);
System.out.println(str4);
}
}
String的常用方法:
方法比较多这里就不一一列举,但是要注意的是:String创建的字符串不会改变的,因此,如果我们调用了“改变原字符串”的方法(例如截取,插入,删除),实际上程序会返回一个新的字符串给我们,而原来的字符串并没有改变。
既然String创建出来的字符串不能修改,那么当我们需要对字符串进行修改时,可以考虑使用StringBuffer或者StringBuilder。其中,String和StringBuffer都是自jdk1.0就有,而StringBuilder是诞生在jdk5.0。
StringBuffer:
StringBuffer的可变性:
我们打开StringBuffer的空参构造器,看到其父类中也有一个字符型数组value[],这个数组就是StringBuffer用来存储数据的底层数组,只不过这个数组没用final来修饰,因此StringBuffer创建的字符串是可以修改的。
执行 StringBuffer sb = new StringBuffer(); ----->相当于char[] value = new char[16]
sb.append('a'); ---->相当于value[0] = 'a'
StringBuffer的扩容问题(重要):
假设有这样一种情况:我们要对一个字符串追加元素,我们这时侯选择StringBuffer,但是,如果我们要追加的元素太多了,超过了value[]数组的长度,这时侯该怎么办?
- 如果追加的元素为空,返回原来的value[]数组
- 如果追加的元素不为空,如果value[]容量够大,那么直接追加到原有字符串的后面,如果value[]容量太小,那么要创建一个新的数组,其大小是value[]数组的2倍再加上2.
看源码:
StringBuffer的append方法实际上调用的是其父类的append方法:
StringBuffer父类中的append方法:
判断value[]数组是否需要扩容:
扩容的方法:
StringBuffer的常用方法:
增:
- public StringBuffer append(数据类型 b) 提供了很多的append()方法,用于进行字符串连接
删:
- public StringBuffer delete(int start,int end) 删除指定位置的内容
改:
- public StringBuffer replace(int start,int end,String str) 将指定范围[start,end)的内容替换成其他内容
- public void setCharAt(int n ,char ch) 替换指定位置的字符
- public StringBuffer reverse() 字符串翻转
插:
- public StringBuffer insert(int offser, xxx) 在指定位置上增加一个xxx
查:
- public int indexOf(String str) 返回str首次在字符串中出现的位置
- public char charAt(int n) 取指定位置的字符
- public String substring(int start,int end) 截取指定范围的字符串,原来的字符串并不会收到影响
- public String substring(int start) 字符串截取,从指定位置到字符串结尾,原来的字符串并不会收到影响
长度:
- public int length() 返回字符串的长度,注意不是char[] value字符串数组的长度!
StringBuffer和StringBuilder:
StringBuilder与StringBuffer的实现方法一样,只是StringBuffer中的方法都有synchronized修饰,线程安全,但是效率低
StringBuider线程不安全,但是效率高。
总结:
效率:StringBuilder>StringBuffer>String
线程安全:StringBuffer>StringBulider,String