JDK之String源码分析

文章目录


在这里插入图片描述

String不是基本数据类型,而是一个对象。因为对象的默认值是null,所以String的默认值也是null

String类的定义

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    

final — 不可继承&&不可变

  • String是由final修饰的,表示String是不可以继承的。
    为什么String要设计成为不可变的???

Serializable

这个序列化接口没有任何方法和域,仅用于标识序列化的语意。

String实现了序列化的接口Serializable,也就是说String是支持序列化和反序列化的。

那什么是Java对象的序列化呢? 深入分析Java的序列化与反序列化说:

Java平台允许我们在内存中创建可复用的Java对象,但是一般情况下,只有当jvm处于运行时,这些对象才可能存在,也就是说,这些对象的生命周期不会比JVM的生命周期长。但是在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象的序列化就能够帮助我们实现该功能
使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。
必须注意,对象序列化保存的是对象的"状态",也就是它的成员变量。由此可知,对象序列化不会关注类中的静态变量。
除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或者这网络中传递对象时,都会用到对象序列化
Java序列化API为处理对象序列化提供了一个标准机制,该API简单易用

在String源码中,我们也可以看到支持序列化的类成员定义:

private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

Comparable

这个接口只有一个compareTo(T 0)接口,用于对两个实例化对象比较大小。

CharSequence

这个接口是一个只读的字符序列。包括length(), charAt(int index), subSequence(int start, int end)这几个API接口,值得一提的是,StringBuffer和StringBuild也是实现了改接口。

成员变量

    /** The value is used for character storage. */
    private final char value[];

这是一个final修饰的字符数组,用于存储字符串内容,从final可以知道,String的内容一旦被初始化了就不能被更改了

    /** Cache the hash code for the string */
    private int hash; // Default to 0
  • hash是String实例化时hashCode的一个缓存。因为String经常用于比较,比如在HashMap中,如果每次进行比较都重新计算hashcode的值的话,很麻烦,而保存一个hashCode的缓存无疑能够优化这样的操作
  • String设置使用了享元模式【可以简单的理解位缓存,这是设计中的一种优化策略】,引入了"常量池"
  • 常量池指的是在编译期间确定,并且保存在已编译的.class文件中的一些数据。它包括了类、方法、接口等中的常量,以及字符串常量

在String源码中,我们也可以看到支持序列化的类成员定义

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    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];

Java的序列化机制是通过在运行时判断类的serialVersion来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。

serialPersistentFields[不懂]这个定义则比上一个少见许多,大致猜到是与序列化时的类成员有关系: 默认序列化自定义包括关键字transient和静态字段名serialPersistenFieldstransient用于定义哪个字段不被默认序列化,serialPersistenFields用于指定哪些字段需要被默认序列化。如果两者同时定义了就忽略transient

构造方法

在这里插入图片描述

String()

 public String() {
       this.value = "".value;
   }

先看下下面这段代码

Class clazz = "".getClass();
System.out.println(""+"的class是:"+clazz);
Object o = clazz.newInstance();
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
Object o2 = declaredConstructor.newInstance("123123");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field: declaredFields){
    System.out.println("我是由"+""+"声明的对象我有字段:"+field);
}
 
Field value = clazz.getDeclaredField("value");
 
value.setAccessible(true);
Object o1 = value.get(o2);
 
System.out.println(o1 instanceof  char[]);
 
char[] o11 = (char[]) o1;
System.out.println((o11));
value.setAccessible(true);

以上代码其实就是java的反射机制,后来才知道,这个位置其实通过反射机制实现的,可以理解为:

“”.getClass.newInstance() ---->Field.get(“value”) 来拿到field="value"的值。

String(String original)

将字符串常量转为字符串:

String s6 = new String("java");

源码:

public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
  • 在堆中创建一个String类型的结构,这个结构的value和hash和原始的orginal一模一样。也就是是orginal的副本
  • 因为是副本,所以不用担心改变源String会影响目标String的值
  • 用new String()创建的字符串不是常量,不能再编译器就确定。所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

除非需要参数字符串的显式拷贝,否则不需要使用这个构造函数。

String(char value[])

将字符数组转为字符串

例子:

char[] arr3 = {'a','b','c'};

String s4 = new String(arr3);

System.out.println(s4);//abc

源码:

