JDK源码分析--String的一些解析
注:
以下解析基于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 ……
上一篇: Android的延时操作(更新中)
推荐阅读
-
Python的socket模块源码中的一些实现要点分析
-
Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析
-
Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
-
Mybaits 源码解析 (六)----- 全网最详细:Select 语句的执行过程分析(上篇)(Mapper方法是如何调用到XML中的SQL的?)
-
JDK1.8中的ConcurrentHashMap源码分析
-
MyBatis源码分析之——配置解析创建SqlSessionFactory的过程
-
基于FFmpeg源码分析TS数据格式的解析
-
JDK源码分析--String的一些解析
-
一些布局源码的分析
-
jQuery源码中的一些细节分析