一、string 属性
/** The value is used for character storage. */ 直译:该值用于字符存储,故:string底层是由数组实现的 private final char value[];
/** Cache the hash code for the string */ 直译:缓存字符串的哈希代码。故:這個涉及到 == 和 eques 比較 private int hash; // Default to 0
/** * Class String is special cased within the Serialization Stream Protocol. * * A String instance is written into an ObjectOutputStream according to * <a href="{@docRoot}/../platform/serialization/spec/output.html"> * Object Serialization Specification, Section 6.2, "Stream Elements"</a> */ 直译:序列化作用 private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
可序列化类中可序列化字段的描述。数组
用于声明类的可序列化字段。
二、不可变问题
注:
final 关键字表示对象是最终形态的,对象是不可改变的。
final 应用于类、方法和变量时意义是不同的,但本质是一样的:final 表示不可改变。
final 用在变量的前面表示变量的值不可以改变,此时该变量可以被称为常量。
final 用在方法的前面表示方法不可以被重写
final 用在类的前面表示类不可以被继承,即该类是最终形态。
String 被final修饰,所以Strng不能被继承,由于String是由数组构成,并且属性value被final也不能修改,一旦初始化之后就不会改变。
三、构造:例
// 线程安全 public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } }
StringBuffer insert、append,及大多方法是由synchronized关键字修饰的,StringBuffer的父类AbstractStringBuilder
是一个抽象类实现了: Appendable, CharSequence
StringBuffer的insert和append直接使用的是父类的,并且父类根据参数类型不同分别重载了append方法,
由此可见stringbuffer插入和append在业务处理上是基本一致的。
四:string常用方法
public int length() {return value.length;} // 通过数组属性获取长度。 public boolean isEmpty() {return value.length == 0;} // 调用该方法的时候一定要校验是否为null,否则会NPE //例: a.equals(anObject) public boolean equals(Object anObject) { // 先对数值进行比较,如果一致则返回true if (this == anObject) { return true; } // 校验比较对象是否是String类型,否则直接返回false,类型都不一致,就没必要继续匹配了 if (anObject instanceof String) { // 强转,类型一致 String anotherString = (String)anObject; // a的长度 int n = value.length; // 如果类型一致,长度不一致,则没必要继续匹配,直接返回false if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; // 循环比较如果匹配过程中有一个元素不一致,则判定不成立,返回false while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } // 测试此字符串的子字符串是否从指定的索引以指定的前缀开头。 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. if ((toffset < 0) || (toffset > value.length - pc)) { return false; } while (--pc >= 0) { if (ta[to++] != pa[po++]) { return false; } } return true; }
这是我们常用的配备前缀或者判断是否以指定字符开头的方法,所以可以直接指定开始索引,更直接
以指定字符串结束,还是通过startsWith方法来处理,
空字符串的哈希值为零,如果数组有值,则循环计算哈希码值
String str= "abcd"; // 此时value[] = {'a','b','c','d'} 因此
for循环会执行4次
第一次:h = 31*0 + a = 97
第二次:h = 31*97 + b = 3105
第三次:h = 31*3105 + c = 96354
第四次:h = 31*96354 + d = 2987074
以上代码可以算出 str的hashcode = 2987074
可以使用System.err.println(new String("abcd").hashCode()); 进行验证
// 返回此字符串中第一次出现指定的字符,从指定索引开始搜索。 public int indexOf(int ch, int fromIndex) { // 数据校验,设置索引,所以可以看出是从0开始 final int max = value.length; if (fromIndex < 0) { fromIndex = 0; // 如果目标索引大于当前字符最大长度,那么目标字符肯定不会出现在当前字符中,所以返回不包含 } else if (fromIndex >= max) { // Note: fromIndex might be near -1>>>1. return -1; } // Character.MIN_SUPPLEMENTARY_CODE_POINT, // 补充码位。U+10000和U+10FFFF之间的Unicode码位。 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); } }
循环匹配返回索引,否则
private int indexOfSupplementary(int ch, int fromIndex) { // 确定指定的代码点是否是一个有效的Unicode代码点值。 if (Character.isValidCodePoint(ch)) { final char[] value = this.value; final char hi = Character.highSurrogate(ch); final char lo = Character.lowSurrogate(ch); final int max = value.length - 1; for (int i = fromIndex; i < max; i++) { if (value[i] == hi && value[i + 1] == lo) { return i; } } } return -1; }
判断包含系列基本都是使用indexOf方法
lastIndexOf系列方法主要是使用这个静态方法
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) { /* * Check arguments; return immediately where possible. For * consistency, don't check for null str. */ int rightIndex = sourceCount - targetCount; if (fromIndex < 0) { return -1; } if (fromIndex > rightIndex) { fromIndex = rightIndex; } /* Empty string always matches. */ if (targetCount == 0) { return fromIndex; } int strLastIndex = targetOffset + targetCount - 1; char strLastChar = target[strLastIndex]; int min = sourceOffset + targetCount - 1; int i = min + fromIndex; startSearchForLastChar: while (true) { while (i >= min && source[i] != strLastChar) { i--; } if (i < min) { return -1; } int j = i - 1; int start = j - (targetCount - 1); int k = strLastIndex - 1; while (j > start) { if (source[j--] != target[k--]) { i--; continue startSearchForLastChar; } } return start - sourceOffset + 1; } }
这个通过指定索引开始截取,可以看出两个字符串下标越界校验处理,最后返回当前字符串或重新新创建一个索引开始到结束的字符串,new String(value,beginIndex,subLen) 实现如下
通过数组copy方法
数组Arrays
最后使用的系统数组copy,返回了一个新的数组,没有更改原始数据
指定开始结束,只是业务处理中多了一步校验
替换,最后也是返回了一个新的字符串
全替换,和指定替换有点不一样,这个是把当前字符作为正则进行处
// 分割源码 public String[] split(String regex, int limit) { /* fastpath if the regex is a (1)one-char String and this character is not one of the RegEx's meta characters ".$|()[{^?*+\\", or (2)two-char String and the first char is the backslash and the second is not the ascii digit or ascii letter. */ // 正则校验 char ch = 0; 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<>(); while ((next = indexOf(ch, off)) != -1) { if (!limited || list.size() < limit - 1) { list.add(substring(off, next)); off = next + 1; } else { // last one //assert (list.size() == limit - 1); 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); }
整体还是通过循环、截取、拼接最后组合成一个数组然后返回
valueOf方法最终还是,创建了一个新的数组,所以只要通过valueOf方法处理的String 都是会在堆内存中创建一个对象的,和 new String(); 同理,如果只是引用 String str = “123”,则只是在栈内存中,
简单说明堆栈:
GC方式:栈自动释放,堆需要GC
空间:栈比堆小
分配方式:栈支持静态和动态分配,堆仅支持动态分配
效率:栈的效率比堆高
学习记录第一天,加油!!!