public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
  • 参数是一个char数组,【传递的是地址】
  • 原本堆空间的String结构的value指向的是null,现在会重新创建一个char[],将原来的字符串数组中的内容一一复制到char[]中,然后令String.value = char[]

String(char[] value,boolean share)

String(char[] value,boolean share) {
    // assert share : "unshared not supported";
    this.value = value;
}

String(char[] value)方法对比:

  • 该方法多了一个参数boolean share,这个参数在方法体中并没有使用。那为什么还要这个参数呢?加上这个参数只是为了区分String(char[] value)方法,只有参数不同才能进行重载
  • 具体实现方法不同:String(char[] value)方法在创建String的时候是使用Arrays.copy方法将value的内容全部复制到String中,而这个String(char[] value,boolean share)方法是直接将value的引用赋值给String中的value。也就是说,这个方法构造出来的String和参数中的char[] value共享同一个数组。

为什么Java会提供这样一个方法呢?

  • 性能好: 一个是直接赋值,一个一一拷贝,赋值更快
  • **节约内存:**该方法之所以设置为 protected,是因为一旦该方法设置为公有,在外面可以访问的话,如果构造方法没有对 arr 进行拷贝,那么其他人就可以在字符串外部修改该数组,由于它们引用的是同一个数组,因此对 arr 的修改就相当于修改了字符串,那就破坏了字符串的不可变性。
  • 安全的:对于调用他的方法来说,由于无论是原字符串还是新字符串,其 value 数组本身都是 String 对象的私有属性,从外部是无法访问的,因此对两个字符串来说都很安全。

String(char value[], int offset, int count)

将字符数组的一部分转为字符串

怎么使用

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        char[] a={'a','d','f','r','d','e'};
        String s = new String(a,2,4);
        System.out.println(s); // frde
    }

参数:

  • value:作为字符源的数组
  • offset:char[]数组要复制的起始坐标
  • count:要从char[]数组复制多少个字符到目标数组中

分析:

  • 必须先检查参数是否正确。
    • char[] 为null,或者offset起始偏移量小于0,返回并抛出异常
    • 如果要复制的长度count小于0,抛出异常
    • 如果偏移量offset 大于等于value.length,抛出超出索引长度异常
    • 如果偏移量offset + count 大于等于 value.length,抛出超出索引长度异常
    • 如果count等于0,那么只要偏移量不超过数组索引长度,就直接让this.value = "".value就可以了
  • 如果参数正确,那么调用数组的赋值长度就可以了
// value[]:作为字符源的数组,offset:偏移量、下标从0开始并且包括offset,count:从数组中取到的元素的个数。
public String(char value[], int offset, int count) {
  // 如果偏移量小于0抛出IndexOutOfBoundsException异常
    if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
  // 判断要取的元素的个数是否小于等于0
    if (count <= 0) {
    // 要取的元素的个数小于0,抛出IndexOutOfBoundsException异常
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
    // 在要取的元素的个数等于0的情况下,判断偏移量是否小于等于数组的长度
        if (offset <= value.length) {
      // 偏移量小于等于数组的长度,返回一个空字符串数组的形式
            this.value = "".value;
            return;
        }
    }
    // 如果偏移量的值大于数组的长度减去取元素的个数抛出IndexOutOfBoundsException异常
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
  // 复制元素
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

以上介绍的构建String对象的方式中,基本都是属于操作它内部的字符数组来实现的

总结:当我们使用字符数组char []创建String的时候,会用到Arrays.copyOf方法或者Arrays.copyOfRange方法,这两个方法是将原有字符数组中的内容一一复制到String中的字符数组中,会创建一个新的字符串对象,随后修改的字符数组不会影响新创建的字符串

String类和编码转换

String(int[] codePoints, int offset, int count)

分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。

例子1

        int[] ints = {91,81,97,99,98};
        String str = new String(ints,2,3);
        System.out.println(str);

返回:

acb

例子2

        int[] a = {100, 2312, 12313, 54545, 23432, 22, 65, 78, 99};
        String b = new String(a, 0, a.length);
        System.out.println(b);
dई〙픑守ANc

源码分析请参考

String(byte bytes[])

前面我们已经知道,String底层是一个char[]字符数组,char[]字符数组是以Unicode码来存储的。

byte是网络传输或者存储的序列化形式,所以在很多传输和存储的过程中需将将byte[]数组和String进行相互转换,而byte[]数组和String相互转换就要小心编码问题。

我们在使用 byte[] 构造 String 的时候,如果没有指明解码使用的字符集的话,那么 StringCoding 的 decode 方法首先调用系统的默认编码格式,如果没有指定编码格式则默认使用 ISO-8859-1 编码格式进行编码操作。

作用:

  • 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }

