Java中String,StringBuffer,StringBuilder基础知识

前言

在平时开发中,咱们不少时候都会用到StringStringBufferStringBuilde这三者。那么这三者到底是什么呢,下面一一讲述。java

String

String
查看 api文档,能够知道, String是继承 object类,并实现了序列化接口,字符序列接口,字符串排序接口。 String是Java中的字符串,Java程序中的全部字符串字面值(如"abc")都做为此类的实例实现,字符串是常量,它们的值在建立以后不能更改,字符串缓冲区支持可变的字符串。由于 String对象是不可变的,全部能够共享,例如: String str1 = "abc";等效于 char data[] = {'a','b','c'};String str2 = new String(data);,由于 String是不可变的对象,每次对 String类型改变的时候其实都等同于生成一个新的 String对象,而后将指针指向新的 String对象,因此常常改变内容的字符串最好不要用 String,由于每次生成对象都会对系统性能产生影响,当内存多了无引用的对象, JVM的GC就会工做,频繁GC就会形成界面的卡顿,下面直接上例子:

直接赋值和建立对象

状况一

String str1 = "abc";
		 char data[] = {'a','b','c'};
		 String str2 = new String(data);
		 //输出true
		 System.out.println(str1.equals(str2));
		 //输出false
		 System.out.println(str1 == str2);
		 
		 String str3 = "This is Java";
		 String str4 = new String("This is Java");
		 //输出true
		 System.out.println(str3.equals(str4));
		 //输出false
		 System.out.println(str3 == str4); 
		  
复制代码

上面代码str1str3是直接赋值的,str2是经过字符数组来构建一个字符串常量,str4是直接经过new关键字来建立对象。能够看到用equals比较str1str2str3str4是相等的,用==比较str1str2str3str4是不相等的。这里简单说一下equals==的简单区别:api

  • ==操做符的做用
  1. 比较基本数据类型是否相同
  2. 判断引用是否指向堆内存的同一块地址,也就是是否指向同一个对象
  • equasl方法的做用
  1. 判断两个变量是否对同一个对象的引用,也就是内存堆中的内容是否相同,其实就是对String对象所封装的字符串内容做比较,具体的值是否相等,若是两个String对象所封装的字符型内容相同,则equals()方法将返回true

而上面的状况由于str1str2是两个不一样的对象,可是内容相等,因此==返回了false,而equals返回true,str3str4也是同样道理,从上面能够简单得出一个结论,不管上直接赋值仍是经过new关键字来建立String对象,都会生成两个不一样的String对象,即便字符型内容相同。数组

状况二

String str5 = "This is Java";
		 String str6 = "This is";
		 String str7 = "This is" + " Java";
		 //输出true
		 System.out.println(str5.equals(str7));
		 //输出true
		 System.out.println(str5 == str7); 
复制代码

从上面代码能够看出,不管是用==或者equals来比较两个没有引用而且内容值是相等返回都是true,也就是String str7 = "This is" + " Java";这句话没有建立新的对象,而是引用指向str5的地址。安全

状况三

String str8 = "This is Java";
		 String str9 = "This is";
		 String str10 = str9 + " Java";
		 //输出true
		 System.out.println(str8.equals(str10));
		 //输出false
		 System.out.println(str8 == str10); 
复制代码

上面发现用==比较时,str8str10不是指向同一个地址,这是由于在编译时str8str9就已经肯定了,而str10是引用变量,不会再编译时肯定,因此会建立String对象。多线程

状况四

String str11 = "This is Java";
		 String str12 = new String("This is Java");
		 String str13 = str12;
		 
		 //输出true
		 System.out.println(str11.equals(str12));
		 //输出true
		 System.out.println(str11.equals(str13));
		 //输出true
		 System.out.println(str12.equals(str13));
		 //输出false
		 System.out.println(str11 == str13); 
		 //输出false
		 System.out.println(str11 == str12); 
		 //输出true
		 System.out.println(str12 == str13); 
复制代码

由于三个String对象的值都是相等的,因此用equals来比较是返回true,由于str13是指向str12,至关于传递引用,因此用==来比较是相等的,都是同一个对象,用一张图来比较直观:app

String图一
这里简单说一下,由于一开始 String str11 = "This is Java";在常量池已经建立 This is Java这个常量,因此 String str12 = new String("This is Java");这句代码不会再常量池建立对象,只会在堆上建立对象 This is Java,也就是说 new String建立字符串它其实分两步操做:

  1. 在堆上建立对象
  2. 检查常量池有没有字符串字面量,若是没有就在常量池建立常量,若是存在就不作任何操做

状况五

String str14 = "This is Java";
		 String str15 = "This is Java";
		 String str16 = "This is Java";
		 
		 //输出true
		 System.out.println(str14.equals(str15));
		 //输出true
		 System.out.println(str14.equals(str16));
		 //输出true
		 System.out.println(str15.equals(str16));
		 //输出true
		 System.out.println(str14 == str15); 
		 //输出true
		 System.out.println(str14 == str16); 
		 //输出true
		 System.out.println(str15 == str16); 
复制代码

直接上图:性能

String 图2

状况六

String str17 = new String("This is Java");
		 String str9 = str17.intern();
		 String str18 = "This is Java";
		 //输出false
		 System.out.println(str9 == str17);
		 //输出true
		 System.out.println(str9 == str18);
复制代码

上面调用了intern这个方法,这句话就是把字符串对象加入常量池中,实际操做以下,直接上图吧:ui

intern
因此上面代码,用下面图显示:

String intern示意图

