Java基础系列——String的方法(21)

冷暖自知 提交于 2020-12-16 03:25:08

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;
    }

注意,这里是谁调用就返回谁,也就是说谁调用这个方法,就返回哪个字符串。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!