==判斷的是對象的內(nèi)存起始地址是否相同,equals判斷自定義的語義是否相同
public class Demo { public static void main(String[] args) throws Exception { String s = 'abc'; String s1 = 'abc'; String s2 = 'a' + 'bc'; final String str1 = 'a'; final String str2 = 'bc'; String s3 = str1 + str2; String s4 = new String('abc'); System.out.println(s == s1); System.out.println(s == s2); System.out.println(s == s3); System.out.println(s == s4); }} //結(jié)果:true true true false
final修飾變量,如果是基本類型那么內(nèi)容運(yùn)行期間不可變,如果是引用類型那么引用的對象(包括數(shù)組)運(yùn)行期地址不可變,但是對象(數(shù)組)的內(nèi)容是可以改變的
當(dāng)然只要類庫設(shè)計人愿意,只要增加一個類似的setCharAt(index)的接口,String就變成可變的了
private final char value[]; private int hash; // Default to 0 public String(char value[]) { this.value = Arrays.copyOf(value, value.length); }
public class Demo { public static void main(String[] args) throws Exception { String s = 'abc'; String str = s; String s1 = 'bbb'; System.out.println(str == s); Field f = s.getClass().getDeclaredField('value'); f.setAccessible(true); f.set(s, new char[]{'b', 'b', 'b'}); System.out.println(str + ' ' + s); System.out.println(s == str); System.out.println(s == s1); }} //結(jié)果:bbb bbb true false
s的內(nèi)容改變了但是hashCode值并沒有改變,雖然s與s1的內(nèi)容是相同的但是他們hashCode值并不相同
public class Demo { public static void main(String[] args) throws Exception { String s = 'abc'; String s1 = 'bbb'; System.out.println(s.hashCode()); Field f = s.getClass().getDeclaredField('value'); f.setAccessible(true); f.set(s, new char[]{'b', 'b', 'b'}); System.out.println(s + ' '+ s1); System.out.println(s.hashCode() +' ' +s1.hashCode()); }} //結(jié)果:96354 bbb bbb 96354 97314
String hashCode的源碼
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length;="" i++)="" {="" h="">31 * h + val[i]; } hash = h; } return h; }
public class Demo { @Override public String toString() { //會造成遞歸調(diào)用// return 'address'+super.toString(); return 'address'+super.toString(); } public static void main(String[] args) { System.out.println(new Demo()); }}
String的length表示的是代碼單元的個數(shù),而不是字符的個數(shù)
public class Demo { public static void main(String[] args) { String s = '\u1D56B'; System.out.println(s); System.out.println(s.length()); }}
我們看看String是怎么處理增補(bǔ)字符的
public String(int[] codePoints, int offset, int count) { if (offset <>0) { throw new StringIndexOutOfBoundsException(offset); } if (count <>0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > codePoints.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } final int end = offset + count; // Pass 1: Compute precise size of char[] int n = count; for (int i = offset; i < end;="" i++)="" {="">int c = codePoints[i]; if (Character.isBmpCodePoint(c)) continue; else if (Character.isValidCodePoint(c)) n++; else throw new IllegalArgumentException(Integer.toString(c)); } // Pass 2: Allocate and fill in char[] final char[] v = new char[n]; for (int i = offset, j = 0; i < end;="" i++,="" j++)="" {="">int c = codePoints[i]; if (Character.isBmpCodePoint(c)) v[j] = (char)c; else Character.toSurrogates(c, v, j++); } this.value = v; } static void toSurrogates(int codePoint, char[] dst, int index) { // We write elements 'backwards' to guarantee all-or-nothing dst[index+1] = lowSurrogate(codePoint); dst[index] = highSurrogate(codePoint); }
public final class String implements java.io.Serializable, ComparableString>, CharSequence { private final char value[]; private static final long serialVersionUID = -6849794470754667710L; /** Cache the hash code for the string */ private int hash; // Default to 0 public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length;="" i++)="" {="" h="">31 * h + val[i]; } hash = h; } return h; }
public String(String original) { this.value = original.value; this.hash = original.hash; } public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } } public String(StringBuilder builder) { this.value = Arrays.copyOf(builder.getValue(), builder.length()); } public String(char value[]) { this.value = Arrays.copyOf(value, value.length); } public String(char value[], int offset, int count) { if (offset <>0) { throw new StringIndexOutOfBoundsException(offset); } if (count <>0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); } public String(byte bytes[], int offset, int length, Charset charset) { if (charset == null) throw new NullPointerException('charset'); checkBounds(bytes, offset, length); this.value = StringCoding.decode(charset, bytes, offset, length); } public String(byte bytes[], int offset, int length) { checkBounds(bytes, offset, length); this.value = StringCoding.decode(bytes, offset, length); } static char[] decode(byte[] ba, int off, int len) { String csn = Charset.defaultCharset().name(); try { // use charset name decode() variant which provides caching. return decode(csn, ba, off, len); } catch (UnsupportedEncodingException x) { warnUnsupportedCharset(csn); } try { return decode('ISO-8859-1', ba, off, len); } catch (UnsupportedEncodingException x) { // If this code is hit during VM initialization, MessageUtils is // the only way we will be able to get any kind of error message. MessageUtils.err('ISO-8859-1 charset not available: ' + x.toString()); // If we can not find ISO-8859-1 (a required encoding) then things // are seriously wrong with the installation. System.exit(1); return null; } }
使用外部數(shù)組來初始化String內(nèi)部數(shù)組只有保證傳入的數(shù)組不可能被改變才能保證String的不可變性,例如用String初始化String對象時
String(char[] value, boolean share) { // assert share : 'unshared not supported'; this.value = value; } public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); } // 使用了Arrays.copyof方法來構(gòu)造新的數(shù)組,拷貝元素,而不是共用數(shù)組 public String substring(int beginIndex) { if (beginIndex <>0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen <>0) { throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }
如果String(value,share)可以在外部使用,就可以改變字符串內(nèi)容
public class Demo { public static void main(String[] args) { char[] arr = new char[] {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}; String s = new String(arr,true); arr[0] = 'a'; System.out.println(s); }}
aLongString 已經(jīng)不用了,但是由于其與aPart共享value數(shù)組,所以不能被回收,造成內(nèi)存泄漏
public String subTest(){ String aLongString = '...a very long string...'; String aPart = aLongString.substring(20, 40); return aPart; }
length() 返回字符串長度isEmpty() 返回字符串是否為空charAt(int index) 返回字符串中第(index+1)個字符char[] toCharArray() 轉(zhuǎn)化成字符數(shù)組trim() 去掉兩端空格toUpperCase() 轉(zhuǎn)化為大寫toLowerCase() 轉(zhuǎn)化為小寫String concat(String str) //拼接字符串String replace(char oldChar, char newChar) //將字符串中的oldChar字符換成newChar字符//以上兩個方法都使用了String(char[] value, boolean share);boolean matches(String regex) //判斷字符串是否匹配給定的regex正則表達(dá)式boolean contains(CharSequence s) //判斷字符串是否包含字符序列sString[] split(String regex, int limit) 按照字符regex將字符串分成limit份。String[] split(String regex)
可以看到主要是調(diào)用構(gòu)造函數(shù)或者是調(diào)用對應(yīng)類型的toString完成到字符串的轉(zhuǎn)換
public static String valueOf(boolean b) { return b ? 'true' : 'false'; } public static String valueOf(char c) { char data[] = {c}; return new String(data, true); } public static String valueOf(int i) { return Integer.toString(i); } public static String valueOf(long l) { return Long.toString(l); } public static String valueOf(float f) { return Float.toString(f); } public static String valueOf(double d) { return Double.toString(d); } public static String valueOf(char data[], int offset, int count) { return new String(data, offset, count); } public static String copyValueOf(char data[], int offset, int count) { // All public String constructors now copy the data. return new String(data, offset, count); }
可以看到String的字符串匹配算法使用的是樸素的匹配算法,即前向匹配,當(dāng)遇到不匹配字符時,主串從下一個字符開始,字串從開始位置開始
其他相關(guān)字符串匹配算法
static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) { if (fromIndex >= sourceCount) { return (targetCount == 0 ? sourceCount : -1); } if (fromIndex <>0) { fromIndex = 0; } if (targetCount == 0) { return fromIndex; } char first = target[targetOffset]; int max = sourceOffset + (sourceCount - targetCount); for (int i = sourceOffset + fromIndex; i <= max;="" i++)="" {="">=>/* Look for first character. */ if (source[i] != first) { while (++i <= max="" &&="" source[i]="" !="first);" }="">=>/* Found first character, now look at the rest of v2 */ if (i <= max)="" {="">=>int j = i + 1; int end = j + targetCount - 1; for (int k = targetOffset + 1; j < end="" &&="" source[j]="=" target[k];="" j++,="" k++);="">if (j == end) { /* Found whole string. */ return i - sourceOffset; } } } return -1; }
String s = '你好,世界!'; byte[] bytes = s.getBytes('utf-8'); public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException(); return StringCoding.encode(charsetName, value, 0, value.length); } static byte[] encode(String charsetName, char[] ca, int off, int len) throws UnsupportedEncodingException { StringEncoder se = deref(encoder); String csn = (charsetName == null) ? 'ISO-8859-1' : charsetName; if ((se == null) || !(csn.equals(se.requestedCharsetName()) || csn.equals(se.charsetName()))) { se = null; try { Charset cs = lookupCharset(csn); if (cs != null) se = new StringEncoder(cs, csn); } catch (IllegalCharsetNameException x) {} if (se == null) throw new UnsupportedEncodingException (csn); set(encoder, se); } return se.encode(ca, off, len); }
boolean equals(Object anObject); boolean contentEquals(StringBuffer sb); boolean contentEquals(CharSequence cs); boolean equalsIgnoreCase(String anotherString); int compareTo(String anotherString); int compareToIgnoreCase(String str); boolean regionMatches(int toffset, String other, int ooffset,int len) //局部匹配 boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len) //局部匹配 public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
public String replace(CharSequence target, CharSequence replacement) { return Pattern.compile(target.toString(), Pattern.LITERAL).matcher( this).replaceAll(Matcher.quoteReplacement(replacement.toString())); } public String replaceFirst(String regex, String replacement) { return Pattern.compile(regex).matcher(this).replaceFirst(replacement); } public String replaceAll(String regex, String replacement) { return Pattern.compile(regex).matcher(this).replaceAll(replacement); } public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */ while (++i < len)="" {="">if (val[i] == oldChar) { break; } } if (i < len)="" {="">char buf[] = new char[len]; for (int j = 0; j < i;="" j++)="" {="" buf[j]="val[j];" }="">while (i < len)="" {="">char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; }
public native String intern();
// int轉(zhuǎn)String的方法比較public class Demo { public static void main(String[] args) throws Exception { int i = 5; String i1 = '' + i; String i2 = String.valueOf(i); String i3 = Integer.toString(i); }} // 原始代碼public class Demo { public static void main(String[] args) throws Exception { String string='hollis'; String string2 = string + 'chuang'; }} //反編譯代碼public class Demo { public static void main(String[] args) throws Exception { String string = 'hollis'; String string2 = (new StringBuilder(String.valueOf(string))).append('chuang').toString(); }}