实际调用的是String(byte[] bytes, int offset, int length)

String(byte[] bytes, int offset, int length)

作用:

  • 通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。

例子

       byte[] arr2 = {97,98,99,100,101};

        String s3 = new String(arr2,2,3);

        System.out.println(s3);//cde

源码:

public String(byte bytes[], int offset, int length) {
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(bytes, offset, length);
    }

String(byte[] bytes, int offset, int length, Charset charset)

作用:

  • 通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。

String(byte bytes[], int offset, int length, String charsetName)

https://www.cnblogs.com/xuejianbest/p/10285298.html

从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到value

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

String(byte bytes[], String charsetName)

调用public String(byte bytes[], int offset, int length, String charsetName)构造函数

public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }

方法

在这里插入图片描述

int compareTo(String anotherString)

int compareToIgnoreCase(String str)

作用:

  • 按字典顺序比较两个字符串。

示例:

       String str1 = "Strings";
		String str2 = "Strings";
		String str3 = "Strings123";

		int result = str1.compareToIgnoreCase( str2 );
		System.out.println(result);
	  
		result = str2.compareTo( str3 );
		System.out.println(result);
	 
		result = str3.compareTo( str1 );
		System.out.println(result);

源码:

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

public int compareToIgnoreCase(String str) {
        return CASE_INSENSITIVE_ORDER.compare(this, str);
    }

int compare(String s1, String s2)

 public int compare(String s1, String s2) {
 // 获取s1的长度
            int n1 = s1.length();
            //获取s2的长度
            int n2 = s2.length();
            // 得到较少的长度
            int min = Math.min(n1, n2);
            // 从0开始读取循环比较,比较范围 [0, min)
            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; }
    }

String concat(String str)

拼接字符串,基本上不用了的

public String concat(String str) {
// 获取带拼接的字符串长度
        int otherLen = str.length();
        // 如果带拼接的字符串长度为0就直接返回当前字符串
        if (otherLen == 0) {
            return this;
        }
        // 获取当前字符串的长度
        int len = value.length;
        // 构建一个新的长度为len+otherlen的字符数组,并将当前字符串拷贝到这个字符中
        char buf[] = Arrays.copyOf(value, len + otherLen);
        //   str.getChars底层是System.arraycopy,也就是将str拷贝到buf【偏移量为当前字符串长度】
        str.getChars(buf, len);
        // 构建一个新的String:buf的引用给新String
        return new String(buf, true);
    }

示例:

"aaaa".concat("w3xue")

void checkBounds(byte[] bytes, int offset, int 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 intern()

public native String intern();

存在于.class文件中的常量池,在运行期间被JVM装载,并且可以扩充。String中的intern方法就是扩充常量池的一个方法。当一个String实例str调用intern方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有就返回其引用,如果没有,则在常量池中增加一个Unicode等于Str的字符串并返回它的引用

navtive关键字说明修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言比如C/Cpp之类实现的文件中。
Java语言本身不能对OS底层进行访问好操作,但是可以通过JNI接口调用其他语言来实现对底层的访问
JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。

public static void main(String[] args) {
        String a0 = "Lering string";
        String a1 = new String("Lering string");
        String a2 = new String("Lering string");
        System.out.println(a0 == a1); // false
        a1.intern(); // 这里没有作用,因为它的返回值没有赋给a1
        a2 = a2.intern();
        System.out.println(a0 == a1); //false
        System.out.println(a0 == a1.intern()); //true
        System.out.println(a0 == a2); //true
    }

char charAt(int index)

charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。

参数:

  • index— 字符的索引

返回值:

  • 返回指定索引处的字符

示例:

public static void main(String[] args) {
        String s = new String("www.w3xue.com");
        char result = s.charAt(2);
        System.out.println(result);
    }

结果:

w

源码:

public char charAt(int index) {
// 先检查索引是否越界
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        //然后直接使用索引访问char[]数组
        return value[index];
    }

void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)

