String 不可变字符序列
定义
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
因为有final修饰,所以String类不能被继承
实现了serializable接口,可以进行对象序列化
实现了comparable接口 对其中的compareTo方法进行了重写(后面分析)
实现了CharSequence接口,其实就是一个字符序列,stringbuffer和stringBuilder都实现了该接口,说明他们的底层都是字符数组实现的,也就是说字符串是Unicode字符序列组成
变量
private final char value[]; //底层是数组实现 并且由final修饰,所以说是string是不可变的 private int hash; //哈希码 联想hashcode()函数 //反序列化时对比码 private static final long serialVersionUID = -6849794470754667710L;
构造函数
/** * 无参构造方法 * 因为string对象不可变,所以没必要调用此方法 */ public String() { this.value = "".value; } /** * * * 只将value指向了原来的数组 */ public String(String original) { this.value = original.value; this.hash = original.hash; } /** * 以字符数组作为参数 * 内部调用了 Arrays.copyOf(value, value.length) * 只是将该数组复制到value中 * Arrays.copyOf 又调用了System.arraycopy * Arrays.copyOf 返回了一个新建的字符数组 * System.arraycopy 是一个本地方法 不是Java语言实现的 */ public String(char value[]) { this.value = Arrays.copyOf(value, value.length); } public static char[] copyOf(char[] original, int newLength) { char[] copy = new char[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
功能函数
/** * public String substring(int beginIndex) * 参数不在字符数组范围内将抛出数组越界异常 * 截取传入参数及以后位置的字符所构成的字符串,包括起始位 * 如果说从0开始,那就等于没有截取,所以返回当前字符串 * 因为字符串为不可变的所以一旦被截取则返回新的字符串对象 */ 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); } /** * 截取起始位到终止位之间字符所构成的字符串 * 包括起始位置字符但不包括终止位 * 其他与上述类似 */ public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
replace函数(替换指定字符)
//1.替换字符时,如果要替换的字符和被替换的字符相等,则等效于没有替换,所以直接返回当前字符串 //2.当要替换的和被替换的字符不等时进入if语句里面继续执行,此时如果在字符串中找到被替换的字符 //则替换该字符 最终返回一个新的字符串 public String replace(char oldChar, char newChar) { 1 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]; 2 buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; }
spilt切割函数 按指定规则进行字符串切割,返回一个字符串数组
trim去除首尾所有空格
toCharArrary 返回该字符串对应的字符数组,此时返回的是个新的字符数组
其他
/** * equals方法 * 先直接==判断是否相等,相等返回true * 然后进行类型判断,为true则继续进行 否则返回false * 然后判断相应位置字符是否相等,一旦不等则返回false * 所有位置的字符相等才返回true * 根据这样的规则,所以字符串对象在使用equals进行比较时比较的是内容 */ 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; }
compareTo()
/** * 重写接口compareTo方法 * 转换为字符数组进行操作,比较次数最多为两个字符串长度较小的 * 字符可以与int相互转换,所以可以使用"-"操作符 * 由数组首位开始进行比较 * 当两个字符不等时,返回正数,所以默认是升序排列 * 两个字符不等时继续下一个位置的字符比较 */ public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; } public static void main(String[] args) { String[] strings= {"apple","fool","appear","food","as"}; Arrays.sort(strings); for (String string : strings) { System.out.println(string); } } 输出: appear apple as food fool
stringBuilder 可变字符序列
定义
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence
继承了一个抽象类AbstractStringBuilder
可以看出StringBuilder和StringBuffer都继承了该抽象类
同样是数组实现但是不同于string该数组并没有被final和private修饰
构造函数
//无参构造函数 里面调用父类的一个构造方法 public StringBuilder() { super(16); } //创建大小为16字符数组并将引用赋值给value AbstractStringBuilder(int capacity) { value = new char[capacity]; } //有参构造函数,指定字符数组的大小,同样调用上述父类的构造方法 public StringBuilder(int capacity) { super(capacity); } /** 构造方法 参数为字符串 * 同样调用了父类的构造方法 数组长度为当前字符串长度加上16 * 调用append方法 append方法又调用了父类的该方法 将字符串放入value中 */ public StringBuilder(String str) { super(str.length() + 16); append(str); } public StringBuilder append(String str) { super.append(str); return this; } public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }
append用于追加字符串,从函数可以看出返回值始终为this 也就是说每次操作后返回的都是自己,即都是在操作同一个对象,
扩容
不同于string中的数组没有被限制,可变都是通过操作该数组来实现的
//确保容量 当超出容量后 重新new了数组并扩容然后替换之前的数组 private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { 1 value = Arrays.copyOf(value, 2 newCapacity(minimumCapacity)); } } 1 public static char[] copyOf(char[] original, int newLength) { char[] copy = new char[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } 2 private int newCapacity(int minCapacity) { // overflow-conscious code int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; }
删除指定位置之间的字符串,取首不取尾
public StringBuilder delete(int start, int end)