当我们在聊String
本文出自:http://blog.csdn.net/dt235201314/article/details/78330377
一丶概述
还记得那会的“Hello World”,第一个程序,输出的String,下面详情String源码,颇有计算机二级考试习题的感觉。
二丶源码及案例
image.png
1.String是final类型的
在Java中,被 final 类型修饰的类不允许被其余类继承,被final修饰的变量赋值后不允许被修改。
什么是不可变类?
所谓不可变类,就是创立该类的实例后,该实例的属性是不可改变的,java提供的包装类和java.lang.String类都是不可变类。当创立它们的实例后,其实例的属性是不可改变的。
需要注意的是,对于如下代码
String s="abc";s="def";你可能会感到疑惑,不是说String是不可变类吗,这怎样可以改变呢,平时我也是这样用的啊。请注意,s是字符串对象的”abc”引用,即引用是可以变化的,跟对象实例的属性变化没有什么关系,这点请注意区分。
2.String类实现了Serializable, Comparable, CharSequence接口。
Comparable接口有compareTo(String s)方法,CharSequence接口有length(),charAt(int index),subSequence(int start,int end)方法,后面详解
3.成员变量
//用于存储字符串private final char value[]; //缓存String的hash值private int hash; // Default to 0String类中包含一个不可变的char数组用来存放字符串,一个int型的变量hash用来存放计算后的哈希值。
4.构造函数
//不含参数的构造函数,一般没什么用,由于value是不可变量public String() { this.value = new char[0];} //参数为String类型public String(String original) { this.value = original.value; this.hash = original.hash;} //参数为char数组,使用java.utils包中的Arrays类复制public String(char value[]) { this.value = Arrays.copyOf(value, value.length);} //从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到valuepublic String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException("charsetName"); checkBounds(bytes, offset, length); this.value = StringCoding.decode(charsetName, bytes, offset, length);} //调用public String(byte bytes[], int offset, int length, String charsetName)构造函数public String(byte bytes[], String charsetName) throws UnsupportedEncodingException { this(bytes, 0, bytes.length, charsetName);} //StringBuffer 和 StringBuider 也可以被当做构造 String 的参数。//这两个构造方法是很少用到的,由于当我们有了 StringBuffer 或者者 StringBuilfer 对象之后可以直接使用他们的 toString 方法来得到 String。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()); } 相关问题:
String两种不同的赋值方式
String str = new String(“abc”);
String str = “abc”;
为什么String可以不用new即可以创立对象?这两种赋值方式有什么不同?
例:
public class Test { public static void main(String[] args) { String a = "ok"; // 新建了一个String对象 String b = "ok"; // 从缓冲池找 String c = new String("ok"); // 新建一个String对象 String d = new String("ok"); // 不从缓冲池找,新建一个 System.out.println(a==b);//将输出"true";由于两个变量指向同一个对象。 System.out.println(c==d);//将输出"flase";由于两个变量不指向同一个对象。尽管值相同,只有用c.equals(d)才能返回true. String e = "a"+"b"+1; String f = "ab1"; System.out.println(e == f);//将输出"true";由于编译器识别 “e = "a"+"b"+1”等同于“e = "ab1"” String g = new String("ab1"); String h = "ab1"; System.out.println(g == h);////将输出"flase";由于两个变量不指向同一个对象 } }5.常用方法
boolean equals(Object anObject)
boolean equals(Object anObject) public boolean equals(Object anObject) { //假如引用的是同一个对象,返回真 if (this == anObject) { return true; } //假如不是String类型的数据,返回假 if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; //假如char数组长度不相等,返回假 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;}equals方法经常用得到,它用来判断两个对象从实际意义上能否相等,String对象判断规则:
1. 内存地址相同,则为真。
2. 假如对象类型不是String类型,则为假。否则继续判断。
3. 假如对象长度不相等,则为假。否则继续判断。
4. 从后往前,判断String类中char数组value的单个字符能否相等,有不相等则为假。假如一直相等直到第一个数,则返回真。
int compareTo(String anotherString)
public int compareTo(String anotherString) { //自身对象字符串长度len1 int len1 = value.length; //被比较对象字符串长度len2 int len2 = anotherString.value.length; //取两个字符串长度的最小值lim int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; //从value的第一个字符开始到最小长度lim处为止,假如字符不相等,返回自身(对象不相等处字符-被比较对象不相等字符) while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } //假如前面都相等,则返回(自身长度-被比较对象长度) return len1 - len2;}了解:
java中的compareto方法,返回参加比较的前后两个字符串的asc码的差值,看下面一组代码
String a=”a”,b=”b”;
System.out.println(a.compareto.b);
则输出-1;
若a=”a”,b=”a”则输出0;
若a=”b”,b=”a”则输出1;
单个字符这样比较,若字符串比较长呢??
若a=”ab”,b=”b”,则输出-1;
若a=”abcdef”,b=”b”则输出-1;
也就是说,假如两个字符串首字母不同,则该方法返回首字母的asc码的差值;
假如首字母相同呢??
若a=”ab”,b=”a”,输出1;
若a=”abcdef”,b=”a”输出5;
若a=”abcdef”,b=”abc”输出3;
若a=”abcdef”,b=”ace”输出-1;
即参加比较的两个字符串假如首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的asc码差值,假如两个字符串不一样长,可以参加比较的字符又完全一样,则返回两个字符串的长度差值
int hashCode()
int hashCode() public int hashCode() { int h = hash; //假如hash没有被计算过,并且字符串不为空,则进行hashCode计算 if (h == 0 && value.length > 0) { char val[] = value; //计算过程 //s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } //hash赋值 hash = h; } return h;}String类重写了hashCode方法,Object中的hashCode方法是一个Native调用。String类的hash采用多项式计算得来,我们完全可以通过不相同的字符串得出同样的hash,所以两个String对象的hashCode相同,并不代表两个String是一样的。
boolean startsWith(String prefix,int toffset)
boolean startsWith(String prefix,int toffset) public boolean startsWith(String prefix, int toffset) { char ta[] = value; int to = toffset; char pa[] = prefix.value; int po = 0; int pc = prefix.value.length; // Note: toffset might be near -1>>>1. //假如起始地址小于0或者者(起始地址+所比较对象长度)大于自身对象长度,返回假 if ((toffset < 0) || (toffset > value.length - pc)) { return false; } //从所比较对象的末尾开始比较 while (--pc >= 0) { if (ta[to++] != pa[po++]) { return false; } } return true;} public boolean startsWith(String prefix) { return startsWith(prefix, 0);} public boolean endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length);}起始比较和末尾比较都是比较经常用得到的方法,例如在判断一个字符串是不是http协议的,或者者初步判断一个文件是不是mp3文件,都可以采用这个方法进行比较
String concat(String str)
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);}concat方法也是经常用的方法之一,它先判断被增加字符串能否为空来决定要不要创立新的对象。
String replace(char oldChar,char newChar)
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;}这个方法也有讨巧的地方,例如最开始先找出旧值出现的位置,这样节省了一部分比照的时间。replace(String oldStr,String newStr)方法通过正则表达式来判断。
String trim()
public String trim() { int len = value.length; int st = 0; char[] val = value; /* avoid getfield opcode */ //找到字符串前段没有空格的位置 while ((st < len) && (val[st] <= ' ')) { st++; } //找到字符串末尾没有空格的位置 while ((st < len) && (val[len - 1] <= ' ')) { len--; } //假如前后都没有出现空格,返回字符串本身,左右有空格则去掉空格 return ((st > 0) || (len < value.length)) ? substring(st, len) : this;}ASCII值比较,“”为32,字母都大于64,可参考ASCII值表
String substring()
// 截取字符串 public String substring(int beginIndex) { if (beginIndex < 0) { // 假如起始下标<0 throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; // 获取截取长度 if (subLen < 0) { // 假如截取长度<0 throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }// 截取字符串 public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { // 假如起始下标<0 throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { // 假如末尾下标>字符数组长度 throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; // 获取截取长度 if (subLen < 0) { // 假如截取长度<0 throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }如:
“hamburger”.substring(4) returns “urger”
“hamburger”.substring(4, 8) returns “urge”
“smiles”.substring(1, 5) returns “mile”
indexOf():
//该字符跟数组中的每个字符从左往右比较 //lastIndexOf一样只不过是从右往左比较 public int indexOf(int ch, int fromIndex) { final int max = value.length; if (fromIndex < 0) { fromIndex = 0; } else if (fromIndex >= max) { // Note: fromIndex might be near -1>>>1. return -1; } if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { // handle most cases here (ch is a BMP code point or a // negative value (invalid code point)) final char[] value = this.value; for (int i = fromIndex; i < max; i++) { if (value[i] == ch) { return i; } } return -1; } else { return indexOfSupplementary(ch, fromIndex); } } //indexOf最终是调用下面的第二个static方法来进行求解的 //求解步骤大概是: //首先搜索到第一个字符所在的位置,之后一一比较; //这里并没有使用kmp算法因而是一个可以优化的地方 public int indexOf(String str, int fromIndex) { return indexOf(value, 0, value.length, str.value, 0, str.value.length, fromIndex); } 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; } split
public String[] split(String regex, int limit) { char ch = 0;//1. 假如regex只有一位,且不为列出的特殊字符;//2.如regex有两位,第一位为转义字符且第二位不是数字或者字母,“|”表示或者,即只需ch小于0或者者大于9任一成立,小于a或者者大于z任一成立,小于A或者大于Z任一成立//3.第三个是和编码有关,就是不属于utf-16之间的字符 if (((regex.value.length == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || (regex.length() == 2 && regex.charAt(0) == '\\' && (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && ((ch-'a')|('z'-ch)) < 0 && ((ch-'A')|('Z'-ch)) < 0)) && (ch < Character.MIN_HIGH_SURROGATE || ch > Character.MAX_LOW_SURROGATE)) { int off = 0; int next = 0; boolean limited = limit > 0; ArrayList<String> list = new ArrayList<>();//假如这个字符串中包含了用于分割的ch,则进行下一步操作 while ((next = indexOf(ch, off)) != -1) { if (!limited || list.size() < limit - 1) { list.add(substring(off, next)); off = next + 1; } else { list.add(substring(off, value.length)); off = value.length; break; } } // If no match was found, return this if (off == 0) return new String[]{this}; // Add remaining segment if (!limited || list.size() < limit) list.add(substring(off, value.length)); // Construct result int resultSize = list.size(); if (limit == 0) while (resultSize > 0 && list.get(resultSize - 1).length() == 0) resultSize--; String[] result = new String[resultSize]; return list.subList(0, resultSize).toArray(result); } return Pattern.compile(regex).split(this, limit); } 详解见:
String.split()方法你可能不知道的一面
String intern()
public native String intern();intern方法是Native调用,它的作用是在方法区中的常量池里通过equals方法寻觅等值的对象,假如没有找到则在常量池中开拓一片空间存放字符串并返回该对应String的引用,否则直接返回常量池中已存在String对象的引用
例:
String a = new String("ab1");String b = new String("ab1").intern();a == b就为true
int hash32()
int hash32() private transient int hash32 = 0;int hash32() { int h = hash32; if (0 == h) { // harmless data race on hash32 here. h = sun.misc.Hashing.murmur3_32(HASHING_SEED, value, 0, value.length); // ensure result is not zero to avoid recalcing h = (0 != h) ? h : 1; hash32 = h; } return h;}在JDK1.7中,Hash相关集合类在String类作key的情况下,不再使用hashCode方式离散数据,而是采用hash32方法。这个方法默认使用系统当前时间,String类地址,System类地址等作为因子计算得到hash种子,通过hash种子在经过hash得到32位的int型数值。
其余方法:
public int length() { return value.length;}public String toString() { return this;}public boolean isEmpty() { return value.length == 0;}public char charAt(int index) { if ((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } return value[index];}三丶参考文章
String 源码解析,深入认识String
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 当我们在聊String