Stirng类的方法有很多,本片博客描述的就是所有的方法,包括一些新的方法(主要是JDK1.8之后新出的方法)。
- charAt( int index )
charAt方法描述:
Returns the char value at the specified index.返回指定位置的字符
那么也就是说,在方法中传入一个参数,返回一个具体的位置。具体代码如下:
public class TestString5 {
public static void main(String[] args) {
String s = "http://oschina.net/lujiapeng" ;
char c = s.charAt( 3 ) ;
System.out.println( c ); // p
c = s.charAt( 9 ) ;
System.out.println( c ); // c
c = s.charAt( 100 ) ;
System.out.println( c ); // StringIndexOutOfBoundsException: String index out of range: 100
}
}
那么在这里清楚的看到,当传入一个3的时候,会返回一个字符p,如果传入一个9的话,会返回一个字符c,如果传入100的话,会抛出一个异常:StringIndexOutofBoundsException ,这个异常信息表示字符串的索引超出了对应的范围。通俗的理解:如果想要获取指定位置的字符,是不能超出字符串长度的;如果超出了长度,就发生异常信息了。但是charAt内部是如何实现的呢?要搞清楚。
public char charAt(int index) {
// 这里先进行一次判断
if (isLatin1()) {
// 进入到这里进行操作
return StringLatin1.charAt(value, index);
} else {
return StringUTF16.charAt(value, index);
}
}
但是又有问题了,如何判断的呢 ?在这之前,要清楚,写字面值和使用char数组进行new的效果是一样的,所以要先来明确一下有些常量值是什么,分别是多少 。
当new String( char[] chars )的时候,会发现此时会进行一个类的初始化,当一个类进行初始化的时候,就会对类中的变量进行初始化操作,那么有些值就会发生改变。
static final byte LATIN1 = 0;
private final byte coder = 0 ;
static final boolean COMPACT_STRINGS = false ;
但是,COMPACT_STRINGS 又在静态代码块中进行了赋值,如下 :
static {
COMPACT_STRINGS = true;
}
那么,最终 COMPACT_STRINGS = true ;
// 那么此时返回了true
private boolean isLatin1() {
return COMPACT_STRINGS && coder == LATIN1;
}
如果是true 的话,那么就会进入到如下代码 :
public static char charAt(byte[] value, int index) {
// 首先进行判断,如果index 的值小于0,或者index的值大于或等于数组的长度
if (index < 0 || index >= value.length) {
// 那么这里就会抛出异常信息
throw new StringIndexOutOfBoundsException(index);
}
// 这里进行操作( 位与操作 : 数组位置与 0xff 位于,然后转成字符 )
return (char)(value[index] & 0xff);
}
此时会发现,这里传入了一个数组,并且传入了一个index的索引。那么这个数组要注意,就是将字面值(也就是声明的字符串中的值,放在了这里,同时经过了改变),其实底层就是操作这个数组的。那么此时就得到了一个字符,这个字符是我们所需要的,但是要明确,数组下标是从0 开始的,那么字符串中的下标也就是从0开始的。
- codePointAt(int index)
codePointAt()方法描述如下:
Returns the character (Unicode code point) at the specified index.
返回指定位置的字符( 以unicode的形式返回 ),示例如下:
public class TestString6 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
int j = s.codePointAt( 0 ) ;
System.out.println( j );
}
}
- codePointBefore(int index)
codePointBefore(int index)方法描述如下:
Returns the character (Unicode code point) before the specified index.
返回在指定位置之前的字符( 以Unicode方式返回 )。示例如下:
public class TestString7 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
int j = s.codePointBefore( 1 ) ;
System.out.println( j );
}
}
这里一定要注意,如果传入的参数是0,或者超过字符串的长度的话,那么就会抛出一个异常信息。官方API描述如下:
IndexOutOfBoundsException - if the index argument is less than 1 or greater than the length of this string.
- codePointCount(int beginIndex, int endIndex)
codePointCount(int beginIndex, int endIndex)方法描述如下:
Returns the number of Unicode code points in the specified text range of this String.
返回此字符串指定文本范围内的Unicode代码点的数量。示例如下:
public class TestString7 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
int j = s.codePointBefore( 1 ) ;
System.out.println( j );
int count = s.codePointCount( 1 , 4 ) ;
System.out.println( count );
}
}
当传入这两个参数的时候,会返回3 , 那么也就是说从开始位置到结束位置中包含了几个Unicode的点,也就是说在这个区间内,在Unicode表中对应的点有几个。
- compareTo(String anotherString)
这个方法主要是用来和另一个字符串进行比较,在官方API中的描述如下:
Compares two strings lexicographically.
比较两个字符串按照字典排序。其实是String类实现了Comparable接口,必须要实现其中的方法compareTo,那么在String类中也实现了这个方法,那么这个方法的具体返回如下:
the value 0 if the argument string is equal to this string; a value less than 0 if this string is lexicographically less than the string argument; and a value greater than 0 if this string is lexicographically greater than the string argument.
在爱词霸上的翻译如下:
如果参数字符串等于此字符串,则值为0;如果此字符串按字典顺序小于字符串参数,则值小于0;如果此字符串按字母顺序大于字符串参数,则值大于0。
具体示例如下:
public class TestString8 {
public static void main(String[] args) {
String s = "hello , world " ;
String s2 = "hello , java " ;
System.out.println( s.compareTo( s2 ) );
}
}
- compareToIgnoreCase(String str)
该方法也是用于比较字符串,只不过相对于上边的方法来说,不过不区分大小写。示例如下:
public class TestString8 {
public static void main(String[] args) {
String s = "hello , world " ;
String s2 = "hello , java " ;
System.out.println( s.compareTo( s2 ) );
s = "HELLO , JAVA " ;
s2 = "hello , java " ;
System.out.println( s.compareTo( s2 ) );
System.out.println( s.compareToIgnoreCase( s2 )); // 0
}
}
此时这里返回了0,表示此时忽略大小写来进行比较的。官方API的描述如下:
a negative integer, zero, or a positive integer as the specified String is greater than, equal to, or less than this String, ignoring case considerations.
翻译如下:指定字符串的负数、零或正整数大于、等于或小于此字符串,忽略大小写考虑。
- concat(String str)
该方法描述用字符串连接另一个字符串,官方API描述如下:
Concatenates the specified string to the end of this string.
在这个字符串的尾部连接指定的字符串,示例如下:
public class TestString8 {
public static void main(String[] args) {
String s = "hello , world " ;
String s2 = "hello , java " ;
System.out.println( s.compareTo( s2 ) );
s = "HELLO , JAVA " ;
s2 = "hello , java " ;
System.out.println( s.compareTo( s2 ) );
System.out.println( s.compareToIgnoreCase( s2 ));
String s3 = s.concat( s2 ) ;
System.out.println( s3 ); // HELLO , JAVA hello , java
}
}
从结果上可以清楚的看到,是将另一个字符串拼接到另一个字符串后边。那么在源码中进行了操作,源码如下:
public String concat(String str) {
// 获取到字符串的长度
int olen = str.length();
// 如果长度是0,那么就认为是空串
if (olen == 0) {
// 如果是空串的话,那么就返回自己
return this;
}
// coder方法主要是返回byte值
if (coder() == str.coder()) {
byte[] val = this.value;
byte[] oval = str.value;
// 计算新数组的长度
int len = val.length + oval.length;
// 复制数组
byte[] buf = Arrays.copyOf(val, len);
System.arraycopy(oval, 0, buf, val.length, oval.length);
// 使用新数组 创建新的字符串
return new String(buf, coder);
}
int len = length();
byte[] buf = StringUTF16.newBytesFor(len + olen);
// getBytes方法内部还是要赋值数组或构建一个新的数组
getBytes(buf, 0, UTF16);
str.getBytes(buf, len, UTF16);
// 构建一个新的字符串
return new String(buf, UTF16);
}
这里可以清楚的看到当返回的时候,是new String的形式来返回,所以说这里是新创建了一个字符串。
- contains(CharSequence s)
该方法描述是否包含了指定的字符序列,如果且仅当此字符串包含指定的 char 值序列时, 才返回 true。示例如下:
public class TestString8 {
public static void main(String[] args) {
// 检测是否包含指定字符串
boolean b = s3.contains( "j" ) ;
System.out.println( b ); // true
}
}
如果是包含了指定的字符序列,那么就返回true,否则就返回false。源码如下:
public boolean contains(CharSequence s) {
// 调用indexOf 方法,主要判断 长度是否大于等于0,如果大于等于0 , 那么就证明存在。
return indexOf(s.toString()) >= 0;
}
- contentEquals(CharSequence cs)
当且仅当此字符串表示与指定序列相同的char值序列时,结果为真。注意,如果CharSequence是StringBuffer,那么方法就会在它上同步。此方法有重载。示例如下:
public class TestString8 {
public static void main(String[] args) {
// 判断是否相等
boolean b = "123123".contentEquals( "jasdfgasdgf" ) ;
System.out.println( b );
}
}
这里主要是判断是否与内容相等,重载的方法传入的是StringBuffer,源码中有描述
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder( 参数如果是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( 参数如果是String类型,判断是否相等)
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
int n = cs.length();
if (n != length()) {
return false;
}
// 将调用这个方法的字符串 中存放的字节数组进行赋值操作
byte[] val = this.value;
if (isLatin1()) {
// 利用循环判断是否相等
for (int i = 0; i < n; i++) {
if ((val[i] & 0xff) != cs.charAt(i)) {
return false;
}
}
} else {
在StringUTF16调用方法判断是否相等
if (!StringUTF16.contentEquals(val, cs, n)) {
return false;
}
}
return true;
}
如果源码阅读起来有点困难可以考虑不看。同理的方法还有:contentEquals(StringBuffer sb)
- copyValueOf(char[] data) 、copyValueOf(char[] data, int offset, int count)
等价与方法valueOf,是静态方法,那么就可以按照如下方式使用:
String.copyValueOf() 等价于valueOf().: Returns the string representation of a specific subarray of the char array argument.(返回char数组参数的特定子数组的字符串表示形式。)
示例如下:
public class TestString9 {
public static void main(String[] args) {
char[] chars = {'a' , 'b' , 'c' } ;
String s = String.copyValueOf( chars ) ;
System.out.println( s );
s = String.copyValueOf( chars , 0 , 2 ) ;
System.out.println( s );
}
}
那么这个方法具体干了些什么,还是要看源码的
public static String copyValueOf(char data[]) {
return new String(data);
}
public static String copyValueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
此时可以发现,其实内部是通过构造方new出来一个字符串,所以说这个方法容易造成内存溢出。
同理的方法 :valueOf方法,如下所示:
static String valueOf(boolean b)
static String valueOf(char c)
static String valueOf(char[] data)
static String valueOf(char[] data, int offset, int count)
static String valueOf(double d)
static String valueOf(float f)
static String valueOf(int i)
static String valueOf(long l)
static String valueOf(Object obj)
那么这里描述的也就是说将任何一个对象都可以转换成字符串。示例如下:
public class TestString10 {
public static void main(String[] args) {
char[] chars = {'1' , '2' } ;
/**
* return new String(data);
*/
String s = String.valueOf( chars ) ;
/**
* return new String(data, offset, count);
*/
s = String.valueOf( chars , 0 , 1 ) ;
/**
* return Integer.toString(i);
*/
s = String.valueOf(1 ) ;
/**
* if (COMPACT_STRINGS && StringLatin1.canEncode(c)) {
* return new String(StringLatin1.toBytes(c), LATIN1);
* }
* return new String(StringUTF16.toBytes(c), UTF16);
*/
s = String.valueOf( 'c' ) ;
/**
* return Long.toString(l);
*/
s = String.valueOf( 3L ) ;
/**
* return Float.toString(f);
*/
s = String.valueOf( 9.0F ) ;
/**
* return Double.toString(d);
*/
s = String.valueOf( 10.0 ) ;
/**
* public static String valueOf(boolean b) {
* return b ? "true" : "false";
* }
*/
s = String.valueOf( true ) ;
}
}
那么在这里就列举出来大部分的情况,所以如果想要将基本数据类型转换成字符串,那么就可以使用这种方法,当然有以下更为简洁的方法:
int i = 1 ;
String s = i + "" ;
那么采用这个方式可能更加简洁。
- endsWith(String suffix) & startsWith(String prefix) & startsWith(String prefix, int toffset)
这两个方法是相同的想法,官方API描述:
endsWith(String suffix) Tests if this string ends with the specified suffix.(判断是否以特定的字符串结尾)
startsWith(String prefix) Tests if this string starts with the specified prefix.(判断是否以特定的字符串开始)
startsWith(String prefix, int toffset) Tests if the substring of this string beginning at the specified index starts with the specified prefix.(判断从指定索引处开始的字符串的子字符串是否以指定的前缀开始。)
示例如下:
public class TestString11 {
public static void main(String[] args) {
String url = "https://my.oschina.net/lujiapeng" ;
boolean b = url.endsWith(".com") ; // false
System.out.println( b );
b = url.startsWith("http") ; // true
System.out.println( b );
b = url.startsWith( "http" , 3 ) ; // false
System.out.println( b );
}
}
无论endsWith 或者 startsWith 都是调用 startsWith方法,具体可以查看源码:
public boolean startsWith(String prefix, int toffset) {
// 如果小于0 , 或者大于剩余的长度,那么就是超出范围的,返回false
// Note: toffset might be near -1>>>1.
if (toffset < 0 || toffset > length() - prefix.length()) {
return false;
}
// 当前指定字符串中存放的数组
byte ta[] = value;
// 要判断的字符串中的数组
byte pa[] = prefix.value;
int po = 0;
// 看一下要判断的字符串的数组的长度
int pc = pa.length;
if (coder() == prefix.coder()) {
int to = isLatin1() ? toffset : toffset << 1;
// 利用循环进行判断,只要有一个不相等,那么就返回false
while (po < pc) {
if (ta[to++] != pa[po++]) {
return false;
}
}
} else {
if (isLatin1()) { // && pcoder == UTF16
return false;
}
// 利用循环进行判断,只要有一个不相等,那么就返回false
// coder == UTF16 && pcoder == LATIN1)
while (po < pc) {
if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) {
return false;
}
}
}
// 以上都没有进去,就证明存在,返回true
return true;
}
- equals(Object anObject) & equalsIgnoreCase(String anotherString)
这两个方法可以一起看,主要目的判断是否相等。equals方法其实是重写了来自父类Object的方法,那么本身也就是不同的。示例如下:
public class TestString12 {
public static void main(String[] args) {
String s1 = "abc" ;
String s2 = "ABC" ;
/**
* 重写了来自父类Object的方法
*/
System.out.println( s1.equals( s2 ) );
/**
* 忽略大小写的比较
*/
System.out.println( s1.equalsIgnoreCase( s2 ));
}
}
那么用起来是比较简单的,但是要看一下源码:
equals() :
public boolean equals(Object anObject) {
// 如果两个对象地址相同,证明是一个对象
if (this == anObject) {
return true;
}
// 如果另一个对象是String类型的,那么就强制类型转换
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
// 调用StringLatin1.equals 或 StringUTF16.equals
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
StringLatin1.equals() 方法 :
// 注意,这里传入的是数组,可能要进行比较了
public static boolean equals(byte[] value, byte[] other) {
// 如果两个数组的长度相同,那么就通过循环比较,如果有一个不一样,那么就返回false
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
// 长度相同,而且还没有找到不一样的,那么就返回true
return true;
}
return false;
}
StringUTF16.equals
public static boolean equals(byte[] value, byte[] other) {
// 判断长度是否相等,然后获取字符进行比较,如果有一个不相等,那么就返回false
if (value.length == other.length) {
int len = value.length >> 1;
for (int i = 0; i < len; i++) {
if (getChar(value, i) != getChar(other, i)) {
return false;
}
}
// 没有找到不相等的元素,返回true
return true;
}
return false;
}
而 equalsIgnoreCase(String anotherString)方法的源码就不太一样了,可以看一下:
public boolean equalsIgnoreCase(String anotherString) {
// 如果地址相同就返回true,否则进行判断
return (this == anotherString) ? true
: (anotherString != null) // 传入的字符串不是null
&& (anotherString.length() == length()) // 两个字符串长度相同
&& regionMatches(true, 0, anotherString, 0, length()); // 进入方法中进行判断
}
// 此时 ignoreCas = true
// toffset = 0 ; ooffset = 0 ,length就是字符串的长度
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
if (!ignoreCase) {
// 如果进入到了这里,那么就通过这方法进行比较( 主要是获取字符进行逐位比较 )
return regionMatches(toffset, other, ooffset, len);
}
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)length() - len)
|| (ooffset > (long)other.length() - len)) {
return false;
}
byte tv[] = value;
byte ov[] = other.value;
// 这里主要是进行比较,将字节数组转换成字符数组,再转换成大写进行逐个比较
if (coder() == other.coder()) {
return isLatin1()
? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len)
: StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len);
}
return isLatin1()
? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)
: StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);
}
如果想要继续看源码的话,可以进行深入理解。
- format(String format, Object... args) && format(Locale l, String format, Object... args)
静态方法两个,也就是说可以通过String直接调用,描述如下:
format(String format, Object... args)
Returns a formatted string using the specified format string and arguments.(使用指定的格式字符串和参数返回格式化字符串。)
format(Locale l, String format, Object... args)
Returns a formatted string using the specified locale, format string, and arguments.( 使用指定的语言环境、格式字符串和参数返回格式化字符串。)
那么简单点说就是:用于创建格式化的字符串以及连接多个字符串对象。显示不同转换符实现不同数据类型到字符串的转换:
转换符 | 说明 | 示例 |
---|---|---|
%s | 字符串类型 | "lujiapeng" |
%c | 字符类型 | 'm' |
%b | 布尔类型 | true |
%d | 整数类型(十进制) | 99 |
%x | 整数类型(十六进制) | FF |
%o | 整数类型(八进制) | 77 |
%f | 浮点类型 | 99.99 |
%a | 十六进制浮点类型 | FF.35AE |
%e | 指数类型 | 9.38e+5 |
%g | 通用浮点类型(f和e类型中较短的) | |
%h | 散列码 | |
%% | 百分比类型 | % |
%n | 换行符 | |
%tx | 日期与时间类型(x代表不同的日期与时间转换符 |
示例如下:
public class TestString13 {
public static void main(String[] args) {
String str=null;
str=String.format("Hi,%s", "lujiapeng");
System.out.println(str);
str=String.format("Hi,%s:%s.%s", "my","oschina","lujiapeng");
System.out.println(str);
System.out.printf("字母a的大写是:%c %n", 'A');
System.out.printf("3>7的结果是:%b %n", 3>7);
System.out.printf("100的一半是:%d %n", 100/2);
System.out.printf("100的16进制数是:%x %n", 100);
System.out.printf("100的8进制数是:%o %n", 100);
System.out.printf("50元的书打8.5折扣是:%f 元%n", 50*0.85);
System.out.printf("上面价格的16进制数是:%a %n", 50*0.85);
System.out.printf("上面价格的指数表示:%e %n", 50*0.85);
System.out.printf("上面价格的指数和浮点数结果的长度较短的是:%g %n", 50*0.85);
System.out.printf("上面的折扣是%d%% %n", 85);
System.out.printf("字母A的散列码是:%h %n", 'A');
}
}
输出结果如下:
Hi,lujiapeng
Hi,my:oschina.lujiapeng
字母a的大写是:A
3>7的结果是:false
100的一半是:50
100的16进制数是:64
100的8进制数是:144
50元的书打8.5折扣是:42.500000 元
上面价格的16进制数是:0x1.54p5
上面价格的指数表示:4.250000e+01
上面价格的指数和浮点数结果的长度较短的是:42.5000
上面的折扣是85%
字母A的散列码是:41
还有一个方法会传入一个Local类型的参数,表明是指定的哪个指定的语言环境。
那么源码呢,会使用 Formatter中的format方法,那么这里就不细说了。
- getBytes()
返回一个新的字节数组,那么这个方法有重载,所以一次看完。
getBytes():Encodes this String into a sequence of bytes using the platform's default charset, storing the result into a new byte array.(使用平台的默认字符集将这个字符串编码为一个字节序列,将结果存储到一个新的字节数组中。)
getBytes(String charsetName) : Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array.(使用指定的charset将这个字符串编码为一个字节序列,将结果存储到一个新的字节数组中。)
getBytes(Charset charset):Encodes this String into a sequence of bytes using the given charset, storing the result into a new byte array.(使用给定的字符集将这个字符串编码为一个字节序列,将结果存储到一个新的字节数组中。)
示例如下 :
public class TestString14 {
public static void main(String[] args) {
String s = "http://myoschina.com/lujiapeng" ;
byte[] bytes = s.getBytes() ;
System.out.println(Arrays.toString( bytes ));
try {
bytes = s.getBytes("UTF-8") ;
System.out.println( Arrays.toString( bytes ) );
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
主要是将对应的字符串转换成字节数组,那么内部如何实现的呢?看源码:
public byte[] getBytes() {
return StringCoding.encode(coder(), value);
}
static byte[] encode(byte coder, byte[] val) {
// 获取默认的当前的环境的编码
Charset cs = Charset.defaultCharset();
// 如果当前编码环境是UTF_8,那么使用encodeUTF8方法进进行编码,其余的也是一样的
if (cs == UTF_8) {
return encodeUTF8(coder, val, true);
}
if (cs == ISO_8859_1) {
return encode8859_1(coder, val);
}
if (cs == US_ASCII) {
return encodeASCII(coder, val);
}
StringEncoder se = deref(encoder);
if (se == null || !cs.name().equals(se.cs.name())) {
se = new StringEncoder(cs, cs.name());
set(encoder, se);
}
// 进行处理,返回字节数组
return se.encode(coder, val);
}
但是要注意,这个方法一直返回一个新的byte数组,还有一个地方要注意,如果要传入多个参数,注意要抓取异常。
- getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
该方法描述了将字符串中的字符复制到目标数组中,有几个参数要注意一下:
srcBegin : 开始位置
srcEnd : 结束位置
dst : 目标数组
dstBegin : 目标数组的开始位置
注意,该方法可能会造成下标越界异常。可能造成异常的点有:srcBegin、srcEnd、dstBegin这三个地方,具体代码如下示例:
public class TestString15 {
public static void main(String[] args) {
String url = "https://my.oschina.net/lujiapeng" ;
char[] chars = new char[10] ;
System.out.println( chars );
url.getChars( 1 , 11 , chars , 0 ) ; // ttps://my.
System.out.println( chars );
}
}
接下来来看一下源码,为什么会有异常信息的出现,而且每次的异常信息都有可能不太一样,要注意:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
// 进行开始位置与结束位置的检查
checkBoundsBeginEnd(srcBegin, srcEnd, length());
checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);
if (isLatin1()) {
StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin);
} else {
StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin);
}
}
static void checkBoundsBeginEnd(int begin, int end, int length) {
// 如果开始位置小于0 , 或者开始位置大于结束位置,或者 结束位置大于字符串的长度,抛出异常信息
if (begin < 0 || begin > end || end > length) {
// 字符串索引越界异常( 可能是参数传入的有问题)
throw new StringIndexOutOfBoundsException(
"begin " + begin + ", end " + end + ", length " + length);
}
}
// 进行目标位置、目标数组检查
static void checkBoundsOffCount(int offset, int count, int length) {
// 如果数组中的位置小于0 , 或者字符串中的结束位置大于开始位置,或者数组的开始位置大于数组中的剩余位置
if (offset < 0 || count < 0 || offset > length - count) {
throw new StringIndexOutOfBoundsException(
"offset " + offset + ", count " + count + ", length " + length);
}
}
那么在这里主要就是对数组中的开始位置、字符串中的位置进行了检查,所以抛出的异常都是字符串索引越界异常信息(java.lang.StringIndexOutOfBoundsException)。所以在官方API中;进行了详细的描述:
IndexOutOfBoundsException - If any of the following is true:
srcBegin is negative.// 开始位置是一个负数
srcBegin is greater than srcEnd // 开始位置大于结束位置
srcEnd is greater than the length of this string // 结束位置比字符串的长度要大
dstBegin is negative // 数组的开始位置是一个负数
dstBegin+(srcEnd-srcBegin) is larger than dst.length // 数组的开始位置 加上 要复制的数量(也就是字符串的结束位置减去开始位置)的总和要比数组的长度要长的。
那么这个方法就看完了。
- hashCode()
该方法来自于父类java.lang.Object,用来计算一个hash值。示例如下:
public class TestString16 {
public static void main(String[] args) {
String s = "os.china" ;
System.out.println( s.hashCode() ); // 573699277
}
}
注意,这里重写了HashCode方法,具体源码如下:
public int hashCode() {
int h = hash; // hash的默认值就是0
if (h == 0 && value.length > 0) {
hash = h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
}
return h;
}
具体的分析就不去看了,有兴趣可以自己看。
- indexOf()
表示第一次出现的位置,有如下重载:
indexOf(int ch):Returns the index within this string of the first occurrence of the specified character.(返回指定的字符第一次在这个字符串中的索引)
indexOf(int ch, int fromIndex):Returns the index within this string of the first occurrence of the specified character, starting the search at the specified index.(从开始的索引开始搜索,返回指定的字符第一次出现在字符串中的位置)
indexOf(String str):Returns the index within this string of the first occurrence of the specified substring.(返回指定子串在字符串中第一次出现的索引)
indexOf(String str, int fromIndex):Returns the index within this string of the first occurrence of the specified substring, starting at the specified index.(从开始的索引开始搜索,返回指定的字符串第一次出现在字符串中的位置)
那么这个方法使用起来比较简单,那么我们来看一下示例:
public class TestString17 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
int first = s.indexOf( 12 ) ;
System.out.println( first ); // -1
first = s.indexOf( 's' ) ;
System.out.println( first ); // 4
first = s.indexOf( 'n' , 10 ) ;
System.out.println( first ); // 16
first = s.indexOf( "https" ) ;
System.out.println( first ); // 0
first = s.indexOf( "net" , 8 ) ;
System.out.println( first ); // 19
}
}
那么源码中进行了描述,依旧是在StringLatin1或StringUTF16类中进行操作。那么现在来看第一个描述:
// indexOf内部源码调用了相同的方法进行重载
public int indexOf(int ch) {
return indexOf(ch, 0);
}
//这里依旧调用StringLatin1.indexOf方法或StringUTF16.indexOf方法进行操作
public int indexOf(int ch, int fromIndex) {
return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex)
: StringUTF16.indexOf(value, ch, fromIndex);
}
StringLatin1.indexOf 的方法描述如下:
// 传入的参数:value表示字符串的数组,ch表示要所有的字符,fromIndex表示从哪里开始搜索
public static int indexOf(byte[] value, int ch, int fromIndex) {
if (!canEncode(ch)) { //将ch右移8位,看看是否等于0,如果相等,直接返回-1
return -1;
}
// 最大值表示当前字符串中的数组的长度
int max = value.length;
// 如果开始的位置小于0,那么就从0开始
if (fromIndex < 0) {
fromIndex = 0;
} else if (fromIndex >= max) { // 如果开始的位置大于等于最大的长度,那么就返回-1
// Note: fromIndex might be near -1>>>1.
return -1;
}
// 将int参数转成byte参数
byte c = (byte)ch;
// 通过遍历查找对应的位置,如果相等,那么就返回指定的位置
for (int i = fromIndex; i < max; i++) {
if (value[i] == c) {
return i;
}
}
return -1;// 如果依旧没有找到,那么就返回-1
}
StringUTF16.indexOf方法描述如下:参数依旧和上边一样
public static int indexOf(byte[] value, int ch, int fromIndex) {
int max = value.length >> 1; // 最大值是数组右移1位之后进行的赋值
if (fromIndex < 0) { // 如果开始的位置小于0,那么就从0开始
fromIndex = 0;
} else if (fromIndex >= max) { // 如果开始的位置大于等于最大的长度,那么就返回-1
// 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))
return indexOfChar(value, ch, fromIndex, max); // 调用方法,相关资料可以进行查阅,这里就不再进行描述了
} else {
return indexOfSupplementary(value, ch, fromIndex, max);// 调用方法,相关资料可以进行查阅,这里就不再进行描述了
}
}
那么传入字符串的方法就在这里就不再看了,底层依旧是通过遍历数组进行比较。
- intern()
该方法表示返回字符串的规范形式。官方API描述如下:
Returns a canonical representation for the string object.
示例如下:
public class TestString18 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
System.out.println( s );
System.out.println( s.intern() );
s = new String( s ) ;
System.out.println( Integer.toHexString(System.identityHashCode( s )) ); // 1e643faf
System.out.println( Integer.toHexString( System.identityHashCode( s.intern() ))); // 6e8dacdf
}
}
此时我们可以发现,这里其实描述了对应的地址,如果光看输出字符串的话,是没有问题的,但是要查看地址的话,会发现,这两个地址是不同的,那么应该如何解释呢?其实比较好理解,s当new之后其实是指向堆中的内容,也就是存放在堆中的地址,那么intern方法会将字符串的地址直接指向非堆的空间(1.8之后,池的概念已经被取消了)。
- isEmpty()
判断字符串是否为空。示例如下
public class TestString19 {
public static void main(String[] args) {
var s = "https://my.oschina.net/lujiapeng" ;
System.out.println( s.isEmpty() ); // false
s = "" ;
System.out.println( s.isEmpty() ); // true
}
}
其源码如下:
public boolean isEmpty() {
// 主要是判断字符串中是否包含字符( 如果有空白,也算有字符 )
return value.length == 0;
}
- lastIndexOf
判断最后一次出现的位置,基本与indexOf一致,只不过在源码中使用倒序的方式进行遍历,得出位置。
- length()
返回当前字符串的长度。
- replace()
该方法描述了关于字符串进行替换的方法,有重载
replace(char oldChar, char newChar):替换指定的字符
replace(CharSequence target, CharSequence replacement) : 替换指定的字符序列
示例如下 :
public class TestString20 {
public static void main(String[] args) {
var s = "https://my.oschina.net/lujiapeng" ;
System.out.println( s.replace( 'h' , 's')); // sttps://my.oscsina.net/lujiapeng
System.out.println( s.replace("https" , "Whaha") ); // Whaha://my.oschina.net/lujiapeng
}
}
此时可以看到对应的字符或字符串进行了改变,通过源码可以发现,如果是替换字符序列的,那么使用StringBuilder进行追加字符串,然后进行替换;如果是替换字符的话,那么就将字符转换成字节,然后进行new String( byte[] bytes , 0 )才返回字符串。
- replaceAll
Replaces each substring of this string that matches the given regular expression with the given replacement.( 用给定的替换替换匹配给定正则表达式的字符串的每个子字符串。) 那么在这里就要明确如何使用。使用replaceAll方法的时候要注意,第一个参数可以是正则表达式( 关于什么是正则表达式,放在后边再说),同样也可以是指定的字符串。第二个参数表示要替换的内容。示例如下:
public class TestString20 {
public static void main(String[] args) {
var s = "https://my.oschina.net/lujiapeng" ;
System.out.println( s.replace( 'h' , 's')); // sttps://my.oscsina.net/lujiapeng
System.out.println( s.replace("https" , "Whaha") ); // Whaha://my.oschina.net/lujiapeng
System.out.println( s.replaceAll( "h" , "z")); // zttps://my.osczina.net/lujiapeng
System.out.println( s.replaceAll( "t" , "k")); // hkkps://my.oschina.nek/lujiapeng
}
}
注意看输出结果,就会发现其实是将指定的字符串进行了替换,那么在这里使用的是字符串进行替换。如果要使用正则表达式的话,也是可以的。
- replaceFirst
Replaces the first substring of this string that matches the given regular expression with the given replacement.(用给定的替换替换匹配给定正则表达式的字符串的第一个子字符串。) 注意,此方法主要就是对第一次出现的子串进行替换,与replaceAll使用起来几乎是一致的。
- split(String regex) ,split(String regex, int limit)
分割方法,这个方法有重载,但是呢,这两个方法的描述都是一样的。Splits this string around matches of the given regular expression.(将此字符串按照指定的正则表达式进行分割)示例如下:
public class TestString21 {
public static void main(String[] args) {
var s = "https://my.oschina.net/lujiapeng" ;
String[] arrays = s.split( "/") ;
System.out.println(Arrays.toString( arrays )); // [https:, , my.oschina.net, lujiapeng]
arrays = s.split( "/" , 2 ) ;
System.out.println( Arrays.toString( arrays )); // [https:, /my.oschina.net/lujiapeng]
}
}
通过示例可以清楚的看到,如果没有指定第二个参数,那么就会按照指定的字符进行分割,会将整个字符串进行分割;如果指定了第二个参数,那么就是将字符串分割成指定的第二个参数的长度。
- startsWith(String prefix),startsWith(String prefix, int toffset)
这两个方法与endWith的使用方式几乎一致,只不过是用来判断以指定字符串开头的意思,那么在这里就不去细细的描述了。
- subSequence(int beginIndex, int endIndex) ,substring(int beginIndex) , substring(int beginIndex, int endIndex)
该方法返回一个子序列,这个子序列是该序列中截取出来的;而subString表示截取对应的字符串。第一个参数表示开始的位置,第二个参数表示结束的位置,如果只传入了一个参数,那么就表示从开始的位置一直截取到字符串的末尾。示例如下:
public class TestString23 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
System.out.println( s.subSequence( 1 , 4 ) ); // ttp
System.out.println( s.substring( 9 ) ); // y.oschina.net/lujiapeng
System.out.println( s.substring( 4 , 7 )); // s:/
}
}
注意,通过源码可以清楚的看到subSequence底层是调用subString的,subString这个方法都是底层会new String 出来,所以说,如果使用这个方法的话,注意一下内存。
- toCharArray()
该方法描述将指定的字符串转换成一个char类型的数组,如果是在jdk1.8的时候,直接使用内部的char数组即可,因为本人使用的是jdk11,那么在这里就需要将byte数组转换成char数组。示例如下:
public class TestString24 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
System.out.println(Arrays.toString( s.toCharArray() ));
// [h, t, t, p, s, :, /, /, m, y, ., o, s, c, h, i, n, a, ., n, e, t, /, l, u, j, i, a, p, e, n, g]
}
}
- toLowerCase() , toLowerCase(Locale locale) , toUpperCase() , toUpperCase(Locale locale)
这四个方法放在一起看,描述如下:
toLowerCase() : 将指定的字符串按照默认的语言环境转换成小写
toUpperCase() : 将指定的字符串按照默认的语言环境转换成大写
而带有参数的方法则需要指定语言环境。示例如下:
public class TestString25 {
public static void main(String[] args) {
String s = "https://my.oschina.net/lujiapeng" ;
System.out.println( s.toUpperCase() ); // HTTPS://MY.OSCHINA.NET/LUJIAPENG
System.out.println( s.toUpperCase( Locale.FRANCE )); // HTTPS://MY.OSCHINA.NET/LUJIAPENG
}
}
这里仅仅使用了toUpperCase方法进行示例,那么toLowerCase()方法的使用规则和toUpperCase()方法没有什么区别。
- trim()
该方法描述了是将字符串剔除首尾空白,也就是说会将字符串的前后空白全部剔除掉。示例如下:
public class TestString26 {
public static void main(String[] args) {
String s = " https://my.oschina.net/lujiapeng " ;
System.out.println( s.length() ); // 34
s = s.trim() ;
System.out.println( s.length() ); // 32
}
}
在这里就可以清楚的看到前后的空白被剔除了。
- toString()
该方法来自于父类的Object类中的方法,只不过是重写了这个方法,使用起来没有什么问题,主要看源码:
public String toString() {
return this;
}
注意,这里是谁调用就返回谁,也就是说谁调用这个方法,就返回哪个字符串。
来源:oschina
链接:https://my.oschina.net/lujiapeng/blog/4809145