Java String的部分源码学习
2016-03-05 16:04
423 查看
String类的源码比较简单,但也还是包含了很多的方法,这里挑常见的部分写写自己的理解。
String是一个final类,所以我们不能继承String类,也不能对其中的方法进行改写。String有一个私有的final字符数组,用来保存我们的字符串,另一个私有int变量hash,用来保存hashcode。所以,我们可以知道,大部分对于String的操作,其实都是对数组进行操作,而字符数组被声明为final,故我们不能对它进行改写,一旦,我们的String对象的内容有所变化,很多都是重新生成了一个字符数组。
String类的构造函数有很多,包括无参数的构造函数,参数为String对象的构造函数,参数为数组的构造函数,使用数组的部分内容来构造,以及通过Unicode,ASCII,StringBuffer,StringBuilder来构造,等等。
以上代码可以看出:
如果使用使用无参的构造函数,例如,String str = new String();那么得到的String对象中的数组长度是0
如果使用String对象来初始化,那么,初始化后的String对象和原来的String对象的hash是一样的。
如果使用数组来初始化,最后得到的String对象的内容是对原来数组的拷贝。
那么,如果使用StringBuffer和StringBuilder来初始化,又是什么情况呢?
这两种方法构造出来的String对象同原来的String对象也不是同一个引用,而且,与StringBuffer和StringBuilder的特性一致,StringBuffer对buffer对象使用sychronized,是线程安全的。
String可以通过length方法来获取字符串的长度,它的实现就是返回了内部字符数组的length属性值。
同理,如何判断一个String对象是不是空呢,即判断字符数组的长度是否为空。
下面看一下String是如何实现equal方法的。
equal的对比,首先,判断是否是同一个对象的引用。如果传入的参数的类型也是String,那么再对比两个字符串的长度,如果长度相同,再通过循环来比较每一个数组上每一个位置的值是否相同。String还提供了一个contentEquals方法来对比字符串和一个CharSequence的内容是否一致。
在比较两个字符串方面,有compareTo和compareToIgnoreCase两个方法,顾名思义,前者会严格对比每一个字符是不是一致,而后者会忽略大小写的影响。
如何忽略大小写呢,如果当同一位置的字符不相等的时候,同时转成大写或小写,再进行比较。(不过,为什么转大写之后,如果不等为什么还会尝试转一次小写呢?)
上面列出了String的hashcode算法。这里采用的是直接寻址法,RSHash。
通常情况,我们会把String,StringBuilder,StringBuffer做比较,得出的结论是,如果事后要对String尾部进行添加操作,String是最慢的。这里,可以看下String的具体实现。
可以看出,实际上是重新声明了一个长度为两者之和的数组,然后将value拷贝进去,最后用这个数组去初始化一个String对象,所以,最后的结果是会新生成一个String对象,并将新的引用返回。比起另两种直接在数组上操作的方法相比,new一个对象的开销明显更大。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 }
String是一个final类,所以我们不能继承String类,也不能对其中的方法进行改写。String有一个私有的final字符数组,用来保存我们的字符串,另一个私有int变量hash,用来保存hashcode。所以,我们可以知道,大部分对于String的操作,其实都是对数组进行操作,而字符数组被声明为final,故我们不能对它进行改写,一旦,我们的String对象的内容有所变化,很多都是重新生成了一个字符数组。
String类的构造函数有很多,包括无参数的构造函数,参数为String对象的构造函数,参数为数组的构造函数,使用数组的部分内容来构造,以及通过Unicode,ASCII,StringBuffer,StringBuilder来构造,等等。
public String() { this.value = new char[0]; } public String(String original) { this.value = original.value; this.hash = original.hash; } public String(char value[]) { this.value = Arrays.copyOf(value, value.length); }
以上代码可以看出:
如果使用使用无参的构造函数,例如,String str = new String();那么得到的String对象中的数组长度是0
如果使用String对象来初始化,那么,初始化后的String对象和原来的String对象的hash是一样的。
如果使用数组来初始化,最后得到的String对象的内容是对原来数组的拷贝。
那么,如果使用StringBuffer和StringBuilder来初始化,又是什么情况呢?
public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } } public String(StringBuilder builder) { this.value = Arrays.copyOf(builder.getValue(), builder.length()); }
这两种方法构造出来的String对象同原来的String对象也不是同一个引用,而且,与StringBuffer和StringBuilder的特性一致,StringBuffer对buffer对象使用sychronized,是线程安全的。
String可以通过length方法来获取字符串的长度,它的实现就是返回了内部字符数组的length属性值。
public int length() { return value.length; }
同理,如何判断一个String对象是不是空呢,即判断字符数组的长度是否为空。
public boolean isEmpty() { return value.length == 0; }
下面看一下String是如何实现equal方法的。
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; }
equal的对比,首先,判断是否是同一个对象的引用。如果传入的参数的类型也是String,那么再对比两个字符串的长度,如果长度相同,再通过循环来比较每一个数组上每一个位置的值是否相同。String还提供了一个contentEquals方法来对比字符串和一个CharSequence的内容是否一致。
在比较两个字符串方面,有compareTo和compareToIgnoreCase两个方法,顾名思义,前者会严格对比每一个字符是不是一致,而后者会忽略大小写的影响。
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; } } }
如何忽略大小写呢,如果当同一位置的字符不相等的时候,同时转成大写或小写,再进行比较。(不过,为什么转大写之后,如果不等为什么还会尝试转一次小写呢?)
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
上面列出了String的hashcode算法。这里采用的是直接寻址法,RSHash。
通常情况,我们会把String,StringBuilder,StringBuffer做比较,得出的结论是,如果事后要对String尾部进行添加操作,String是最慢的。这里,可以看下String的具体实现。
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); }
可以看出,实际上是重新声明了一个长度为两者之和的数组,然后将value拷贝进去,最后用这个数组去初始化一个String对象,所以,最后的结果是会新生成一个String对象,并将新的引用返回。比起另两种直接在数组上操作的方法相比,new一个对象的开销明显更大。
相关文章推荐
- 使用Maven创建Java web工程
- Java Web学习(四)
- LeetCode : Reverse Integer [java]
- Java复习笔记(三)——文档注释
- win10怎么安装JDK8,怎么配置JDK8的环境变量
- eclipse快捷键
- Eclipse使用入门教程
- java中带参无返回值方法的使用
- eclipse中的配详解置文件
- Java设计模式之建造者模式
- Java之矩阵求特征值
- java final static public private protected关键字
- java中jar包反编译以及遇到转换失败时的对策
- java自动内存管理的偏爱
- Spring MVC入门
- Java设计模式之抽象工厂模式
- Spring JdbcTemplate Exception摘记
- Java并发编程笔记 并发概览
- java静态块的执行顺序
- Java学习——开端