JDK源码分析--String的一些解析

x33g5p2x  于2021-12-18 转载在 其他  
字(7.8k)|赞(0)|评价(0)|浏览(474)

注:

以下解析基于JDK1.8.0_74

一、实现的3个接口

1**、java.io.Serializable**

Serializable接口是启用其序列化功能的接口。 实现java.io.Serializable 接口的类是可序列化的。

序列化:把对象转换为字节序列的过程称为对象的序列化。

反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

序列化的作用总结:

(1)数据持久化,保存对象的字节序列到本地磁盘或数据库

(2)实现对象以字节流的形式在网络中进行传输

(3)实现对象在进程间的传递

2**、Comparable<String>**

这个接口有一个方法public int compareTo(T o);

String重写了这个方法:

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

(1)取2个String的中较小的长度作为循环比较的次数;

(2)按序比较char的 Unicode value,如果不相等则返回此次序上的Unicode value差值(当前对象减去要比较的对象);

(3)如果按照较小长度循环完成(比较的char完全相同),则返回2个String的长度差值(当前对象的长度减去要比较对象的长度)。

3**、CharSequence**

int length();//String的实现:返回字符数组长度

char charAt(int index);//String的实现:返回下标为index的字符

CharSequence subSequence(int start, int end);//String的实现:调用String的substring(int beginIndex, int endIndex)方法做截取

public String toString();// return this;

Java8添加了2个默认方法:

public default IntStream chars() {

……

}

public default IntStream codePoints() {

……

}

注意,Java8添加的2个方法都使用了“default”修饰。这个关键字也是Java8的一个新特性。default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.(译文:默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。)

也就是说接口中也可以写实现方法了,但需要用default关键字来修饰,实现此接口的类可以直接使用该实现方法,当然,也可以重写这个方法。

这2个方法都是返回的IntStream,可能通过其forEach()方法实现内部元素遍历,具体使用如:

str.chars().forEach(c -> System.out.print(c+" "));

str.codePoints().forEach(c -> System.out.print(c+" "));

对于如println之类的静态方法,也可以使用方法引用来简化Lambda表达式的书写:

str.chars().forEach(System.out::println);

二、重要属性

1、String的实现,就是一个char数组,长度固定

private final char value[];

2、String的hash码

private int hash; // Default to 0

三、构造方法

16个构造方法,其中有2个在Java8中已标注@Deprecated。

1、无参构造,默认为空

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

2、根据已有对象构造一个String实例

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

3、根据数组对象构造String实例;将数组中的元素copy到新实例的value中,而不是直接赋值;Arrays.copyOf方法在之前分析ArrayList源码时提到过,native方法,实际调用C语言的复制函数,效率较高(https://blog.csdn.net/u010188178/article/details/87805325)

public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}

4、类似上面“3”的构造方法,只是从偏移量offset处开始复制,复制的总长度为count,当然错误的传参会抛出异常,这里不细讲。

