从底层了解String,Stringbuffer,StringBuilder三个类:String不变性,StringBuffer和StringBuilder的可变性,数组扩容,以及这三个类的常用方法

目录

String:

String类的源码分析:

String的不变性:

String的实例化:

String类对象的内存分析:

字符串的不同拼接方式:  

将String与包装类和基本数据类型进行相互转换:

String的常用方法:

StringBuffer:

StringBuffer的可变性:

StringBuffer的扩容问题(重要):

StringBuffer的append方法实际上调用的是其父类的append方法:

StringBuffer父类中的append方法:

判断value[]数组是否需要扩容:

扩容的方法:

StringBuffer的常用方法:

         增:

删:

改:

插:

查:

长度:

StringBuffer和StringBuilder:

总结:


我们不管在学习什么语言的时候,字符串都是一个非常重要的知识点,每个语言的实现和操作都不完全相同,因此,在学习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[]数组的长度,这时侯该怎么办?

  1. 如果追加的元素为空,返回原来的value[]数组
  2. 如果追加的元素不为空,如果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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值