1.String

描述:String并非Java支持的基本类型,而是包含在java.lang这个包下的一个类。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

下面是JDK源码中String的声明:

1 public final class String
2     implements java.io.Serializable, Comparable<String>, CharSequence {
3     /** The value is used for character storage. */
4     private final char value[];
5     /*************************/   
6 }

1)String是一个final类,这意味着String是不可继承的。

2)Java的String和C++的string类似,实质上都是一个字符数组。从源码第4行不难看出,String的本质是对字符数组进行了封装。

3)String中维护的字符数组value[]被声明为final,final类型的数据被初始化后将不可被修改,这使得String成为一个不可变类,意即String一旦被初始化,要想像C++string一样对其中的字符进行修改、替换、删除等基本操作是行不通的。Java引入了另外两个类StringBuilder和StringBuffer来提供这些操作。

注:String的不可修改指的是对象本身,并非指对象的引用。

Java的设计者将String置为不可变类是考虑到线程安全等问题。

初始化:Java String的初始化的方法主要有两种。

1 char Date[]={'a','b','c'};
2 String str1="abc";              //声明变量时同时初始化
3 String str2=new String("abc");  //new
4 String str3=new String(Date);   //转换字符数组
5 System.out.println(str1+" "+str2+" "+str3);

比较:C++中的string可以直接通过简单的判等符号"=="按字母顺序进行比较。

而在Java中,声明得到的变量名(例如上面的str1,str2)都是对象的引用,使用"=="进行比较时,进行比较的其实是引用。而引用相等并不能保证对象本身相等。

关于引用和对象本身之间的关系,举个例子:

String a="abc"; String b="abc" ,那么a==b将返回true。这是因为在java中字符串的值是不可改变的,相同的字符串在内存中只会存一份,所以a和b指向的是同一个对象;

再比如: String a=new String("abc"); String b=new String("abc"); 那么a==b将返回false,因为a和b指向不同的对象。

源码第1行显示String类implements中包括了comparable接口,可以使用compareTo来进行String对象之间的比较。

下面是String类中的compareTo源码,不难看出compareTo比较的本质是将字符串中的字符的Unicode码进行比较。

 1     public int compareTo(String anotherString) {
 2         int len1 = value.length;
 3         int len2 = anotherString.value.length;
 4         int lim = Math.min(len1, len2);
 5         char v1[] = value;
 6         char v2[] = anotherString.value;
 7 
 8         int k = 0;
 9         while (k < lim) {
10             char c1 = v1[k];
11             char c2 = v2[k];
12             if (c1 != c2) {
13                 return c1 - c2;
14             }
15             k++;
16         }
17         return len1 - len2;
18     }

compareTo返回值有三种情况,如果s1.compareTo(s2),-1为<,0为=,1为>。

String重写了Object类中的equals方法,字符串也可以通过equals进行比较。

下面是重写的源码:

 1     public boolean equals(Object anObject) {
 2         if (this == anObject) {
 3             return true;
 4         }
 5         if (anObject instanceof String) {
 6             String anotherString = (String)anObject;
 7             int n = value.length;
 8             if (n == anotherString.value.length) {
 9                 char v1[] = value;
10                 char v2[] = anotherString.value;
11                 int i = 0;
12                 while (n-- != 0) {
13                     if (v1[i] != v2[i])
14                         return false;
15                     i++;
16                 }
17                 return true;
18             }
19         }
20         return false;
21     }

equals的返回值只有两种,true(相等)和false(不相等),当涉及到比较大小时,应该选择compareTo。

忽略大小写的比较可以用equalsIgnoreCase方法。

连接:Java并不支持运算符重载,但Java还是重载了两个运算符“+”和“+=”,可以使用“+”来连接两个字符串。

重载“+”的实质是创建了一个StringBuilder对象然后调用了其内部的append方法。

这一过程将会产生相当多的中间对象,这些对象将交给Java的垃圾回收机制处理。

String也提供了concat方法来连接字符串。

两者内部实现的方式有所不同,concat在内部申请了一个长度为两个要连接的字符串的长度之和的字符数组,用Array.copyOf方法将其复制到各自的位置得到一个字符数组,再重新new一个value为所得字符数组的String变量,返回。

