夯实Java基础(十三)——字符串

字符串应该是咱们在Java中用的最频繁、最多的,可见字符串对于咱们来讲是多么的重要,因此咱们很是有必要去深刻的了解一下。正则表达式

一、String

String就表明字符串,在Java中字符串属于对象。咱们刚刚接触Java时,在学习数据类型的时候应该提到过String。Java有基本数据类型和引用数据类型,而String就是一个引用数据类型,它是一个类,既然它是一个类,那咱们就来看看它的源码结构。数组

从上面的图能够看出,String类是用final修饰的,代表它不能再被继承了,由于String这个类中对字符串的操做的方法已经很是丰富,不须要咱们再去扩展功能了。同时还实现了序列化、比较排序、字符序列这三个接口,代表字符串能够被序列化、能够用于比较排序。关于实现了CharSequence接口下面有讲到。咱们还能够看到String类中定义了一个char型数组value[ ],这个是用于存储字符串的内容,而且使用final修饰的,代表这个是常量。因此字符串一旦被初始化,就不能够被改变,表示String是不可变性,这就致使每次对String的操做都会生成新的String对象。安全


关于String实现CharSequence接口这里,我去百度一下:多线程

CharSequence是一个接口,表示char值的一个可读序列。此接口对许多不一样种类的char序列提供统一的自读访问。此接口不修改该equals和hashCode方法的常规协定,所以,一般未定义比较实现 CharSequence 的两个对象的结果。他有几个实现类:CharBuffer、String、StringBuffer、StringBuilder。app

CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。性能

对于一个抽象类或者是接口类,不能使用new来进行赋值,可是能够经过如下的方式来进行实例的建立:学习

CharSequence cs="hello";测试

实例:ui

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         String str="String";
 4         StringBuffer sBuffer=new StringBuffer("StringBuffer");
 5         StringBuilder sBuilder=new StringBuilder("StringBuilder");
 6         show(str);
 7         show(sBuffer);
 8         show(sBuilder);
 9     }
10     //若是参数类型为String则不能接收StringBuffer和StringBuilder
11     public static void show(CharSequence cs){
12         System.out.println(cs);
13     }
14 }

运行结果:spa

多是这样理解的吧,CharSequence是一个接口,自己是没有什么读写意义的。String只是它的一个实现类,虽然String是只读,可是CharSequence的实现类还有StringBuffer,StringBuilder这些可写的,因此用CharSequence做为参数能够接收String,StringBuffer,StringBuilder这些类型。

参考:https://blog.csdn.net/a78270528/article/details/46785949

二、String的实例和拼接

String的实例:

建立String的实例有两种方式,一种是直接给String的变量赋值,另外一种是使用String的构造器建立实例。那么这两种方式建立的实例有什么区别呢?区别就是前者会建立一个对象,然后者会建立两个对象。

举例:

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         //方式一:直接赋值
 4         String s1="abc";
 5         String s2="abc";
 6         //方式二:new+构造器
 7         String s3=new String("abc");
 8         String s4=new String("abc");
 9         System.out.println(s1 == s2);//true
10         System.out.println(s1 == s3);//false
11         System.out.println(s1 == s4);//false
12         System.out.println(s3 == s4);//false
13         System.out.println(s1.equals(s3));//true
14     }
15 }

运行结果一目了然,String的值是常量,它的值是放在方法区的处理池中,而常量池中相同的值在只会存在一份。咱们知道==比较的地址,equal()比较的是内容,s1和s2指向同一个引用,因此地址相同,而s3和s4它们分别建立了两个对象,地址值显然不一样。

经过这个图咱们也容易分析出建立实例时建立了几个对象。

String s1 = "abc"建立对象的过程:

首先检查常量池中是否存在内容为"abc"的字符串,若是有,则再也不建立对象,直接让s1变量指向该字符串的引用,若是没有则在常量池中建立"abc"对象而后让s1引用该对象。

String s3 = new String("abc")建立实例的过程:

首先在堆建立一个String的对象,并让s3引用指向该对象,而后再到常量池中查看是否存在内容为"abc"字符串对象,若是存在,则将String对象中的value引用指向常量对象,将new出来的字符串对象与字符串常量池中的对象联系起来,若是不存在,则在字符串常量池中建立一个内容为"abc"的字符串对象,并将堆中的String对象与之联系起来。


 String的拼接:

字符串的拼接有三种方式:直接使用"+"、使用concat()方法、使用append()方法。这里我主要来讨论一下"+"问题,举例:

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         String s1="Hello";
 4         String s2="World";
 5 
 6         String s3="HelloWorld";
 7         String s4="Hello"+"World";
 8         String s5=s1+"World";
 9         String s6="Hello"+s2;
10         String s7=s1+s2;
11 
12         System.out.println(s3==s4);//true
13         System.out.println(s3==s5);//false
14         System.out.println(s3==s6);//false
15         System.out.println(s3==s7);//false
16         System.out.println(s5==s6);//false
17         System.out.println(s5==s7);//false
18         System.out.println(s6==s7);//false
19 
20         String s8=s7.intern();
21         System.out.println(s3==s8);//true
22 
23     }
24 }

从运行结果咱们能够得出结论:①、常量与常量的拼接结果是常量,它们在在常量池中完成。②、只要涉及到有变量的(很是量),结果都是在堆内存中完成的。③、若是拼接后的结果调用了intern()方法,则返回值就是在常量池中。

三、String中经常使用的方法

String中的经常使用方法咱们须要熟练的掌握, 这样平时作一些字符串的常规操做就能够快速知道,而不用去查找API了。