将字符串中的字符复制到目标字符数组中。索引包含srcBegin,不包含srcEnd。

参数:

  • srcBegin:字符串中要复制的第一个字符的索引
  • srcEnd: 字符串中要复制的最后一个字符之后的索引
  • dst:目标数组
    dstBegin – 目标数组中的起始偏移量。

返回值

  • 没有返回值,但会抛出 IndexOutOfBoundsException 异常。

示例:

	String Str1 = new String("www.w3xue.com");
		char[] Str2 = new char[6];

		try {
			Str1.getChars(4, 9, Str2, 0);
			System.out.print("拷贝的字符串为:" );
			System.out.println(Str2 );
		} catch( Exception ex) {
			System.out.println("触发异常...");
		}

以上程序执行结果为:

拷贝的字符串为:W3xue

源码:

  • 先检查参数是否正确
  • 然后使用 System.arraycopy复制数组一部分到目标数组中
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
// 起始索引小于0抛出异常
        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);
    }

ps:

* @param      src      the source array. 源数组
* @param      srcPos   starting position in the source array. 源数组的起始位置
* @param      dest     the destination array. 目标数组
* @param      destPos  starting position in the destination data. 目标数组的起始位置
* @param      length   the number of array elements to be copied. 复制的长度
public static native void arraycopy(Object src,int srcPos,Object dest, int destPos,int length)

byte[] getBytes()

使用平台默认的字符集将字符串编码为byte序列,并将结果存储到一个新的byte数组中

返回值:

  • 返回byte数组

例子:

public static void main(String[] args)  {
        String s = new String("abcdefg");
        byte[] str = s.getBytes();
        System.out.println(str + ":");
        for (int i = 0 ; i < str.length; i++){
            System.out.print(str[i] + "\t");
        }
    }

程序结果:

[B@1540e19d:
97	98	99	100	101	102	103	

源码:

public byte[] getBytes() {
        return StringCoding.encode(value, 0, value.length);
    }

byte[] getBytes(Charset charset)

根据指定字符集将String编码为byte序列,并用byte数组存储起来

参数:

  • charsetName – 支持的字符集名称。

返回值:

  • 返回 byte 数组。

示例:

 public static void main(String[] args)  {
        String s = new String("abcdefg");
        try {
            byte[] str = s.getBytes("UTF-8" );
            System.out.print(str + ":");
            for (int i = 0 ; i < str.length; i++){
                System.out.print(str[i] + "\t");
            }

            System.out.println();

            byte[] str1 = s.getBytes("ISO-8859-1");
            System.out.print(str1 + ":");
            for (int i = 0 ; i < str1.length; i++){
                System.out.print(str[i] + "\t");
            }
        } catch (UnsupportedEncodingException e) {
            System.out.println("不支持的字符集");
        }
    }

源码:

public byte[] getBytes(Charset charset) {
        if (charset == null) throw new NullPointerException();
        return StringCoding.encode(charset, value, 0, value.length);
    }

boolean equals(Object anObject)

将字符串与指定的对象比较,只要两者的内容相等会可以了
这个方法重写了Object中的equals方法。方法中的将此字符串与指定对象进行比较

参数:

  • anObject – 与字符串进行比较的对象。

返回值:

  • bool

示例:

    public static void main(String args[]) {
        String Str1 = new String("W3xue");
        String Str2 = Str1;
        String Str3 = new String("W3xue");
        String Str4 = "W3xue";
        boolean retVal;

        retVal = Str1.equals( Str2 );
        System.out.println("返回值 = " + retVal );

        retVal = Str1.equals( Str3 );
        System.out.println("返回值 = " + retVal );

        retVal = Str1.equals( Str4 );
        System.out.println("返回值 = " + retVal );
    }

返回值:

返回值 = true
返回值 = true
返回值 = true

源码:

 public boolean equals(Object anObject) {
 // 如果引用的是同一个对象,返回true
        if (this == anObject) {  //比较地址
             return true;
        }
          // 判断给定的对象是否是String类型的
        if (anObject instanceof String) {
        // 将anObject转为String类型anotherString ,如果anotherString的底层数组长度不相等,返回false
            String anotherString = (String)anObject;
            // 获取当前字符串的长度
            int n = value.length;  
            // 判断给定字符串的长度是否等于当前字符串的长度
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                // 从后往前单个字符比较,如果有不相等,返回假
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                // 如果每个字符都相等,返回真
                return true;
            }
        }
        return false;
    }

