欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 18:06:39
...

注:

以下解析基于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) {
	……
}

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

转  换  符

说    明 

示    例

%s

字符串类型(小写)

"mingrisoft"

%S

字符串类型(大写)

"MINGRISOFT"

%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代表不同的日期与时间转换符

 

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 ……