总结

  1. String a = "xxx" 可能建立一个或者不建立对象,当xxx这个字符常量在String常量池不存在,会在String常量池建立一个String对象,而后a会指向这个内存地址,后面继续用这种方式继续建立xxx字符串常量,始终只有一个内存地址被分配。
  2. String a = new String("xxx")至少建立一个对象,也有可能两个。只要用到new就确定在堆上建立一个String对象,而且检查String常量池是否存在,若是不存在就会在Stirng常量池建立这个String对象,存在就不建立。

StringBuffer字符串变量

StringBuffer
api文档上,一样能够知道 StringBuffer继承 Object类,并实现了序列化接口,可以被添加 char序列和值的对象接口,可读序列接口。和 String相比,没有实现 Comparable,而是实现了 Appendable。它是 线程安全的可变字符序列,一个相似于 String的字符串缓冲区,可是不能修改,虽然在任意时间点上它都包含某种特定的字符序列,但经过某些方法调用的能够改变该序列的长度和内容。 StringBuffer上的主要操做是 appendinsert方法,能够重载这些方法,以接受任意类型的数据,每一个方法都能有效地将给定的数据转换成字符串,而后将该字符串的字符追加或者插入到字符串缓冲区中。 append方法始终将这些字符添加到缓冲区的末端,而 insert方法则在指定的点添加字符。 StringBufferString不同,它是**字符串变量,是能够改变的对象,当对字符串作操做时,就是在对象上作操做,不会像 String同样建立额外的对象进行操做。

StringBuffer str20 = new StringBuffer("This is");
		 //StringBuffer后面追加内容
		 str20.append(" Java");
		 System.out.println(str20); //输出:This is Java
		 //删除第6-8个字符
		 str20.delete(5, 8);
		 System.out.println(str20); //输出:This Java
		 //在9个位置插入
		 str20.insert(9," is good"); 
		 System.out.println(str20); //输出:This Java is good
		 //替换5-9
		 str20.replace(5, 9, "C++");
		 System.out.println(str20); //输出:This C++ is good
		 //倒序
		 str20.reverse();
		 System.out.println(str20); //输出:doog si ++C sihT
		 
		 //变成大写
		 System.out.println(str20.toString().toUpperCase()); //输出:DOOG SI ++C SIHT
		 
		 //第一种方案StringBuffer转化成String
		 String str21 = new String(str20);
		 System.out.println(str21); //输出:doog si ++C sihT
		 
		 //第二种方案
		 String str22 = str20.toString();
		 System.out.println(str22); //输出:doog si ++C sihT
复制代码

StringBuilder字符串变量

StringBuilder示意图
StringBuffer同样,也是实现 SerializableAppendableCharSequence接口。一个可变的字符序列,这个类提供一个与 StringBuffer兼容的API,可是不保证同步,该类被设计用做 StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。若是可能,建议优先采用该类,由于在大多数实现中,比 StringBuffer要快。在 StringBuilder 上的主要操做是 appendinsert方法,可重载这些方法,以接受任意类型的数据。每一个方法都能有效地将给定的数据转换成字符串,而后将该字符串的字符追加或插入到字符串生成器中。 append方法始终将这些字符添加到生成器的末端;而 insert方法则在指定的点添加字符。将 StringBuilder的实例用于 多个线程是不安全的。若是须要这样的同步,则建议使用 StringBuffer。主要一些操做:

StringBuilder str23 = new StringBuilder("This is"); 
		 //StringBuilder后面追加内容
		 str23.append(" Java");
		 System.out.println(str23); //输出:This is Java
		 //删除第6-8个字符
		 str23.delete(5, 8);
		 System.out.println(str23); //输出:This Java
		 //在9个位置插入
		 str23.insert(9," is good"); 
		 System.out.println(str23); //输出:This Java is good
		 //替换5-9
		 str23.replace(5, 9, "C++");
		 System.out.println(str23); //输出:This C++ is good
		 //倒序
		 str23.reverse();
		 System.out.println(str23); //输出:doog si ++C sihT
		 
		 //变成大写
		 System.out.println(str23.toString().toUpperCase()); //输出:DOOG SI ++C SIHT
		 
		 //第一种方案StringBuffer转化成String
		 String str24 = new String(str23);
		 System.out.println(str24); //输出:doog si ++C sihT
		 
		 //第二种方案
		 String str25 = str23.toString();
		 System.out.println(str25); //输出:doog si ++C sihT
		 
复制代码

三者速度比较

//作赋值操做 
		//String赋值
		String str = new String("I Love Android");
		long starttime1 = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
			str = str + "!";
		}
		long endtime1 = System.currentTimeMillis();
		System.out.println("String花费的时间 :" + (endtime1 - starttime1));
		
		//StringBuilder 
		StringBuilder str3 = new StringBuilder("I Love C++");
		long starttime3 = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
			str3 = str3.append("!");
		}
		long endtime3 = System.currentTimeMillis();
		System.out.println("StringBuilder花费的时间 :" + (endtime3 - starttime3));
					
		//StringBuffer
		StringBuffer str2 = new StringBuffer("I Love Java");
		long starttime2 = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
			str2 = str2.append("!");
		}
		long endtime2 = System.currentTimeMillis();
		System.out.println("StringBuffer花费的时间 :" + (endtime2 - starttime2));
		
			
复制代码

运行截图:spa

三者运行时间

总结

  • String :不可变类,任何对String的改变都会引起新的String对象的生成,适用于少许的字符串操做的状况
  • StringBuffer :线程安全,任何对它所指代的字符串的改变都不会产生新的对象,适用多线程下在字符缓冲区进行大量操做的状况
  • StringBuilder :线程不安全,所以不适合多线程中使用,适用于单线程下在字符缓冲区进行大量操做的状况
  • 速度运行方面:StringBuilder > StringBuffer > String
相关文章
相关标签/搜索