boolean equalsIgnoreCase(String anotherString)

public boolean equalsIgnoreCase(String anotherString) {
  // 引用相同返回true。引用不相同进行长度、各个位置上的char是否相同
    return (this == anotherString) ? true
            : (anotherString != null)
            && (anotherString.value.length == value.length)
            && regionMatches(true, 0, anotherString, 0, value.length);
}

boolean startsWith(String prefix)

作用:

  • 字符串是否以指定的前缀开始

参数:

  • prefix: 前缀
  • toffset:字符串中开始查找的位置

返回值:

  • 如果字符串以指定的前缀开始,则返回 true;否则返回 false

例子:

public void test_start(){
        String Str1 = new String("www.w3xue.com");


        System.out.print("返回值 :" );
        System.out.println(Str1.startsWith("www"));  //true

        System.out.print("返回值 :" );
        System.out.println(Str1.startsWith("w3xue", 4)); //true

        System.out.print("返回值 :" );
        System.out.println(Str1.startsWith("w3xue"));  //false
    }

源码:

public boolean startsWith(String prefix) {
        return startsWith(prefix, 0);
    }
 public boolean endsWith(String suffix) {
 // 偏移量: 当前字符串长度 - 后缀字符串长度
        return startsWith(suffix, value.length - suffix.value.length);
    }
 
 public boolean startsWith(String prefix, int toffset) {
 // 同一个引用
        char ta[] = value;
        // 当前字符偏移量
        int to = toffset;
        // 与前缀字符串同一个内存
        char pa[] = prefix.value;
        // 0起始
        int po = 0;
        // 前缀字符串长度
        int pc = prefix.value.length;
        // Note: toffset might be near -1>>>1.
        //  如果 当前字符偏移量小于0【因为数组索引不可能小于0】 或者当前字符偏移量+前缀字符串长度大于当前字符长度,那么就超过索引范围了,因此返回false
        if ((toffset < 0) || (toffset > value.length - pc)) {
            return false;
        }
        // 循环整个前缀字符串长度
        while (--pc >= 0) {
        // 判断当前字符【从偏移量开始】是否和前缀字符串相等
            if (ta[to++] != pa[po++]) {
                return false;
            }
        }
        return true;
    }

boolean matches(String regex)

作用:

  • matches() 方法用于检测字符串是否匹配给定的正则表达式。
    调用此方法的 str.matches(regex) 形式与以下表达式产生的结果完全相同:
    Pattern.matches(regex, str)

参数:

  • regex – 匹配字符串的正则表达式。

返回值:

  • 在字符串匹配给定的正则表达式时,返回 true。

boolean regionMatches(int toffset,String other, int ooffset, int len)

boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len)

作用:

  • 用于检测两个字符串在一个区域内是否相等

参数:

  • ignoreCase – 如果为 true,则比较字符时忽略大小写。
  • toffset:此字符串中子区域的起始偏移量
  • other:字符串参数
  • ooffset – 字符串参数中子区域的起始偏移量。
  • len – 要比较的字符数。

返回值:

  • 如果字符串的指定子区域匹配字符串参数的指定子区域,则返回 true;否则返回 false。是否完全匹配或考虑大小写取决于 ignoreCase 参数。

示例:

    public void test_start(){
        String Str1 = new String("www.w3xue.com");
        String Str2 = new String("W3xue");
        String Str3 = new String("w3xue");

        System.out.print("返回值 :" );
        System.out.println(Str1.regionMatches(4, Str2, 0, 5));  //false

        System.out.print("返回值 :" );
        System.out.println(Str1.regionMatches(4, Str3, 0, 5)); //true 

        System.out.print("返回值 :" );
        System.out.println(Str1.regionMatches(true, 4, Str3, 0, 5));  //true
    }
  • 源码:
public boolean regionMatches(boolean ignoreCase, int toffset,
            String other, int ooffset, int len) {
            // 和value共享一块内存
        char ta[] = value;
        int to = toffset;
        // 和要比较的str共享一颗内存
        char pa[] = other.value;
        int po = ooffset;
        // Note: toffset, ooffset, or len might be near -1>>>1.
        // 如果偏移量小于0或者偏移量+要比较的长度小于字符串长度,就直接返回false
        if ((ooffset < 0) || (toffset < 0)
                || (toffset > (long)value.length - len)
                || (ooffset > (long)other.value.length - len)) {
            return false;
        }
        // 不短读取两个字符串
        while (len-- > 0) {
            char c1 = ta[to++];
            char c2 = pa[po++];
            // 如果相等就进入下次循环
            if (c1 == c2) {
                continue;
            }
            // 如果不相等,如果不忽略大小就直接返回false,否则:
            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;
    }

String replace(char oldChar, char newChar)

作用:

  • 用newChar字符替换字符串中出现过的所有oldChar字符,并返回提缓缓后的新字符

参数:

  • oldChar:原则来的字符
  • newChar:新字符

返回值:

  • 替换之后生成的新字符串

示例:

public void test_start(){
        String Str = "HHaabbc";
        System.out.print("返回值 :" );
        System.out.println(Str.replace('H', 'T'));
        System.out.print("原始字符串 :" );
        System.out.println(Str);
    }

String replaceAll(String regex, String replacement)

String replaceFirst(String regex,String replacement)

  • 使用给定的参数 replacement 替换字符串第一个匹配给定的正则表达式的子字符串

参数:

  • regex – 匹配此字符串的正则表达式。
  • replacement – 用来替换第一个匹配项的字符串。

返回值:

  • 成功则返回替换的字符串,失败则返回原始字符串。
        String Str1 = new String("ww  w3 xue w3 3");


        System.out.print("返回值 :" );

        System.out.println(Str1.replaceFirst("w3", "abab")); //:ww  abab xue w3 3

        System.out.print("原始值 :" );  

        System.out.println(Str1); //ww  w3 xue w3 3

        System.out.print("返回值 :" );
        System.out.println(Str1.replaceAll("w3", "abab")); //ww  abab xue abab 3

        System.out.print("原始值 :" );

        System.out.println(Str1); //ww  w3 xue w3 3

String[] split(String regex)

String[] split(String regex, int limit)

作用:

  • 根据匹配给定的正则表达式来拆分字符串

参数:

  • regex: 正则表达式分隔符
  • limit: 分割的份数

返回值:

  • 成功返回替换的字符串,失败返回原始字符串

示例:

    public void test_start(){
        String Str = "www-to-aa-cc-vfvvd.com";
        System.out.println("返回值:");
        for (String s : Str.split("-", 2)){
            System.out.println(s);
        }

        System.out.println("");
        System.out.println("返回值 :" );
        for (String retval: Str.split("-", 3)){
            System.out.println(retval);
        }
        System.out.println("");
        System.out.println("返回值 :" );
        for (String retval: Str.split("-", 0)){
            System.out.println(retval);
        }
        System.out.println("");
        System.out.println("返回值 :" );
        for (String retval: Str.split("-")){
            System.out.println(retval);
        }
    }
public String[] split(String regex) {
        return split(regex, 0);
    }

public String[] split(String regex, int limit) {
   *****
    }

返回值:

返回值:
www
to-aa-cc-vfvvd.com

返回值 :
www
to
aa-cc-vfvvd.com

返回值 :
www
to
aa
cc
vfvvd.com

返回值 :
www
to
aa
cc
vfvvd.com

String.join()

作用:拼接字符串
例子:

  • joiner.add
public void test_start(){
        StringJoiner joiner = new StringJoiner(",");
        joiner.add("foo");
        joiner.add("bar");
        joiner.add("baz");

        String joinered = joiner.toString();
        System.out.println(joinered);  //foo,bar,baz
    }
        String joinered = new StringJoiner("-").add("fff").add("aaa").add("ccc").toString();
        System.out.println(joinered);  //foo,bar,baz

        String j1 = String.join("/", "2020", "10", "28");
        System.out.println(j1);

        List<String> list = Arrays.asList("foo", "bar", "baz");
        joinered = String.join(";", list); //2020/10/28
        System.out.println(joinered); //foo;bar;baz
List<Person> list = Arrays.asList(
 new Person("John", "Smith"),
 new Person("Anna", "Martinez"),
 new Person("Paul", "Watson ")
);
 
String joinedFirstNames = list.stream()
 .map(Person::getFirstName)
 .collect(Collectors.joining(", ")); // "John, Anna, Paul"

源码:

# 数组方式
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();
    }

