如果想短时间内看完String源码的哈,打扰了,这个不合适您。这个只是小子我的个人在看源码过程中所遇到问题的记录。
1 通过以下文章来增强对String的认识:
1.1 官方文档
1.3 String源码解析
1.4 String的CASE_INSENSITIVE_ORDER字段中提及到的serialVersionUID的理解:
1.5 关于String.CASE_INSENSITIVE_ORDER的作用
1.6 这篇文章主要是针对String(byte bytes[], int offset, int length, Charset charset)构造方法所提及到的StringCoding.decode()方法进行解释的,不过该篇文章讲的是对static的解释,看的蒙,但对于static关键字的理解还是说的挺赞的:
深入学习java源码之StringCoding.decode()与StringCoding.encode()
1.7 Java中字符串长度方法length()和codePointCount()区别
1.8 深入学习Java中的字符串,代码点和代码单元,也可以参照《Java核心技术卷一基础知识第10版.pdf》的第三章的3.3.4和3.6.6这来加深对代码点和代码单元的认识。
码点是一个代码值,用于与编码表中的字符一一对应。
正规的表述是:码点( code point) 是指与一个编码表中的某个字符对应的代码值。
关于编码表的认识:UniCode编码表
1.9 关于 public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) 方法的理解
public static void main(String[] args) {
// 该功能主要实现的是 获取 str 字符串中的srgBegin开始,srcEnd位置结束的子字符串(srcEnd - srcBegin = 子字符串A),
// 在将字符数组 dest 中从 dstBegin位置开始进行替换为子字符串A,替换的个数为字符串的长度(srcEnd-srcBegin)。
String str = "12345";
char[] dest = new char[]{'a','b','c','d','e','f'};
str.getChars(1,2,dest,2);
System.out.println(dest);
}
1.10 在indexOf(int ch)方法中有提及到类型提升的问题:参考文章:java 基本数据类型及自动类型提升
1.11 关于while循环中的一些细节问题:
while(--len >=0){}// 先自减,后循环
// 等同于
while (len-- > 0) {}// 先循环,后自减
1.12 关于
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex)
方法的示例:
public static void main(String[] args) {
String source = "123456";
String target = "345";
System.out.println(source.indexOf(target,0));// 从0的位置开始检索source字符串,查找target子字符串是否出现在source中,如果出现,则返回出现的子字符串的下标,如果不存在,则直接返回-1。
System.out.println(source.substring(source.indexOf(target,1),source.indexOf(target,1)+target.length()));
}
该方法主要是获取target字符串在source中出现的索引下标(或者是说target为source的子字符串,看看子字符串在source字符串中出现的索引位置,如果target不是source的子字符串,则直接返回-1)。
关于splict()方法中的若干源码的个人问题和理解
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))
".$|()[{^?*+\\":这个字符串表示的是:.$|()[}^?*+\ 这些字符,所以
".$|()[{^?*+\\".indexOf(ch) == -1 :判断是regex字符是否该字符串(.$|()[}^?*+\)中。
(ch = regex.chartAt(1))-‘0’)|('9' - ch) 简化的是:(ch-'0')|('9'-ch)。这里涉及到运算符优先级、及位运算符的关系和类型提升的问题:在理解以上文章后,就可以明白
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0
表示的意思是:如果小于0,表明ch 为 非数字字符,如果大于0,表明ch 在 0-9之间的一个数值字符。
((ch-'a')|('z'-ch)) < 0 && ((ch-'A')|('Z'-ch)) < 0))
意思是:是否为a-Z的字符。如果 < 0 表示字符不在a-Z之间,如果大于0,表示字符在a-Z之间。
(ch < Character.MIN_HIGH_SURROGATE || ch > Character.MAX_LOW_SURROGATE)
这两句的理解是:第二个字符不在0-9,a-z,A-Z中,且不在Unicode编码的\uD800-\uDBFF之间。\uD800-\uDBFF区间表示的是UTF-16中的低代理项,具体代表字符可以去编码表自行查看。
public static final char MIN_LOW_SURROGATE = '\uDC00';
public static final char MAX_LOW_SURROGATE = '\uDFFF';
关于低代理项的认识:该文章的 1.8 点有进行介绍。
public static void main(String[] args) {
System.out.println(String.format("zhunode %s","hello "));
System.out.printf("zhunode %s \n","hello ");// format方法和String.format()等价
System.out.println(String.format("zhunode %d",2342));
System.out.printf("zhunode %d",2342);
}
1.16 关于intern()方法的认识:
public static void main(String[] args) {
String heapArea = new String("hello world");
String methodArea = heapArea.intern(); // 将heapArea对象复制(还是移动)到方法区呢?
System.out.println(heapArea.equals(methodArea));
// 验证methodArea字符串是在方法区的常量池中
String toShow = "hello world";
System.out.println(methodArea == toShow); // methodArea和toShow相等,表明调用intern()方法是将字符串存了一份在方法区的常量池中(还未区分是复制还是剪切)
System.out.println(heapArea == toShow); // 通过比较两个地址值(可以看出是复制一份到方法区的常量池中)
/* 执行结果如下:
true
true
false
*/
}
1.17 自己记述的源码,很不完整(因为不懂)。
package java.lang;
import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* The {@code String} class represents character strings. All
* string literals in Java programs, such as {@code "abc"}, are
* implemented as instances of this class.
* <p>
* Strings are constant; their values cannot be changed after they
* are created. String buffers support mutable strings.
* Because String objects are immutable they can be shared. For example:
* <blockquote><pre>
* String str = "abc";
* </pre></blockquote><p>
* is equivalent to:
* <blockquote><pre>
* char data[] = {'a', 'b', 'c'};
* String str = new String(data);
* </pre></blockquote><p>
* Here are some more examples of how strings can be used:
* <blockquote><pre>
* System.out.println("abc");
* String cde = "cde";
* System.out.println("abc" + cde);
* String c = "abc".substring(2,3);
* String d = cde.substring(1, 2);
* </pre></blockquote>
* <p>
* The class {@code String} includes methods for examining
* individual characters of the sequence, for comparing strings, for
* searching strings, for extracting substrings, and for creating a
* copy of a string with all characters translated to uppercase or to
* lowercase. Case mapping is based on the Unicode Standard version
* specified by the {@link java.lang.Character Character} class.
* <p>
* The Java language provides special support for the string
* concatenation operator ( + ), and for conversion of
* other objects to strings. String concatenation is implemented
* through the {@code StringBuilder}(or {@code StringBuffer})
* class and its {@code append} method.
* String conversions are implemented through the method
* {@code toString}, defined by {@code Object} and
* inherited by all classes in Java. For additional information on
* string concatenation and conversion, see Gosling, Joy, and Steele,
* <i>The Java Language Specification</i>.
*
* <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
* or method in this class will cause a {@link NullPointerException} to be
* thrown.
*
* <p>A {@code String} represents a string in the UTF-16 format
* in which <em>supplementary characters</em> are represented by <em>surrogate
* pairs</em> (see the section <a href="Character.html#unicode">Unicode
* Character Representations</a> in the {@code Character} class for
* more information).
* Index values refer to {@code char} code units, so a supplementary
* character uses two positions in a {@code String}.
* <p>The {@code String} class provides methods for dealing with
* Unicode code points (i.e., characters), in addition to those for
* dealing with Unicode code units (i.e., {@code char} values).
*
* @author Lee Boynton
* @author Arthur van Hoff
* @author Martin Buchholz
* @author Ulf Zibis
* @see java.lang.Object#toString()
* @see java.lang.StringBuffer
* @see java.lang.StringBuilder
* @see java.nio.charset.Charset
* @since JDK1.0
*/
/*
String类表示字符串。 Java程序中的所有字符串文字(例如“abc”)都实现为此类的实例。
*/
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// 该值用于字符串存储
private final char value[];
// 保存字符串的hash code。默认为0,因为字符串默认长度为0,所以hash值计算后的结果也为0。
private int hash; // Default to 0
// serialVersionUID 序列话的版本ID,这个值是来源于JDK 1.0.2,是为了与JVM中的serialUID相呼应
private static final long serialVersionUID = -6849794470754667710L;
/**
* 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];
// 初始化字符串对象,表示的是一个空字符序列
// 注意,该构造方法可以不使用,因为字符串事不可变对象。
public String() {
this.value = "".value;
}
//初始化新创建的字符串对象,使其表示与参数相同的序列;换句话说,新创建的字符串是参数字符串的副本。否则不必使用
// 此构造函数,因为字符串是不可辨的。
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
// 分配一个新字符串,以便它表示包含当前字符数组参数中的字符序列。该字符序列的内容将被复制,
// 随后对字符数组的修改不会影响新创建的字符串。
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
//offset表示第一个被截取的字符在数组value[]中的下标,count表示从此字符开始向后截取的字符的数量
//将value[]数组按照传入的下标和指定的截取数组数据的数量进行截取,并且创建一个内容为此的string对象
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
// 如果获取的长度小于等于0
if (count <= 0) {
// 如果获取的长度小于0,直接抛出异常
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// 如果长度为0且offset小于字符数组的长度,那么就直接将该字符序列复制给新建的字符串对象
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// 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(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注解:若某类或某方法加上该注解之后,表示此方法或类不再建议使用,调用时也会出现删除线,但并不代表不能用,
只是说,不推荐使用,因为还有更好的方法可以调用。
*/
@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);
}
// 检查是否越界
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);
}
/**
通过使用指定的字符集解码指定的字节子数组构造一个新的String。新String的长度是charset的函数,因此可能不等于子数组的长度。
*/
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[], 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[], String charsetName)
throws UnsupportedEncodingException {
this(bytes, 0, bytes.length, charsetName);
}
public String(byte bytes[], Charset charset) {
this(bytes, 0, bytes.length, charset);
}
public String(byte bytes[], int offset, int length) {
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(bytes, offset, length);
}
public String(byte bytes[]) {
this(bytes, 0, bytes.length);
}
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(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
// 返回字符序列的长度,
public int length() {
// 好吧,看不到value是怎么实现字符长度获取的
return value.length;
}
public boolean isEmpty() {
return value.length == 0;
}
// 返回value中index位置的字符,仍说不符规范则抛出异常。
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
/**
// 返回指定索引处的字符(Unicode码位)。索引引用字符值(Unicode代码单位),范围从0到length()-1。
*/
/*
如果在给定索引处指定的char值在高代理范围内,
则以下索引小于此字符串的长度,并且以下索引处的char值在低代理范围内,
则返回与此代理对对应的补充代码点。否则,返回给定索引处的char值。
*/
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
// 该静态方法后面再看看
return Character.codePointAtImpl(value, index, value.length);
}
// 返回指定索引之前的字符(Unicode码位)。索引引用了字符值(Unicode代码单位),其范围从1到长度。
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);
}
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);
}
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);
}
/**
* Copy characters from this string into dst starting at dstBegin.
将此字符串中的字符从dstbegin开始复制到dst。
* This method doesn't perform any range checking.
// 此方法不执行任何范围检查
*/
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
// 关于该方法的理解可以看上面csdn的示例 1.9
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);
}
// 该方法与 上面的 getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) 方法相类似
@Deprecated
public void getBytes(int srcBegin, int srcEnd, byte 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);
}
Objects.requireNonNull(dst);
int j = dstBegin;
int n = srcEnd;
int i = srcBegin;
char[] val = value; /* avoid getfield opcode */
while (i < n) {
dst[j++] = (byte)val[i++];
}
}
/**
如果给定的字符集编码不能够对字符串进行编码,那么该方法执行的结果将是不确定的,
当需要更多地控制编码过程时,应该使用CharsetEncoder类。
*/
// 使用charsetName字符集编码来对String进行编码成字节序列,并将结果保存到一个新的字节数组中。
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
/**
使用给定的字符集将此String编码为字节序列,将结果存储到新的字节数组中。
*/
public byte[] getBytes(Charset charset) {
if (charset == null) throw new NullPointerException();
return StringCoding.encode(charset, value, 0, value.length);
}
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
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循环是装逼神器?还是效率上有提高?
while (n-- != 0) {
// 比较每个字符是否相等
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
public boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence)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;
}
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;
}
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
/**
// 从词典的角度比较两个字符串
// 基于字符串中的每个字符的Unicode值来进行比较。
根据字典上的字符序列表的值(我觉得是unicode码表的数值),字符串对象所表示的字符序列与参数字符串所表示的字符序列来进行比较。
如果此字符串对象在词法上位于参数字符串之前,则结果为负整数。
如果此字符串对象在词法上跟随参数字符串,则结果为正整数。
如果字符串相等,则结果为零;当equals(object)方法返回true时,CompareTo返回0。
*/
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;
}
/**
比较器通过compareToIgnoreCase对String对象进行排序的比较器。
该比较器是可以序列化的
*/
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
// serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。
// 在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,
// 如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
// 为什么需要转换两次来进行比较?有哪些特殊字符吗?
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
/**
忽略大小写进行字符比较
*/
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
/**
// 测试两个字符串区域是否相等
将此对象的字符串与参数字符串的子对象进行比较
如果这些子字符串表示相同的字符序列,那么结果为true。
从this字符串的index位置开始比较,比较的长度为length。
// 如果且仅当以下至少一项为真,则结果为假:
*/
// 比较两个字符串中的部分代码是否相等(区分大小写)
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)
while (len-- > 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
// 比较两个字符串中的部分代码是否相等(可通过ignoreCase来设置是否需要区分大小写,true不区分大小写,false区分)
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.
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 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;
}
// pc先自减,如果符合规则(>=0),则进入循环
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
// enasWith里面调用的是startsWith方法,不过是
// alue.length - suffix.value.length 获取到判断字符串的起始位置
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
// 可以参看csdn中的文章,有介绍。
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;
}
/**
返回该字符串中执行字符第一次出现的index。
如果值为ch的字符出现在此字符串对象表示的字符序列中,则返回第一次出现的索引(以Unicode代码为单位)。
*/
public int indexOf(int ch) {
return indexOf(ch, 0);
}
// 反正从fromIndex开始位置的ch字符所在的索引
// 如果ch没有,则返回-1,有则返回相应的下标index。
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);
}
}
private int indexOfSupplementary(int ch, int fromIndex) {
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;
}
public int lastIndexOf(int ch) {
return lastIndexOf(ch, value.length - 1);
}
public int lastIndexOf(int ch, int fromIndex) {
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;
int i = Math.min(fromIndex, value.length - 1);
for (; i >= 0; i--) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return lastIndexOfSupplementary(ch, fromIndex);
}
}
/**
* Handles (rare) calls of lastIndexOf with a supplementary character.
处理具有补充字符的LastIndexOf调用(很少用到)。
*/
private int lastIndexOfSupplementary(int ch, int fromIndex) {
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;
}
/**
*
// 返回当前字符串中的str字符串所在索引
*/
public int indexOf(String str) {
return indexOf(str, 0);
}
/**
// 返回指定子字符串第一次出现的字符串中的索引,从指定的索引开始。
*/
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,
String target, int fromIndex) {
return indexOf(source, sourceOffset, sourceCount,
target.value, 0, target.value.length,
fromIndex);
}
/**
// 由字符串和StringBuffer共享以进行搜索的代码。source是要搜索的字符数组,target是要搜索的字符串。
*/
// 该方法主要实现的是:从source字符串中查找子字符串target出现的索引位置,如果target不是子字符串,则直接返回-1。
// 在此目的的基础上,加了一些东西作为参数条件,如source字符串的起始位置,也加了target字符串的起始位置targetOffset做参数
// 同时加了source字符串的长度作为参数和target字符串的长度作为参数。
// 在理解此方法目的后,看代码就顺眼多了。
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;
}
// 获取target的起始字符
char first = target[targetOffset];
// source的起始位置 + (source的长度-target的长度)
int max = sourceOffset + (sourceCount - targetCount);
// i = 从
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
// 找到与source字符串相等的字符和相应的索引下标i
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;
}
/**
从字符串的尾部开始,判断子字符串str第一次出现的索引位置。
如果子字符串为空,则直接返回该字符串的长度,表示""出现在末尾。
*/
public int lastIndexOf(String str) {
return lastIndexOf(str, value.length);
}
/**
从fromIndex位置开始,查找字符串中出现子字符串str的索引位置。
如果没有,那就返回-1.
*/
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,
String target, int fromIndex) {
return lastIndexOf(source, sourceOffset, sourceCount,
target.value, 0, target.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;
}
}
// 原来字符串截取的方法是通过调用构造方法进行创建的,并将创建的字符串进行返回
// 注意:检测字符串的substring方法在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);
}
// 该方法与前一个substring方法类似(public String substring(int beginIndex)),最终调用的都是同一个构造方法。
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);
}
/**
*
返回一个字符序列,该字符序列是该序列的子序列。
对此方法以str.subQequence(begin,end)的调用和以str.substring(begin,end)方法的调用的结果是一样的。
这个方法被定义为字符串类可以实现CharSequence接口。
*/
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
/**
将指定字符串连接到指定字符串得末尾
如果参数字符串的长度为0,那么就直接返回this。否则,返回一个字符串对象,该对象表示有字符串对象表示的字符序列
与由参数字符串表示的字符序列的串联。
*/
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);//复制字符串的内容到char数组,并扩展长度
str.getChars(buf, len);//将str的内容复制到buf数组中,位置从len开始。在这一步完成两个字符串的拼接
return new String(buf, true);//将buf数组转化为新的String实例并返回
}
/*
concat示例:
public static void main(String[] args) {
String hello = "hello";
String world = " world";
System.out.println(hello.concat(world));
System.out.println(hello);
}
输出结果:
hello world
hello
*/
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 boolean matches(String regex) {
return Pattern.matches(regex, this);
}
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
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(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
/**
此方法返回的数组包含此字符串的每个子字符串,这些子字符串由另一个与给定表达式匹配的子字符串终止,或者由字符串结尾终止。
数组中的子字符串按照他们出现再字符串的中的顺序进行排序。如果该表达式不能够和输入的字符串进行匹配,那么数组中的元素就只有一个,就是它自己。
limit参数主要是用于控制匹配的次数,所以呢,limit参数最终影响的是数组的长度。。如果limit 参数的值n大于0,那么该正则将最多被使用 n-1次,那么数组的长度就
不会大于n。如果limit的参数的值为非正数(小于等于0),那么就尽可能多次匹配。
*/
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
单字符情况下regex不等于正则表达式的元字符(meta character):.$|()[{^?*+\\
(2)two-char String and the first char is the backslash and
the second is not the ascii digit or ascii letter.
双字符情况下regex第一个字符是反斜杠,第二个字符不是Unicode编码中的数字或字母
*/
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; // 如果limit不大于0,表明尽量截取
ArrayList<String> list = new ArrayList<>();
// 这里的事情是:
// 根据正则的字符ch,和起始位置off,获取下一个要截取的索引位置
// 然后在else中,利用substring方法进行截取
// 接着再对初始的off进行赋值
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) { // 所以这里是两个条件选一,666 在这里存在的一个隐患是,空字符串也会进行添加操作
list.add(substring(off, next));
off = next + 1;
} else { // last one // 再次while(){}循环中,还有最后一个没有获取到,这里获取到最后一个进行添加操作
//assert (list.size() == limit - 1);
list.add(substring(off, value.length));
off = value.length;
break;
}
}
// If no match was found, return this
// 如果没有匹配到,就直接返回this
if (off == 0)
return new String[]{this};
// Add remaining segment
// 将在while循环中没有加上的末尾一个添加上。
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 String[] split(String regex) {
return split(regex, 0);
}
/**
返回一个新字符串,该字符串由与指定分隔符的副本连接在一起的charsequence元素的副本组成。
*/
// 可变长参数
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();
}
/**
根据给定的字符编码来将字符串转的字符序列为小写。
如果大小写转化只是用于显示的话,建议用默认的无参数方法即可。
而如果大小写转化用于业务逻辑的话,强烈建议采用指定Locale.ROOT或Locale.ENGLISH的方法。
*/
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)
// utf-16 编码中的 unicode 高代理项代码单元的最大值。高代理项也称为前导代理项。
&& (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);
}
public String toLowerCase() {
return toLowerCase(Locale.getDefault());
}
public String toUpperCase(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
int firstLower;
final int len = value.length;
/* Now check if there are any characters that need to be changed. */
// 检查是否有字符需要改变大小写的
scan: {
for (firstLower = 0 ; firstLower < len; ) {
int c = (int)value[firstLower];
int srcCount;
if ((c >= Character.MIN_HIGH_SURROGATE)
&& (c <= Character.MAX_HIGH_SURROGATE)) {
c = codePointAt(firstLower);
srcCount = Character.charCount(c);
} else {
srcCount = 1;
}
int upperCaseChar = Character.toUpperCaseEx(c);
if ((upperCaseChar == Character.ERROR)
|| (c != upperCaseChar)) {
break scan;
}
firstLower += srcCount;
}
return this;
}
/* result may grow, so i+resultOffset is the write location in result */
int resultOffset = 0;
char[] result = new char[len]; /* may grow */
/* Just copy the first few upperCase characters. */
System.arraycopy(value, 0, result, 0, firstLower);
String lang = locale.getLanguage();
boolean localeDependent =
(lang == "tr" || lang == "az" || lang == "lt");
char[] upperCharArray;
int upperChar;
int srcChar;
int srcCount;
for (int i = firstLower; 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) {
upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale);
} else {
upperChar = Character.toUpperCaseEx(srcChar);
}
if ((upperChar == Character.ERROR)
|| (upperChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
if (upperChar == Character.ERROR) {
if (localeDependent) {
upperCharArray =
ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale);
} else {
upperCharArray = Character.toUpperCaseCharArray(srcChar);
}
} else if (srcCount == 2) {
resultOffset += Character.toChars(upperChar, result, i + resultOffset) - srcCount;
continue;
} else {
upperCharArray = Character.toChars(upperChar);
}
/* Grow result if needed */
int mapLen = upperCharArray.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] = upperCharArray[x];
}
resultOffset += (mapLen - srcCount);
} else {
result[i + resultOffset] = (char)upperChar;
}
}
return new String(result, 0, len + resultOffset);
}
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
/**
将此字符串的头尾的空格去除后,返回字符串
如果此Sring对象表示空字符序列,或者出此String对象表示的字符序列的第一个和最后一个字符的代码都大于'\u002'
(空字符),则返回此String对象的引用,即this。
*/
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--;
}
// 最后利用substring()方法进行获取字符进行返回
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
public String toString() {
return this;
}
/**
将此字符串转成字符数组
*/
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 static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
/**
*
*/
public static String format(Locale l, String format, Object... args) {
return new Formatter(l).format(format, args).toString();
}
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
public static String valueOf(char data[]) {
return new String(data);
}
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) {
return new String(data, offset, count);
}
/**
* Equivalent to {@link #valueOf(char[])}.
*
* @param data the character array.
* @return a {@code String} that contains the characters of the
* character array.
*/
public static String copyValueOf(char data[]) {
return new String(data);
}
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);
}
/**
返回字符串的规范表示
最初为空的字符串池由String类私有维护。
调用intern方法时,如果池已经包含等于此字符串对象的字符串(由equals(Object)方法确定),则返回池中的字符串。
否则,这个字符串对象添加到池中,并返回此String对象的引用。
因此,对于任何两个字符串s和t,只有当s.equals(t)为true时,s.intern() == t.intern()为真
*/
public native String intern();
}