能够证实,字符串操做是计算机程序设计中最多见的行为。html
首先咱们要明确,String并非基本数据类型,而是一个对象,而且是不可变的对象。查看源码就会发现String类为final型的(固然也不可被继承),并且经过查看JDK文档会发现几乎每个修改String对象的操做,实际上都是建立了一个全新的String对象。java
字符串为对象,那么在初始化以前,它的值为null,到这里就有必要提下””、null、new String()三者的区别。null 表示string尚未new ,也就是说对象的引用尚未建立,也没有分配内存空间给他,而””、new String()则说明了已经new了,只不过内部为空,可是它建立了对象的引用,是须要分配内存空间的。打个比方:一个空玻璃杯,你不能说它里面什么都没有,由于里面有空气,固然也能够把它弄成真空,null与" "、new String()的区别就象真空与空气同样。正则表达式
在字符串中存在一个很是特殊的地方,那就是字符串池。每当咱们建立一个字符串对象时,首先就会检查字符串池中是否存在面值相等的字符串,若是有,则再也不建立,直接放回字符串池中对该对象的引用,若没有则建立而后放入到字符串池中而且返回新建对象的引用。这个机制是很是有用的,由于能够提升效率,减小了内存空间的占用。因此在使用字符串的过程当中,推荐使用直接赋值(即String s=”aa”),除非有必要才会新建一个String对象(即String s = new String(”aa”))。编程
对于字符串的使用无非就是这几个方面:数组
一、字符串比较安全
equals() ------判断内容是否相同。多线程
compareTo() ------判断字符串的大小关系。app
compareToIgnoreCase(String int) ------在比较时忽略字母大小写。ide
== ------判断内容与地址是否相同。测试
equalsIgnoreCase() ------忽略大小写的状况下判断内容是否相同。
reagionMatches() ------对字符串中的部份内容是否相同进行比较(详情请参考API)。
二、字符串查找
charAt(int index) ------返回指定索引index位置上的字符,索引范围从0开始。
indexOf(String str)------从字符串开始检索str,并返回第一次出现的位置,未出现返回-1。
indexOf(String str,int fromIndex);------从字符串的第fromIndex个字符开始检索str。
lastIndexOf(String str)------查找最后一次出现的位置。
lastIndexOf(String str,int fromIndex)----从字符串的第fromIndex个字符查找最后一次出现的位置。
starWith(String prefix,int toffset)-----测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
starWith(String prefix)------测试此字符串是否以指定的前缀开始。
endsWith(String suffix)------测试此字符串是否以指定的后缀结束。
三、字符串截取
public String subString(int beginIndex)------返回一个新的字符串,它是此字符串的一个子字符串。
public String subString(int beginIndex,int endIndex)------返回的字符串是从beginIndex开始到endIndex-1的串。
四、字符串替换
public String replace(char oldChar,char newChar)。
public String replace(CharSequence target,CharSequence replacement)------把原来的etarget子序列替换为replacement序列,返回新串。
public String replaceAll(String regex,String replacement)------用正则表达式实现对字符串的匹配。注意replaceAll第一个参数为正则表达式,鄙人曾经深受其害。
五、更多方法请参考API
StringBuffer和String同样都是用来存储字符串的,只不过因为他们内部的实现方式不一样,致使他们所使用的范围不一样,对于StringBuffer而言,他在处理字符串时,如果对其进行修改操做,它并不会产生一个新的字符串对象,因此说在内存使用方面它是优于String的。
其实在使用方法,StringBuffer的许多方法和String类都差很少,所表示的功能几乎如出一辙,只不过在修改时StringBuffer都是修改自身,而String类则是产生一个新的对象,这是他们之间最大的区别。
同时StringBuffer是不能使用=进行初始化的,它必需要产生StringBuffer实例,也就是说你必须经过它的构造方法进行初始化。
在StringBuffer的使用方面,它更加侧重于对字符串的变化,例如追加、修改、删除,相对应的方法:
一、append():追加指定内容到当前StringBuffer对象的末尾,相似于字符串的链接,这里StringBuffer对象的内容会发生改变。
二、insert:该类方法主要是在StringBuffer对象中插入内容。
三、delete:该类方法主要用于移除StringBuffer对象中的内容。
StringBuilder也是一个可变的字符串对象,他与StringBuffer不一样之处就在于它是线程不安全的,基于这点,它的速度通常都比StringBuffer快。与StringBuffer同样,StringBuider的主要操做也是append与insert方法。这两个方法都能有效地将给定的数据转换成字符串,而后将该字符串的字符添加或插入到字符串生成器中。
上面只是简单的介绍了String、StringBuffer、StringBuilder,其实对于这三者咱们应该更加侧重于他们只见到的区别,只有理清楚他们之间的区别才可以更好的使用他们。
咱们先看以下表格:
这里对于String是否为线程安全,鄙人也不是很清楚,缘由:String不可变,全部的操做都是不可能改变其值的,是否存在线程安全一说还真很差说?可是若是硬要说线程是否安全的话,由于内容不可变,永远都是安全的。
在使用方面因为String每次修改都须要产生一个新的对象,因此对于常常须要改变内容的字符串最好选择StringBuffer或者StringBuilder.而对于StringBuffer,每次操做都是对StringBuffer对象自己,它不会生成新的对象,因此StringBuffer特别适用于字符串内容常常改变的状况下。
可是并非全部的String字符串操做都会比StringBuffer慢,在某些特殊的状况下,String字符串的拼接会被JVM解析成StringBuilder对象拼接,在这种状况下String的速度比StringBuffer的速度快。如:
String name = ”I ” + ”am ” + ”chenssy ” ;
StringBuffer name = new StringBuffer(”I ”).append(” am ”).append(” chenssy ”);
对于这两种方式,你会发现第一种比第二种快太多了,在这里StringBuffer的优点荡然无存。其真实的缘由就在于JVM作了一下优化处理,其实String name = ”I ” + ”am ” + ”chenssy ” ;在JVM眼中就是String name = ”I am chenssy ” ;这样的方式对于JVM而言,真的是不要什么时间。可是若是咱们在这个其中增长一个String对象,那么JVM就会按照原来那种规范来构建String对象了。
对于这三者使用的场景作以下归纳(参考:《编写搞质量代码:改善java程序的151个建议》):
一、String:在字符串不常常变化的场景中可使用String类,如:常量的声明、少许的变量运算等。
二、StringBuffer:在频繁进行字符串的运算(拼接、替换、删除等),而且运行在多线程的环境中,则能够考虑使用StringBuffer,例如XML解析、HTTP参数解析和封装等。
三、StringBuilder:在频繁进行字符串的运算(拼接、替换、删除等),而且运行在多线程的环境中,则能够考虑使用StringBuffer,如SQL语句的拼装、JSON封装等(貌似这两个我也是使用|StringBuffer)。
更多有关于他们之间区别,请参考:http://www.cnblogs.com/zuoxiaolong/p/lang1.html。鄙人就不多此一举了。
对于字符串而言咱们常常是要对其进行拼装处理的,在java中提升了三种拼装的方法:+、concat()以及append()方法。这三者之间存在什么区别呢?先看以下示例:
public class StringTest { /** * @desc 使用+、concat()、append()方法循环10W次 * @author chenssy * @data 2013-11-16 * @param args * @return void */ public static void main(String[] args) { //+ long start_01 = System.currentTimeMillis(); String a = "a"; for(int i = 0 ; i < 100000 ; i++){ a += "b"; } long end_01 = System.currentTimeMillis(); System.out.println(" + 所消耗的时间:" + (end_01 - start_01) + "毫米"); //concat() long start_02 = System.currentTimeMillis(); String c = "c"; for(int i = 0 ; i < 100000 ; i++){ c = c.concat("d"); } long end_02 = System.currentTimeMillis(); System.out.println("concat所消耗的时间:" + (end_02 - start_02) + "毫米"); //append long start_03 = System.currentTimeMillis(); StringBuffer e = new StringBuffer("e"); for(int i = 0 ; i < 100000 ; i++){ e.append("d"); } long end_03 = System.currentTimeMillis(); System.out.println("append所消耗的时间:" + (end_03 - start_03) + "毫米"); } } ------------ Output: + 所消耗的时间:19080毫米 concat所消耗的时间:9089毫米 append所消耗的时间:10毫米
从上面的运行结果能够看出,append()速度最快,concat()次之,+最慢。缘由请看下面分解:
(一)+方式拼接字符串
在前面咱们知道编译器对+进行了优化,它是使用StringBuilder的append()方法来进行处理的,咱们知道StringBuilder的速度比StringBuffer的速度更加快,可是为什么运行速度仍是那样呢?主要是由于编译器使用append()方法追加后要同toString()转换成String字符串,也就说 str +=”b”等同于
str = new StringBuilder(str).append("b").toString();
它变慢的关键缘由就在于new StringBuilder()和toString(),这里但是建立了10W个StringBuilder对象,并且每次还须要将其转换成String,速度能不慢么?
(二)concat()方法拼接字符串
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } char buf[] = new char[count + otherLen]; getChars(0, count, buf, 0); str.getChars(0, otherLen, buf, count); return new String(0, count + otherLen, buf); }
这是concat()的源码,它看上去就是一个数字拷贝形式,咱们知道数组的处理速度是很是快的,可是因为该方法最后是这样的:return new String(0, count + otherLen, buf);这一样也建立了10W个字符串对象,这是它变慢的根本缘由。
(三)append()方法拼接字符串
public synchronized StringBuffer append(String str) { super.append(str); return this; }
StringBuffer的append()方法是直接使用父类AbstractStringBuilder的append()方法,该方法的源码以下:
public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); if (len == 0) return this; int newCount = count + len; if (newCount > value.length) expandCapacity(newCount); str.getChars(0, len, value, count); count = newCount; return this; }
与concat()方法类似,它也是进行字符数组处理的,加长,而后拷贝,可是请注意它最后是返回并无返回一个新串,而是返回自己,也就说这这个10W次的循环过程当中,它并无产生新的字符串对象。
经过上面的分析,咱们须要在合适的场所选择合适的字符串拼接方式,可是并不必定就要选择append()和concat()方法,缘由在于+根据符合咱们的编程习惯,只有到了使用append()和concat()方法确实是能够对咱们系统的效率起到比较大的帮助,才会考虑,同时鄙人也真的没有怎么用过concat()方法。
巩固基础,提升技术,不惧困难,攀登高峰!!!!!!