Java字符串:String,StringBuilder与StringBuffer 随笔 第1张
 1     public String concat(String str) {
 2         int otherLen = str.length();
 3         if (otherLen == 0) {
 4             return this;
 5         }
 6         int len = value.length;
 7         char buf[] = Arrays.copyOf(value, len + otherLen);
 8         str.getChars(buf, len);
 9         return new String(buf, true);
10     }
concat

输出:普通的输出使用System.out.println即可,而想要进行格式化的输出则需要其他一些方法。

1)Java支持C/C++中的printf,其格式字符与C相同。e.g  System.out.printf("%d",a); 

2)System.out,format(),使用方法与printf相同,作用也相同。

循秩获取字符:String中的字符不允许被修改,但允许被访问。可以通过调用String中的charAt(i)方法获取秩为i的字符。

下面是charAt的源码:

先获取String的value数组,然后用访问数组的方式获取相应秩的字符并返回。

1 public char charAt(int index) {
2     if ((index < 0) || (index >= value.length)) {
3         throw new StringIndexOutOfBoundsException(index);
4     }
5     return value[index];
6 }

String类型到C风格字符数组的转换:方法toCharArray(),作用是生成一个char类型的数组。

下面是源码:

声明了一个与String长度相等的字符数组,调用arraycopy将value复制到新的字符数组中。

1 public char[] toCharArray() {
2     // Cannot use Arrays.copyOf because of class initialization order issues
3     char result[] = new char[value.length];
4     System.arraycopy(value, 0, result, 0, value.length);
5     return result;
6 }

String区域比较:regionMatches方法

实现的思路很朴实,即把要比较的两个String转化为字符数组,在指定区域内逐个比较。

一开始排除了范围不存在的情况。

 1     public boolean regionMatches(int toffset, String other, int ooffset,
 2             int len) {
 3         char ta[] = value;
 4         int to = toffset;
 5         char pa[] = other.value;
 6         int po = ooffset;
 7         // Note: toffset, ooffset, or len might be near -1>>>1.
 8         if ((ooffset < 0) || (toffset < 0)
 9                 || (toffset > (long)value.length - len)
10                 || (ooffset > (long)other.value.length - len)) {
11             return false;
12         }
13         while (len-- > 0) {
14             if (ta[to++] != pa[po++]) {
15                 return false;
16             }
17         }
18         return true;
19     }

前后缀检查:startsWith和endsWith

实质上是regionMathes的特例,逐个比较的思路。

Java字符串:String,StringBuilder与StringBuffer 随笔 第3张
 1     public boolean startsWith(String prefix, int toffset) {
 2         char ta[] = value;
 3         int to = toffset;
 4         char pa[] = prefix.value;
 5         int po = 0;
 6         int pc = prefix.value.length;
 7         // Note: toffset might be near -1>>>1.
 8         if ((toffset < 0) || (toffset > value.length - pc)) {
 9             return false;
10         }
11         while (--pc >= 0) {
12             if (ta[to++] != pa[po++]) {
13                 return false;
14             }
15         }
16         return true;
17     }
startsWith

字符查找:indexOf和lastIndexOf,类比C++string里的find和rfind

找到则返回秩,找不到则返回-1

 

2.StringBuilder,StringBuffer

这两个类提供了更多关于字符串的操作,它们继承自共同的父类AbstractStringBuilder,因此在很多方面它们的功能是相同的。

StringBuilder更快,而StringBuffer线程安全(雾)。

修改: void setCharAt(int index, char ch) 

StringBuffer中的源码:

通过修改value数组实现。

Java字符串:String,StringBuilder与StringBuffer 随笔 第5张
1     public synchronized void setCharAt(int index, char ch) {
2         if ((index < 0) || (index >= count))
3             throw new StringIndexOutOfBoundsException(index);
4         toStringCache = null;
5         value[index] = ch;
6     }
setCharAt

插入: public insert(int offset, int i) 

删除: public delete(int start, int end) 

替换: replace(int start, int end, String str) 

翻转: public StringBuffer reverse() 

复制: void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 

 

参考资料:【1】Java源码

       【2】《Java编程思想》

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