①、常规:

  • int length():返回字符串的长度: return value.length
  • boolean isEmpty():判断是不是空字符串:return value.length == 0
  • String trim():返回字符串的副本,忽略前导空白和尾部空白
  • String concat(String str):将指定字符串链接到此字符串的结尾。 等价于用“+”
  • String toLowerCase():使用默认语言环境,将 String 中的全部字符转换为小写
  • String toUpperCase():使用默认语言环境,将 String 中的全部字符转换为大写
  • char[] toCharArray():将字符串转为char型数组。
②、比较:
  • boolean equals(Object obj):比较字符串的内容是否相同
  • boolean equalsIgnoreCase(String anotherString):与equals方法相似,忽略大小写
  • int compareTo(String anotherString):比较两个字符串的大小
  • boolean matches(String regex):判断此字符串是否匹配给定的正则表达式。
③、查找:
  • char charAt(int index): 返回某索引处的字符return value[index]
  • int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引。注:indexOf和lastIndexOf方法若是未找到都是返回-1
  • int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
  • int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
  • int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
  • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
  • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
  • boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
  • boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
④、替换:
  • String replace(char oldChar, char newChar):返回一个新的字符串,它是经过用 newChar 替换此字符串中出现的全部 oldChar 获得的。
  • String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串全部匹配字面值目标序列的子字符串。
  • String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串全部匹配给定的正则表达式的子字符串。
  • String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
⑤、截取:
  • String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
  • String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
⑥、切片:
  • String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
  • String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,若是超过了,剩下的所有都放到最后一个元素中。

若是还想学习更多的方法,能够自行去看String的API。

四、StringBuffer和StringBuilder

StringBuffer和StringBuilder十分类似,它们都表明可变的字符序列,能够对字符序列进行增删改查操做,此时不会产生新的对象,并且它两内部的方法也是同样的。那么String、StringBuffer和StringBuilder的区别是什么。

String(JDK1.0):字符串常量,不可变字符序列,线程安全,效率低。

StringBuffer(JDK1.0):字符串变量,可变字符序列,线程安全,效率低。

StringBuilder(JDK5.0):字符串变量,可变字符序列,线程不安全,效率高。

为何说String和StringBuffer是线程安全,StringBuilder是线程不安全呢?

由于String是final修饰的常量,它是不可变的字符串,全部的操做都是不可能改变它的值,因此线程是安全的。再经过看StringBuffer和StringBuilder的源码,能够很明显发现,StringBuffer是线程安全的,由于其下的全部方法都加上了synchronized。而StringBuilder则没有加这个关键字。

它们之间经常使用的方法:

  • StringBuffer append(xxx):用于进行字符串的拼接
  • cahr charAt(int index):返回char在指定索引在这个序列值。 
  • StringBuffer delete(int start,int end):删除指定位置的内容
  • StringBuffer deleteCharAt(int index):删除char在这个序列中的指定位置。 
  • StringBuffer replace(int start,int end,String str):把[start,end)位置上的元素替换为str
  • void sedtCharAt(int n,cahr ch):将指定索引位置的字符改为ch
  • StringBuffer insert(int offset,xxx):在指定位置插入xxx
  • StringBuffer reverse():将字符序列反转
  • int indexOf(String str):返回指定子字符串第一次出现的字符串内的索引。
  • int lastIndexOf(String str):返回指定子字符串最右边出现的字符串内的索引。 
  • String subString(int start,int end):返回一个新的 String,其中包含此序列中当前包含的字符的子序列。 

五、String、StringBuffer和StringBuilder三者效率对比

String、StringBuffer和StringBuilder涉及可变序列与不可变序列、线程是否安全状况,这些因素必然影响到它们之间的运行效率,因此咱们来比较一下他们之间的运行效率。

简单示例:

 1 public class StringTest {
 2     public static void main(String[] args) {
 3         long startTime=0L;
 4         long endTime=0L;
 5         String text=" ";
 6         StringBuffer buffer=new StringBuffer("");
 7         StringBuilder builder = new StringBuilder("");
 8 
 9         //String
10         startTime=System.currentTimeMillis();
11         for (int i = 0; i < 50000; i++) {
12             text=text+i;
13         }
14         endTime=System.currentTimeMillis();
15         System.out.println("String执行时间:"+(endTime-startTime));
16 
17         //StringBuffer
18         startTime=System.currentTimeMillis();
19         for (int i = 0; i < 50000; i++) {
20             buffer.append(String.valueOf(i));
21         }
22         endTime=System.currentTimeMillis();
23         System.out.println("StringBuffer执行时间:"+(endTime-startTime));
24 
25         //StringBuilder
26         startTime=System.currentTimeMillis();
27         for (int i = 0; i < 50000; i++) {
28             builder.append(String.valueOf(i));
29         }
30         endTime=System.currentTimeMillis();
31         System.out.println("StringBuilder执行时间:"+(endTime-startTime));
32     }
33 }

运行结果可能须要等个5-8秒,运行结果以下:

咱们屡次运行的结果也大体相同,因此运行效率为:StringBuilder > StringBuffer > String。String如此的慢是由于它是字符串常量,在建立对象后是不可改变的,然而每次改变String类型的值都会在常量池中新建一个常量对象,因此很是耗时间。而StringBuffer和StringBuilder的可变的字符序列,它们只是在原有内容发生了改变,并无新建立对象。因此常常改变内容的字符串最好不要用 String类型,推荐使用StringBuffer,由于每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了之后, JVM 的 GC 就会开始工做,性能就会下降。

六、小结

经过上面的学习,咱们简单小结一下:

String:适用于少许字符串操做的状况。

StringBuffer:适用多线程下在字符缓冲区进行大量操做的状况。

StringBuilder:适用于单线程下在字符缓冲区进行大量操做的状况。

相关文章
相关标签/搜索