// 拼接方法
public StringJoiner add(CharSequence newElement) {
  // prepareBuilder()方法首次调用会创建StringBuilder对象,后面再调用会执行拼接分隔符
    prepareBuilder().append(newElement);
    return this;
}
 
// 未进行拼接创建StringBuilder对象,已经拼接以后value != null执行拼接分隔符
private StringBuilder prepareBuilder() {
  // 判断拼接的value是否为空
    if (value != null) {
    // 不为空执行拼接分隔符
        value.append(delimiter);
    } else {
    // 最开始使用拼接的时候,调用这个方法创建一个空的StringBuilder对象,只调一次
        value = new StringBuilder().append(prefix);
    }
    return value;
}
 
// 上面是调用的这个拼接元素方法
@Override
public StringBuilder append(CharSequence s) {
  // 这里啥都没处理,调用的是父类的append方法,设计模式为建造者模式
    super.append(s);
    return this;
}
 
// 上面的prepareBuilder方法是拼接分隔符,这个方法是将分隔符和给定的元素拼接的方法
@Override
public AbstractStringBuilder append(CharSequence s) {
  // 以下3个判断根据类型和是否为空进行区别拼接
    if (s == null)
        return appendNull();
    if (s instanceof String)
        return this.append((String)s);
    if (s instanceof AbstractStringBuilder)
        return this.append((AbstractStringBuilder)s);
  // 拼接
    return this.append(s, 0, s.length());
}

String trim()

作用:

  • 删除字符串的头尾空白符

  • 参数:

返回值:

  • 删除头尾空白符的字符串

  • 例子:

    public static void main(String args[]) {
        String Str = new String("    acdsa    ");
        System.out.print("删除头尾空白 :" );
        System.out.println( Str.trim() );
        System.out.print("原始值 :" );
        System.out.println( Str );
    }

源码:

public String trim() {
// 获取当前底层数组长度
        int len = value.length;
        int st = 0;
        // char[]val是原来底层数组的引用,他们指向同一个内存
        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;
    }

String toUpperCase()&& String toLowerCase()

这个源码没有看懂

 public void test_start(){
        String Str = new String("    acdsa    ");
        System.out.print("返回值 :" );
        System.out.println( Str.toUpperCase() );
        System.out.print("原始值 :" );
        System.out.println( Str );
    }

返回值 : ACDSA
原始值 : acdsa

String toString()

返回当前对象

public String toString() {
        return this;
    }

例子:

public void test_start(){
        String Str = new String("    acdsa    ");
        System.out.print("返回值 :" );
        System.out.println( Str.toString() );
        System.out.print("原始值 :" );
        System.out.println( Str );
        System.out.println( Str  == Str.toString() );
    }

返回值 : acdsa
原始值 : acdsa
true

public char[] toCharArray()

将字符串转换为字符数组

参数:

返回值:

  • 字符数组

示例:

    public static void main(String args[]) {
        String Str = new String("www.aaaa.com");

        System.out.print("返回值 :" ); // 返回值 :www.aaaa.com
        System.out.println( Str.toCharArray() );
    }

源码:

public char[] toCharArray() {
        // Cannot use Arrays.copyOf because of class initialization order issues
        // 构造一个value.length长度的字符数组
        char result[] = new char[value.length];
        // 苑
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }

valueOf – 常见数据类型转String

valueOf(boolean b): 返回 boolean 参数的字符串表示形式。.

public static String valueOf(boolean b) {
        return b ? "true" : "false";
    }

valueOf(char c): 返回 char 参数的字符串表示形式。

public static String valueOf(char c) {
        char data[] = {c};
        return new String(data, true);
    }

valueOf(char[] data): 返回 char 数组参数的字符串表示形式。
copyValueOf(char data[]):返回 char 数组参数的字符串表示形式。

public static String valueOf(char data[]) {
        return new String(data);
    }
public static String copyValueOf(char data[]) {
        return new String(data);
    } 
         

valueOf(char[] data, int offset, int count): 返回 char 数组参数的特定子数组的字符串表示形式。

  • data – 这是字符数组。
  • offset – 这是最初的子数组的偏移量。
  • count – 这是子数组的长度
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);
    }
public static void main(String[] args) {
  
    // character array
    char[] charArr = { 'C', 'O', 'M', 'P', 'I', 'L', 'E', ' ',
    'O', 'N', 'L', 'I', 'N', 'E' };

    String str = String.copyValueOf(charArr, 8, 6 );

    System.out.println(str);
  }/

