Java源码分析五(String)
Java源码分析四(String)
*继承的接口和实现类
序列化
Serializable
//可以进行比较的接口 compareTo(T o);
Comparable
//字符串的父类
CharSequence
类中的属性
//String 底层技术存储的char数组
private final char value[];
//hash值
private int hash; // Default to 0
//序列号的ID号
private static final long serialVersionUID = -6849794470754667710L;
//String是序列化流协议中的特例必须将String实例写入ObjectOutputStream中
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
构造器
空参构造器构造一个为空的String 不是null
public String() {
this.value = "".value;
}
//参数传入一个字符串 相当于 value和hash相同但是由于是在堆中创建所以是一个全新的地址
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
//传入一个char 数组 通过Arrays这个工具类将实参value字符串数组copy到形参this.value
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
//传入一个char数组和起始值offset 和需要截取的长度新建字符串
public String(char value[], int offset, int count) {
//起始值为0直接报异常
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
//截取长度小于等于0时
if (count <= 0) {
//如果count小于0直接报异常
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
如果count==0且offset小于value.length
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
//如果offset大于length-截取量 就报异常
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
//重点注意这里你输入的int值会被转译成ASCII表中的数据 如果你想String 加0 就需要对应ASCII码表中0的ASCII码 48
public String(int[] codePoints, int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= codePoints.length) {
this.value = "".value;
return;
}
}
// 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;
}
//此方法被注释@Deprecated证明不太常用了 跟hibyte的字面意思是UTF-16字符编码的最8高位
//因为是byte[]型在java中是8位的,转换成String就转换成了16位,高8位要填,就是这个hibyte了。(不用管这个了 不用了)
@Deprecated
public String(byte ascii[], int hibyte, int offset, int count) {
checkBounds(ascii, offset, count);
char value[] = new char[count];
if (hibyte == 0) {
for (int i = count; i-- > 0;) {
value[i] = (char)(ascii[i + offset] & 0xff);
}
} else {
hibyte <<= 8;
for (int i = count; i-- > 0;) {
value[i] = (char)(hibyte | (ascii[i + offset] & 0xff));
}
}
this.value = value;
}
//同时不用了
@Deprecated
public String(byte ascii[], int hibyte) {
this(ascii, hibyte, 0, ascii.length);
}
//Default的权限我们调用不到
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
this(bytes, 0, bytes.length, charsetName);
}
//根据你传入的字节数组和编码格式给你解析
public 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[], Charset charset) {
this(bytes, 0, bytes.length, charset);
}
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);
}
//将一个线程安全的StringBuffer 传给String
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
//将一个长度可变的StringBuilder传给String
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
方法解析
//返回第index位置的char元素 如123 charAt(1)=2
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
// 用于对字节数组进行边界检查并请求由 String(byte[],..) 构造函数使用的偏移量和长度值的通用私有实用程序方法。
private static void checkBounds(byte[] bytes, int offset, int length) {
if (length < 0)
throw new StringIndexOutOfBoundsException(length);
if (offset < 0)
throw new StringIndexOutOfBoundsException(offset);
if (offset > bytes.length - length)
throw new StringIndexOutOfBoundsException(offset + length);
}
//返回第index位置元素的ASCII码的值 如 s=“abc” s(1)=98 返回b的ASCII
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
//返回前一个元素的ASCII
public int codePointBefore(int index) {
int i = index - 1;
if ((i < 0) || (i >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointBeforeImpl(value, index, 0);
}
//返回从beginIndex 到endIndex 范围内的搜索到的Unicode代码的个数
public int codePointCount(int beginIndex, int endIndex) {
if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
throw new IndexOutOfBoundsException();
}
return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
}
//重点方法 比较俩字符串 不是先比较长度 而是比较第一位的长度 比如zzz 和1111比较那么会返回73 因为z的ASCII 122 1的ASCII49 为122-49=73
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;
}
//忽略大小写比较两个字符串 如果等于返回0 小于返回负数 大于返回正数
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
//拼接两个字符串 这里调用的构造器是他内部使用的 char boolean 这个构造器
// 注意这个构造器没有改hashCode concat两个连接的数据他的hashcode没有改变
//故 需要超级强调没有改hashCode!!!!!到时候存HashMap的时候hashCode一样后还得调用equlas。
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);
}
//是否包含某个字符串 等下我们就点到最深处看看先介绍一下
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
//继续点
public int indexOf(String str) {
return indexOf(str, 0);
}
//下面还得点 这里稍微解释一下 传入原字符串 首位置和尾位置 需要比较的字符串和首位置 尾位置 以及从哪里比较formIndex
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) {
//如果开始搜索的索引位置大于父数组的长度即使搜索位置大于了如果target是空也是包含的 即使source也是空 空也是包含空的
if (fromIndex >= sourceCount) {
//判断target是否为空 如果为空就返回原数组的长度
return (targetCount == 0 ? sourceCount : -1);
}
//搜索的索引位置小于0 就将其改为0
if (fromIndex < 0) {
fromIndex = 0;
}
//需要比较的数组为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 */
//如果此时i<max 进行比较 从需要比较的字符串
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;
}
//比较 本字符串与StringBuffer 是否一样
public boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence)sb);
}
//传入String的父类 CharSequence 然后通过过滤 过滤出StringBuffer String StringBuilder后看看是否完全一样
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}
//将cahr数组的数据复制 在堆空间中创建一个新的字符串
public static String copyValueOf(char data[]) {
return new String(data);
}
//将char数组按照从offset开始复制count个字符 传入一个新对象
public static String copyValueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
//判断字符串是否以suffix 结尾 下面说starstWith
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
//判断当前字符串是否从第toffset位置开始是以startswith开始的
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或传入的值大于本字符串长度-需要比较的长度 比如原字符串8位 需要比较的4位
//你从第5位开始比 他就3位了比个锤子返回就完事了
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
//进行循环比较了 如果有一个不对头 直接返回fasle就可以
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return 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;
}
//不区分大小写的判断俩字符串是否一样
public boolean equalsIgnoreCase(String anotherString) {
//解读一下含义
// 情况1: 你俩完全就是一个对象(地址值一样) 不比较直接返回true
//情况2: 比较的字符串为空直接返回false没什么好比的
//情况3: 俩字符串的长度不一样直接返回false
//情况四 通过regionMatches 判断后返回 true 整体返回true 否则返回false
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
//解读一下各个参数的意思 逻辑跟indexOf(char[] source, int sourceOffset, int sourceCount,
//char[] target, int targetOffset, int targetCount, int fromIndex)差不多
// ignoreCase 是否区分大小写 toffset 开始位置 other 需要进行比较的字符串
//ooffset从需要比较的字符串哪一位开始 len比较多少位刚才那个是比较到value.length
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int , int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
//这个跟下面的重载方法类似 说下面参数多的
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
//对数据进行格式化输出 格式太多了 不说了 https://blog.csdn.net/lonely_fireworks/article/details/7962171这个大佬写的很好
public static String format(Locale l, String format, Object... args) {
return new Formatter(l).format(format, args).toString();
}
//将字符串转换为byte格式
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
//根据你传入的字符编码 转换成对应的byte数组
public byte[] getBytes(Charset charset) {
if (charset == null) throw new NullPointerException();
return StringCoding.encode(charset, value, 0, value.length);
}
//通过传入的字符编码名称 然后去比较后转换成对应的byte格式 如果传入的String为空 默认转换为ISO-8859-1
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
//当前对象传入一个dst数组 从dst数组的dstBegin位置开始 复制当前value数组插入进去 是默认权限我们无法访问
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
//我们可以使用这个原理一样 就是 有了复制value的条件现在 复制value 从srcbegin到srcend位置上的数据 原理同上
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
//hashCode 着重说 我们新建对象的时候如果没有hashcode就会给你计算 但是有的构造器会直接给hashCode赋值
//调用此方法如果没有hashCode值就给你创建 如果有就不变了 上面我们说的contains后hashCode不变因为有hashCode值
//我觉得不太好 soStupid;
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;
}
//下面是indexOf全家桶上面讲过类似不说了
//这个方法就很厉害了 看不到任何代码 是通过底层c++转译成的dll文件起作用的 而且最骚的是6.0和6.0之后存储不一样
//JDK6:当调用intern 方法时,如果字符串常量池先前已创建出该字符串对象,
//则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串对象的引用。
//JDK6+:当调用intern 方法时,如果字符串常量池先前已创
//建出该字符串对象,则返回池中的该字符串的引用。否则,
//如果该字符串对象已经存在于Java堆中,则将堆中对此对象的引用添加到字符串常量池中,
//并且返回该引用:如果堆中不存在,则在池中创建该字符串并返回其引用。
public native String intern();
//判断字符串是否空soStupid
public boolean isEmpty() {
return value.length == 0;
}
//以下两个方法是jdk8.0之后的新方法 作用相当于拼接
//List names=new ArrayList<String>();
names.add("1");
names.add("2");
names.add("3");
System.out.println(String.join("-", names));
//将会输出1-2-3
public static String join(CharSequence delimiter, CharSequence... elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
// Number of elements not likely worth Arrays.stream overhead.
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
public static String join(CharSequence delimiter,
Iterable<? extends CharSequence> elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
//lastIndexOf类型一 不传入String 只比较某个char 出现的最后一个位置
//返回你输入的int值对应的ASCII最后出现的位置 如String str=zzz123 str.lastIndexof(122)=2
public int lastIndexOf(int ch) {
return lastIndexOf(ch, value.length - 1);
}
//上面调用这个方法
public int lastIndexOf(int ch, int fromIndex) {
//如果输入的ch小于ASCII码表的最大值
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;
//判断一下你想看从第几位开始看最后出现的位置 比如字符串长度为10 你想从0到第5位某字符最后出现的位置这里就传5
int i = Math.min(fromIndex, value.length - 1);
//往后遍历
for (; i >= 0; i--) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else
//你输入的int ch 大于了ASCII的最大值 执行下面的方法
{
return lastIndexOfSupplementary(ch, fromIndex);
}
}
//使用补充字符处理 lastIndexOf 的(罕见)调用。
private int lastIndexOfSupplementary(int ch, int fromIndex) {
//确定指定的代码点是否是一个有效的Unicode代码点值。
if (Character.isValidCodePoint(ch)) {
final char[] value = this.value;
char hi = Character.highSurrogate(ch);
char lo = Character.lowSurrogate(ch);
int i = Math.min(fromIndex, value.length - 2);
for (; i >= 0; i--) {
if (value[i] == hi && value[i + 1] == lo) {
return i;
}
}
}
return -1;
}
//lastIndexOf 类型二 某个字符串在当前字符串中出现的位置
public int lastIndexOf(String str) {
//默认传入的str在当前字符串从0到 value.length上比较
return lastIndexOf(str, value.length);
}
public int lastIndexOf(String str, int fromIndex) {
return lastIndexOf(value, 0, value.length,
str.value, 0, str.value.length, fromIndex);
}
//具体计算某个字符串最后出现的位置
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;
}
}
//返回字符串的长度
public int length() {
return value.length;
}
//使用正则表达式 判断当前字符串是不是某种格式 举例如下
// String format = "\\d{4}-\\d{2}-\\d{2}";
//String formatOther = "\\d{4}-\\d{2}-\\d{2}.*";
//String date = "2011-03-09 11:30:30";
//date.matches(format); //fase
//date.matches(formatOther); //true
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
//非线程安全的情况下看本字符串是否包含sb
private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
char v1[] = value;
char v2[] = sb.getValue();
int n = v1.length;
if (n != sb.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
return true;
}
//方法返回这个字符串,是从给定的索引codePointOffset码点偏移的索引。
//index -- 这是索引被抵消。
//codePointOffset -- 这是偏移量的代码点。
public int offsetByCodePoints(int index, int codePointOffset) {
if (index < 0 || index > value.length) {
throw new IndexOutOfBoundsException();
}
return Character.offsetByCodePointsImpl(value, 0, value.length,
index, codePointOffset);
}
//用于判断当前字符串和传入的字符串在某个区间本字符串从toffset开始传入字符串从ooffset开始到len长度上是否相同
// ignoreCase 是否区分大小写
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
//判断下 开始条件是否小于0或者起始值比字符串长度-比较长度大 如 字符串长度 8 需要比较4个 你从第5个开始比
//直接比不了 return false
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
//进行数据判断很简单 无非就是 区不区分大小写
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
//不区分大小写 效率成倍提升少了if判断
public boolean regionMatches(int toffset, String other, int ooffset,
int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
//将传入的oldChar 全部转换成 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;
}
//将一串字符转换 到时候所Pattern 类的时候说这里没时间说了
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
//看名字总所周知只改重复的第一个字符串
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}
//分割字符串 如 str=1,2,3;String [] after=str.split(",") 将after逐一遍历就会得到 1 2 3
public String[] split(String regex) {
return split(regex, 0);
}
// regex是正则,我们都清楚,需要注意如果是要按照"|" "."之类的特殊符号分割,需要加上转义,比如"\\|",不然切割的会不准确
//limit 是要输入一个数值,这个数值n如果 >0 则会执行切割 n-1次,也就是说执行的次数不会超过输入的数值次.数组长度不会大于切割次数
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);
}
//是不是以某个字符串开始的
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
//判断某个字符串是否从toffset开始的 跟我们说的indexof 那种计算方法类似不说了这个比那个简答
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;
}
//从begin开始 返回endIndex个数据 比如 str=123456 str.subSequence(0,2)就是返回12
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
//返回从begin 到结束 即value.length-beginIndex个字符的字符串
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);
}
//从beginIndex 开始 数返回endIndex个数据 我觉得这里不叫endIndex好 写成count更好理解
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);
}
//从0开始数value.length个的话就直接然后 当前对象否则根据大小创建一个新对象
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
//返回一个复制value值的char 数组 如果想返回当前对象的话手贱改了数据String就变了这个result随便玩
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
//将字符串都变成小写
public String toLowerCase() {
return toLowerCase(Locale.getDefault());
}
//语言环境用作参数。这使您可以使用给定的区域设置(例如:土耳其语,立陶宛语等)规则将字符串中的字符转换为小写。
//不太常用
public String toLowerCase(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
int firstUpper;
final int len = value.length;
/* Now check if there are any characters that need to be changed. */
scan: {
for (firstUpper = 0 ; firstUpper < len; ) {
char c = value[firstUpper];
if ((c >= Character.MIN_HIGH_SURROGATE)
&& (c <= Character.MAX_HIGH_SURROGATE)) {
int supplChar = codePointAt(firstUpper);
if (supplChar != Character.toLowerCase(supplChar)) {
break scan;
}
firstUpper += Character.charCount(supplChar);
} else {
if (c != Character.toLowerCase(c)) {
break scan;
}
firstUpper++;
}
}
return this;
}
char[] result = new char[len];
int resultOffset = 0; /* result may grow, so i+resultOffset
* is the write location in result */
/* Just copy the first few lowerCase characters. */
System.arraycopy(value, 0, result, 0, firstUpper);
String lang = locale.getLanguage();
boolean localeDependent =
(lang == "tr" || lang == "az" || lang == "lt");
char[] lowerCharArray;
int lowerChar;
int srcChar;
int srcCount;
for (int i = firstUpper; i < len; i += srcCount) {
srcChar = (int)value[i];
if ((char)srcChar >= Character.MIN_HIGH_SURROGATE
&& (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
srcChar = codePointAt(i);
srcCount = Character.charCount(srcChar);
} else {
srcCount = 1;
}
if (localeDependent ||
srcChar == '\u03A3' || // GREEK CAPITAL LETTER SIGMA
srcChar == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE
lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
} else {
lowerChar = Character.toLowerCase(srcChar);
}
if ((lowerChar == Character.ERROR)
|| (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
if (lowerChar == Character.ERROR) {
lowerCharArray =
ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
} else if (srcCount == 2) {
resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount;
continue;
} else {
lowerCharArray = Character.toChars(lowerChar);
}
/* Grow result if needed */
int mapLen = lowerCharArray.length;
if (mapLen > srcCount) {
char[] result2 = new char[result.length + mapLen - srcCount];
System.arraycopy(result, 0, result2, 0, i + resultOffset);
result = result2;
}
for (int x = 0; x < mapLen; ++x) {
result[i + resultOffset + x] = lowerCharArray[x];
}
resultOffset += (mapLen - srcCount);
} else {
result[i + resultOffset] = (char)lowerChar;
}
}
return new String(result, 0, len + resultOffset);
}
//toString
public String toString() {
return this;
}
//将Strng都变成大写
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
//自己传入一个Local配置 和toLowerCase一样不说了
public String toUpperCase(Locale locale) {
}
//返回一个新的字符串 前后去掉空格
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;
}
//返回缓存区的一个true
public static String valueOf(boolean b) {
return b ? "true" : "false";
}
//传入一个字符c 将其创建一个新的String 在堆中
public static String valueOf(char c) {
char data[] = {c};
return new String(data, true);
}
//传入一个char 数组 创建一个对象
public static String valueOf(char data[]) {
return new String(data);
}
//跟构造器生成差不多 返回一个新的String
public static String valueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
//下面的几个方法类似都是将数字类型转换为String
public static String valueOf(double d) {
return Double.toString(d);
}
public static String valueOf(float f) {
return Float.toString(f);
}
public static String valueOf(int i) {
return Integer.toString(i);
}
public static String valueOf(long l) {
return Long.toString(l);
}
//着重说一下这个 传入Object后 obj.toStrng 是反射出的类名+@+hashCode值
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
String常见的面试题和String的创建
1. String基础的几种创建方式(重载的构造器就不说了说一个)
String str1=new String("123") 在堆中创建一个数据 如果多执行几次发现这些地址值不一样
String str2="123" 在常量池中创建一个数据 如果常量池中有 直接取常量池中的地址 多次创建地址一样
String str3 =str.inter(); 将str的字符串在常量池中创建一份(前提 常量池中没有)和str=“123”类似 多次创建地址一样
String str4= String.valueOf("123") 这个玩意就特殊了如果你传入String 他调用的Object.toString 而String类的String重写了就比较绕 多次创建时地址一样
2. 通过concat 连接两个字符串
为什么讲这个呢 这个是将两个字符串拼接起来返回一个新的字符串
//与我们下面说的使用运算符+生成String产生鲜明对比 所以我们平时用concat 占用空间小只用新建一个对象
3. 难点通过算数运算符+将字符串连接
①两个引号形式(常量池中的数据相加)
String s1 = new String("123").intern();
String s2 = "1" + "23";
s1==s2 // true 证明 String s2创建的对象是直接拼接后去常量池找有木有一样的有直接取他的地址
②两个new String()的字符串相加(注意看 很难当时理解了好久有因为没有JVM基础,妈的基本上就是照着别人的抄的才能理解)
首先会创建这两个对象(堆中)以及相加后的对象(堆中)
然后判断常量池中是否存在这两个对象的字面量常量
如果存在
不做任何操作
如果不存在
则在常量池上创建对应常量
代码配合注释演示
第一种情况:
String s1 = new String("1")+new String("23");
/*
* 首先堆中会有 1 ,23 ,以及相加之后的123 这三个对象。如果 1,23 这两个对象在常量池中没有相等的字面量
* 那么还会在常量池中创建2个对象 最大创建了5个对象。最小创建了3个对象都在堆中。
*/
//意思是 +号就去常量池里面找 然后根据你创建对象的格式看是放常量池还是堆
s1.intern();
String s2 = "123";
System.out.println( s1 == s2);// true
第二种情况: 将s1.intern()顺序和String s2="123"
String s2 = "123";
s1.intern();
System.out.println( s1 == s2);// false
等下讲itern()方法时候说太麻烦了
③双引号字符串常量与new String字符串相加
首先创建两个对象,一个是new String的对象(堆中),一个是相加后的对象(堆中)
然后判断双引号字符串字面量和new String的字面量在常量池是否存在
如果存在
不做操作
如果不存在
则在常量池上创建对象的常量
//创建的对象在堆中
String s1 = "1"+new String("23");
/*
*首先堆中会有 23 ,以及相加之后的123 这2个对象。如果23,1 这两个对象在常量池中没有相等的字面量
*那么还会在常量池中创建2个对象最大创建了4个对象(2个堆中,2个在常量池中)。最小创建了2个对象都堆中。
*/
String s2 = "123";
System.out.println( s1.intern() == s2);// true
④双引号字符串常量与一个字符串变量相加
首先创建一个对象,是相加后的结果对象(存放堆中,不会找常量池)
然后判断双引号字符串字面量在常量池是否存在
如果存在
不做操作
如果不存在
则在常量池上创建对象的常量
String s1 = "23";
/*
* 这里执行时,常量“1” 会首先到字符串常量池里面去找,如果没有就创建一个,并且加入字符串常量池。
* 得到的123结果对象,不会存入到常量池。这里特别注意和两个常量字符串相加不同 “1”+“23” 参考上面第三点
* 由于不会进入常量池,所以s2 和 s3 常量池地址值不同,所以输出为false
*/
String s2 = "1"+s1;
String s3 = "123";
System.out.println( s2 == s3.intern());
超级大重点!!!intern()
JDK6:当调用intern 方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,
//将此字符串对象添加到字符串常量池中,并且返回该字符串对象的引用。
JDK6+:当调用intern 方法时,如果字符串常量池先前已创
建出该字符串对象,则返回池中的该字符串的引用。否则,
如果该字符串对象已经存在于Java堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用:如果堆中不存在,
//则在池中创建该字符串并返回其引用。
下面我们来解释一下刚才遇到的2个new对象样式相加的问题
第一种情况:
public static void main(String[] args){
String s1 = new String("1")+new String("23");
//这里将s1的地址值存到常量池中 (我以前不会是因为看的JDK6.0之前的内容)
s1.intern();
//这不就很简单了吗 去常量池中找 找到了 神奇的发生了 这里常量池的数据就是s1肯定相等啊
String s2 = "123";
System.out.println( s1 == s2);
第二种情况:
public static void main(String[] args){
String s1 = new String("1")+new String("23");
//在常量池中创建引用 地址是s2的
String s2 = "123";
//我先去常量池中找找有没有123 发现找到了 是s2的地址 我就返回
s1.intern();
//所以 s1 s2没有半毛钱关系 肯定不相等啊 soStupid
System.out.println( s1 == s2);
}
}