public String(char value[], int offset, int count) {
    ……
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

5、类似上面“4”的构造方法,只是参数为int数组,需要注意的是保证数组元素为合法的Unicode代码点(Unicode code points)。

public String(int[] codePoints, int offset, int count) {
	……
}

6、以byte数组为入参的构造方法。

public String(byte bytes[], int offset, int length, String charsetName)
    throws UnsupportedEncodingException {
	if (charsetName == null)
    throw new NullPointerException("charsetName");
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(charsetName, bytes, offset, length);
}
public String(byte bytes[], int offset, int length, Charset charset) {
	if (charset == null)
    	throw new NullPointerException("charset");
    checkBounds(bytes, offset, length);
    this.value =  StringCoding.decode(charset, bytes, offset, length);
}
 public String(byte bytes[], String charsetName)
        throws UnsupportedEncodingException {
    this(bytes, 0, bytes.length, charsetName);
}
 public String(byte bytes[], Charset charset) {
    this(bytes, 0, bytes.length, charset);
}
 public String(byte bytes[], int offset, int length) {
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(bytes, offset, length);
}
 public String(byte bytes[]) {
    this(bytes, 0, bytes.length);
}

7、StringBuffer作为入参的构造方法;注意StringBuffer是线程安全的,在使用它的对象来实例化String时,也添加了synchronized关键字以保证其线程安全。

public String(StringBuffer buffer) {
    synchronized(buffer) {
        this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
    }
}

8、StringBuilder作为入参的构造方法

public String(StringBuilder builder) {

this.value = Arrays.copyOf(builder.getValue(), builder.length());

}

9、源码上注释为“包私有构造函数,共享值数组以提高速度”。

其与构造方法public String(char value[])的区别就是:

(1)增加一个实质上无用的boolean参数,因为没有这个参数,方法不能重载;

(2)直接使用“=”赋值,速度更快且共享内存以节约空间。

在String的concat(String str)方法中就是用的这个构造方法返回连接完成后的新String实例。

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

10、其他2个被@Deprecated标注的构造方法,不再赘述

四、常用方法

1**、equals方法**

public boolean equals(Object anObject) {
	if (this == anObject) {
		return true;
	}
	if (anObject instanceof String) {
		String anotherString = (String) anObject;
		int n = value.length;
		if (n == anotherString.value.length) {
			char v1[] = value;
			char v2[] = anotherString.value;
			int i = 0;
			while (n-- != 0) {
				if (v1[i] != v2[i])
					return false;
				i++;
			}
			return true;
		}
	}
	return false;
}

(1)判断为同一对象,true

(2)如果比较的类型不为String或String的子孙类(当然String是final修饰的,没有子类),false

(3)2个String长度不相等,false

(4)循环比较char数组,完全匹配则返回true

2**、format方法**

在使用format方法时曾经遇到过一个坑,其实也不算坑,只是自己无知罢了。大概功能如下:

String url = "http://www.wolfword.com?md5=%S";

String md5 = "12df4dfs5dsf";

想法很好,但通过format方法拼接后,url地址中的md5值全部替换成了大写,当时不清楚为何会这样,于是乎,阅读源码。

public static String format(String format, Object... args) {
    return new Formatter().format(format, args).toString();
}
public Formatter format(String format, Object ... args) {
    return format(l, format, args);
}
public Formatter format(Locale l, String format, Object ... args) {
	……
}

介于能力有限,不一一分析,从网上扒了点资料整理成表供大家参考:

<br>转  换  符<br><br>说    明 <br><br>示    例<br>
<br>%s<br><br>字符串类型(小写)<br><br>"mingrisoft"<br>
<br>%S<br><br>字符串类型(大写)<br><br>"MINGRISOFT"<br>
<br>%c<br><br>字符类型<br><br>'m'<br>
<br>%b<br><br>布尔类型<br><br>true<br>
<br>%d<br><br>整数类型(十进制)<br><br>99<br>
<br>%x<br><br>整数类型(十六进制)<br><br>FF<br>
<br>%o<br><br>整数类型(八进制)<br><br>77<br>
<br>%f<br><br>浮点类型<br><br>99.99<br>
<br>%a<br><br>十六进制浮点类型<br><br>FF.35AE<br>
<br>%e<br><br>指数类型<br><br>9.38e+5<br>
<br>%g<br><br>通用浮点类型(f和e类型中较短的)<br><br>
<br>%h<br><br>散列码<br><br>
<br>%%<br><br>百分比类型<br><br>%<br>
<br>%n<br><br>换行符<br><br>
<br>%tx<br><br>日期与时间类型(x代表不同的日期与时间转换符<br><br>

3**、compareTo方法**

在前面“一.2”中已经讲到。

4**、compareToIgnoreCase方法**

顾名思义,忽略大小写的比较方法。

public int compareToIgnoreCase(String str) {
    return CASE_INSENSITIVE_ORDER.compare(this, str);
}
public static final Comparator<String> CASE_INSENSITIVE_ORDER
                 = new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
            implements Comparator<String>, java.io.Serializable {
	……
}

如上CaseInsensitiveComparator是String的一个内部类,其重写了Comparator接口的compare方法:

public int compare(String s1, String s2) {
    int n1 = s1.length();
    int n2 = s2.length();
    int min = Math.min(n1, n2);
    for (int i = 0; i < min; i++) {
        char c1 = s1.charAt(i);
        char c2 = s2.charAt(i);
        if (c1 != c2) {
            c1 = Character.toUpperCase(c1);
            c2 = Character.toUpperCase(c2);
            if (c1 != c2) {
                c1 = Character.toLowerCase(c1);
                c2 = Character.toLowerCase(c2);
                if (c1 != c2) {
                    // No overflow because of numeric promotion
                    return c1 - c2;
                }
            }
        }
    }
    return n1 - n2;
}

为什么会同时比较UpperCase和LowerCase,网上查了下,为了兼容Georgian字符。

5**、indexOf方法**

public int indexOf(int ch) {
    return indexOf(ch, 0);
}
public int indexOf(int ch, int fromIndex) {
    final int max = value.length;
    if (fromIndex < 0) {
        fromIndex = 0;
    } else if (fromIndex >= max) {
        // Note: fromIndex might be near -1>>>1.
        return -1;
    }

    if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
        // handle most cases here (ch is a BMP code point or a
        // negative value (invalid code point))
        final char[] value = this.value;
        for (int i = fromIndex; i < max; i++) {
            if (value[i] == ch) {
                return i;
            }
        }
        return -1;
    } else {
        return indexOfSupplementary(ch, fromIndex);
    }
}

该方法返回字符串从fromIndex位置开始,出现第一个字符(Unicode 值等于ch)的下标位置,当然,这里的第一个int类型的参数ch,可以传入char类型,这样的代码就更直观。比如“abc”.indexOf(‘a’)。

这里补充一下知识,基础类型的自动转换优先级:char->int->long->float->double。

除了以上方法,还有indexOf(String str)、lastIndexOf(int ch)、lastIndexOf(String str)等

6**、其他常用方法**

Substring、concat、replace、matches、contains、replaceFirst、replaceAll、split、valueOf ……

相关文章