valueOf(double d): 返回 double 参数的字符串表示形式。

public static String valueOf(double d) {
        return Double.toString(d);
    }

valueOf(float f): 返回 float 参数的字符串表示形式。

public static String valueOf(float f) {
        return Float.toString(f);
    }

valueOf(int i): 返回 int 参数的字符串表示形式。

public static String valueOf(int i) {
        return Integer.toString(i);
    }

valueOf(long l): 返回 long 参数的字符串表示形式。

public static String valueOf(long l) {
        return Long.toString(l);
    }

valueOf(Object obj): 返回 Object 参数的字符串表示形式。

public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

参数:

  • 指定参数类型

返回值:

  • 删除提问空白符的字符串

例子:

 public static void main(String args[]) {
        double d = 1100.00;
        boolean b = true;
        long l = 1234567890;
        char[] arr = {'w', '3', 'x', 'u', 'e'};

        System.out.println("返回值 : " + String.valueOf(d) );
        System.out.println("返回值 : " + String.valueOf(b) );
        System.out.println("返回值 : " + String.valueOf(l) );
        System.out.println("返回值 : " + String.valueOf(arr) );
    }

format

格式化字符串

源码:

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

CharSequence subSequence(int beginIndex, int endIndex)

作用:

  • 返回一个新的字符序列,它是此序列的一个子序列。

参数:

  • beginIndex – 起始索引(包括)。
  • endIndex – 结束索引(不包括)。

返回值:

  • 返回一个新的字符序列,它是此序列的一个子序列。

例子:

        String Str1 = new String("abcdefg");


        System.out.print("返回值 :" );

        System.out.println(Str1.subSequence(0, 2)); //:ab

        System.out.print("原始值 :" );

        System.out.println(Str1); //abcdefg

源码:

public CharSequence subSequence(int beginIndex, int endIndex) {
        return this.substring(beginIndex, endIndex);
    }

String substring(int beginIndex)

String substring(int beginIndex, int endIndex)

作用:

  • 返回字符串的子字符串

参数:

  • beginIndex – 起始索引(包括)。
  • endIndex – 结束索引(不包括)。

返回值:

  • 子字符串

例子:

        String Str1 = new String("abcdefg");


        System.out.print("返回值 :" );
        System.out.println(Str1.substring(1)); //:bcdefg

        System.out.print("原始值 :" );
        System.out.println(Str1); //abcdefg

        System.out.print("返回值 :" );
        System.out.println(Str1.substring(1, 7)); //:bcdefg

        System.out.print("原始值 :" );
        System.out.println(Str1); //abcdefg

源码:

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

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

indexof

int indexOf(String str, int fromIndex)

int indexOf(String str)

作用:

  • 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

int indexOf(int ch)

作用:

  • 返回指定字符在字符串中第一次出现的索引。如果没有就返回-1

int indexOf(int ch, int fromIndex)

作用:

  • 返回指定字符在字符串中第一次出现的索引【从formIndex开始查找】。如果没有就返回-1

参数:

  • ch:字符
  • fromIndex : 开始搜索的索引位置
  • str – 要搜索的子字符串。

返回值:

  • 指定子字符串在字符串中第一次出现处的索引,从指定的索引开始。
        String Str1 = new String("abcdefgww");


        System.out.print("查找字符 w 第一次出现的位置 :" );
        System.out.println(Str1.indexOf('w'));  //7

        System.out.print("从第3个位置开始查找字符 w 第一次出现的位置 :" );
        System.out.println(Str1.indexOf('w', 3)); // 7

        System.out.print("字符串 ef 第一次出现的位置 :" );
        System.out.println(Str1.indexOf("ef"));  //  4

        System.out.print("从第7个位置开始查找字符串 ef 第一次出现的位置 :" );
        System.out.println(Str1.indexOf("ef"));  //4

具体请参考

查询
Java 在线速查手册
深度分析:面试腾讯,阿里面试官都喜欢问的String源码,看完你学会了吗?
String()分析
Java源码分析,String的设计
JDK源码分析(7)String
https://www.cnblogs.com/yangming1996/p/6850441.html
深入理解Java常用类----String
https://blog.csdn.net/yulungggg/article/details/81039655

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值