Java
基础之字符串操做——String
segmentfault
什么是字符串?若是直接按照字面意思来理解就是多个字符链接起来组合成的字符序列。为了更好的理解以上的理论,咱们先来解释下字符序列,字符序列:把多个字符按照必定的顺序排列起来;而字符序列就是做为字符串的内容而存在的。因此能够把字符串理解为:把多个字符按照必定的顺序排列起来而构成的排列组合。数组
若是仍是很差理解,没有关系,我还有法宝。咱们能够用烤串来比喻说明,能够把字符串看做是烤串,烤串上的每一块肉都至关因而一个字符。把一块块肉按照肥瘦相间的顺序排列并串起来便成了咱们吃的烤串,同理,把多个字符按照必定的顺序“串”起来就构成了字符串。安全
字符串的分类,字符串分为可变的字符串和不可变的字符串两种;这里的不可变与可变指的是字符串的对象仍是不是同一个,会不会由于字符串对象内容的改变而建立新的对象。多线程
Java
中的String
类的对象就是不可变的。StringBuilder
类和StringBuffer
类的对象就是可变的;当对象建立完毕以后,该对象的内容发生改变时不会建立新的对象,也就是说对象的内容能够发生改变,当对象的内容发生改变时,对象保持不变,仍是同一个。String类表示不可变的字符串,当前String
类对象建立完毕以后,该对象的内容(字符序列)是不变的,由于内容一旦改变就会建立一个一个新的对象。并发
String对象的建立:app
String s1 = "laofu";
须要注意这里是双引号:""
,区别与字符char类型的单引号:''
;String s2 = new String("laofu");
;以上两种建立方式的对象在JVM中又是如何分布的呢? 分别有什么区别呢?性能
方式一和方式二在JVM中又是如何分布?测试
上图中的常量池:用于存储常量的地方内存区域,位于方法区中。常量池又分为编译常量池和运行常量池两种:优化
JVM
的时候,其中存储的是字节码的相关信息(如:当前执行到的字节码行号等)。方式一和方式二有何不一样?ui
String s1 = "laofu";
有可能只建立一个String
对象,也有可能建立不建立String
对象;若是在常量池中已经存在"laofu",那么对象s1
会直接引用,不会建立新的String
对象;不然,会先在常量池先建立常量"laofu"
的内存空间,而后再引用。String s2 = new String("laofu");
最多会建立两个String
对象,最少建立一个String
对象。可以使用new
关键字建立对象是会在堆空间建立内存区域,这是第一个对象;而后对象中的字符串字面量可能会建立第二个对象,而第二个对象如方式一中所描述的那样,是有可能会不被建立的,因此至少建立一个String
对象。字符串的本质,字符串在底层其实就是char[]
,char
表示一个字符,好比:
String str = "laofu"; 等价于 char[] cs = new char[]{'l','a','o','f','u'};
String对象的空值:
String s1 = null;
此时s1
没有初始化,也在JVM
中没有分配内存空间。String s2 = "";
此时对象s2已经初始化,值为""
,JVM
已经为其分配内存空间。字符串的比较:使用"=="
和"equals"
会有不一样效果,详情在以前的文章中分享过:[JAVA] 只知对象属性,不知类属性?就算类答应,static都不答应
==
号:用于比较对象引用的内存地址是否相同。equals
方法:在Object
类中和==
号相同,但在自定义类中,建议覆盖equals
方法去实现比较本身内容的细节;因为String
类覆盖已经覆盖了equals
方法,因此其比较的是字符内容。因此能够这样来判断字符串非空:
s1 != null;
;""
):"".equals(s1);
;若是上述两个条件都知足,说明字符串确实为空!
字符串拼接:Java
中的字符串能够经过是"+"
实现拼接,那么代码中字符串拼接在JVM
中又是如何处理的呢?咱们经过一个例子说明:经过比较拼接字符串代码编译先后的代码来查看JVM
对字符串拼接的处理。
经过上述例子不难发现,JVM
会对字符串拼接作一些优化操做,若是字符串字面量之间的拼接,不管有多少个字符串,JVM
都会同样的处理;若是是对象之间拼接,或者是对象和字面量之间的拼接,亦或是方法执行结果参与拼接,String
内部会使用StringBuilder
先来获取对象的值,而后使用append
方法来执行拼接。由此能够总结得出:
""
引号建立的字符串都是直接量,在编译期就会将其存储到常量池中;new String("")
建立的对象会存储到堆内存中,在运行期才建立;"aa" + "bb"
建立的也是直接量,这样的字符串在编译期就能肯定,因此也会存储到常量池中;String
直接量的字符串表达式(如"aa" + s1
)建立的对象是运行期才建立的,对象存储在堆中,由于其底层是创新了StringBuilder
对象来实现拼接的;5. 不管是使用变量,仍是调用方法来链接字符串,都只能在运行期才能肯定变量的值和方法的返回值,不存在编译优化操做。
这里列举了一些经常使用String API
,更多的能够查阅jdk使用手册
,作Java
必定得学会查阅jdk手册
。
String 的建立和转换:
byte[] getBytes()
:把字符串转换为byte
数组。char[] toCharArray()
:把字符串转换为char
数组。String(byte[] bytes)
:把byte
数组转换为字符串。String(char[] value)
:把char
数组转换为字符串。获取字符串信息
int length()
:返回此字符串的长度。char charAt(int index)
:返回指定索引处的 char
值。int indexOf(String str)
:返回指定字符串在此字符串中首次(从最左边算起)出现处的索引。int lastIndexOf(String str)
:返回指定字符串在此字符串中最后(最右边算起)出现处的索引。字符串比较判断
boolean equals(Object anObject)
: 将此字符串与指定的对象比较。boolean equalsIgnoreCase(String anotherString)
: 将此 String 与另外一个 String 作忽略大小写的比较。boolean contentEquals(CharSequence cs)
: 将此字符串与指定的 CharSequence
比较,比较的是内容;
字符串大小写转换:调用方法的字符串就是当前字符串
String toUpperCase()
: 把当前字符串转换为大写String toLowerCase()
: 把当前字符串转换为小写先来分别使用String/StringBuilder/StringBuffer
来拼接30000次
字符串,对比各自损耗的时间,通过测试发现:
String
作字符串拼接的时候,耗时最高,性能极低,缘由是String
内容是不可变的,每次内容改变都会在内存中建立新的对象。
性能最好的是StringBuilder
,其次是StringBuffer
,最后是String
。StringBuilder
和StringBuffer
区别并非很大,也有多是测试次数还不够吧。感兴趣的小伙伴能够增长拼接次数来看看。代码很简单,就不展现出来了。
因此在开发中拼接字符串时,优先使用StringBuffer/StringBuilder
,不到万不得已,不要轻易使用String
。
StringBuilder以及StringBuffer的区别
StringBuffer
和StringBuilder
都表示可变的字符串,两种’的功能方法都是相同的。但惟一的区别:
StringBuffer
:StringBuffer
中的方法都使用了synchronized
修饰符,表示同步操做,在多线程并发的时候能够保证线程安全,但在保证线程安全的时候,对其性能有必定影响,会下降其性能。StringBuilder
:StringBuilder
中的方法都没有使用了synchronized
修饰符,线程不安全,正由于如此,其性能较高。对并发安全没有很高要求的状况下,建议使用StringBuilder
,由于其性能很高。像这样的状况会较多些。使用StringBuilder
无参数的构造器,在底层建立了一个长度为16
的char
数组:
此时该数组只能存储16
个字符,若是超过了16
个字符,会自动扩容(建立长度更大的数组,再把以前的数组拷贝到新数组),此时性能极低;若是事先知道大概须要存储多少字符,能够经过构造器来设置字符的初始值:
//建立一个长度为80的char数组. new StringBuilder(80);
StringBuilder的经常使用方法:
append(Object val)
:追加任意类型数据到当前StringBuilder
对象中。StringBuilder deleteCharAt(int index)
:删除字符串中指定位置的字符。完结。虽然老夫不正经,但老夫一身的才华