菜鸟学习日记之String源码阅读

首先看String类的实现和继承。

一,String类的定义和实现

1.String实现了java.io.Serializable接口

这个序列化接口仅用于标识序列化

2.String实现了comparable接口

这个接口里仅实现了一个compareTo方法,用于比较两个实例化对象的大小

3,实现了CharSequence接口,这个其实才是String的核心

String,StringBuffer和StringBuilder本质上都是通过字符数组实现的,而CharSequence就是字符序列。

里面定义了较多我们日常工作使用的方法,比如lengrh()获取字符串的长度,charAt(int i)获取该字符串里下标为i的字符,
其中1.8中新增了两个default方法,一个是chars(),一个是codePoints()

codePoints的翻译为:
返回此序列中的代码点值流。序列中遇到的任何代理项对都将被组合为{@linkplain Character\toCodePoint字符.toCodePoint}作为结果被传递给流。任何其他代码单元,包括普通的BMP字符、未配对的代理项和未定义的代码单元,都会零扩展到{@code int}值,然后传递给流。如果序列在读取流时发生变异,结果会为undefined。

其实也就是chars()和codePoints()这两个方法就是把字符串转换为以char或者codePoint为单位的Stream流。

4,需要注意的是,String不是8种基本数据类型之一,它是一个对象,因为对象的默认值为Null,所以String的默认值也是null,但它又是一种特殊的对象,它有着其他对象都没有的一些特性比如构造方法new String()和new String("")都会返回一个空字符串。

二,内部变量

/** 该值用于字符存储. */

private final char value[];

/**缓存字符串的哈希码 */

private int hash;

/** 使用JDK 1.0.2中的serialVersionUID实现序列化互动性 */

private static final long serialVersionUID = -6849794470754667710L;

/**
* 类字符串是序列化流协议中的特殊大小写.
*
* A String instance is written into an ObjectOutputStream according to
*
* Object Serialization Specification, Section 6.2, “Stream Elements”

*/

private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];

/**
按{@code compareToIgnoreCase}排序{@code String}对象的比较器。这个比较器是可序列化的。

注意,这个比较器没有考虑到语言环境,并且会导致某些语言环境的排序不令人满意。这个java.text语言包提供了collator以允许对区域设置敏感的排序。
*
* @see java.text.Collator#compare(String, String)
* @since 1.2
*/
public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();

由此可以看出String的本质其实就是一个char类的数组。而hash值是String实例化的hashCode的一个hash缓存,因为String经常被用作比较,如果每次比较都要重新计算一次hashcode值的话,无疑效率比较低下,所以保存一个hashcode的缓存无疑能大大优化。CASE_INSENSITIVE_ORDER 其实就是用于忽略大小写的来比较两个字符串比如"String".compareToIgnoreCase(“string”)里就调用该变量。

三,内部类

String源码里只有一个内部类,CaseInsensitiveComparator。
它其实就是上述CASE_INSENSITIVE_ORDER 对象的持有类,作用其实就是忽略大小写比较字符串,但值得注意的还是它里面的代码思想,首先它是一个单例模式的写法,其次它体现了代码复用的思想。

四,关于String中的享元模式(flyweight)

享元模式可以简单的理解为缓存设计(cache),是java设计中对于数据读写的一个优化策略。
常量池指的是在编译期就被确定并被保存于已编译的class文件中的一些数据。它包括了类,方法,接口等等之中的常量。

五,方法

通过阅读String的length,isEmpty(),charAt(int i)的方法,可以看出其实它们就是内部调用数组方法。

1,String的值得注意研究的构造方法

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

该方法为将byte数组从下标为offset的位置开始截取长度为length转换为charsetName的格式编码的字符串

2,trim()

public String trim() {
int len = value.length;
int st = 0;
char[] val = value;
/* avoid getfield opcode */
while ((st < len) && (val[st] <= ’ ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ’ ')) {
len–;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

该方法是讲该字符串的前后空格去掉,日常比较常用

3,compareTo

public int compareTo(String anotherString) {
//自身对象字符串长度len1
int len1 = value.length;
//被比较对象字符串长度len2
int len2 = anotherString.value.length;
//取两个字符串长度的最小值lim
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;

int k = 0;
//从value的第一个字符开始到最小长度lim处为止,如果字符不相等,返回自身(对象不相等处字符-被比较对象不相等字符)
while (k < lim) {
    char c1 = v1[k];
    char c2 = v2[k];
    if (c1 != c2) {
        return c1 - c2;
    }
    k++;
}
//如果前面都相等,则返回(自身长度-被比较对象长度)
return len1 - len2;

}

通过把两个字符串转为Char数组后以循环对比从而比较相同,只要返回结果不等于0就不相同。

4,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,首先利用上述享元模式,查询是否有相同内存地址,如果相同,直接返回相同。
2,如果传入的对象不是String类型,直接返回不相同。
3,如果两个字符串的长度不相同,直接返回不相同。
4,循环对比两个字符串所转换成的字符数组,如果不相等,直接返回不相同。

六,关于String的一些特殊补充

1,String是不可变的。

String a = “a” + “b” + “c”的本质是先生成了三个String对象分别为“a”,“ab"和"ac”,这也是为什么不要用String直接+=,而要用Stringbuffer或者Stringbuilder.

2,关于String为什么不可变
1,允许String对象缓存HashCode的必要
字符串不变保证了hash码的唯一性,所以可以进行缓存,不必每次都去计算新的哈希
2,字符串常量池的存在使得String必须不可变
如果字符串可变,那么常量池将不会稳定,如果某一个String发生变化,那么势必会影响要常量池内其他的字符串对象。
3,线程安全
因为字符串不变,所以它是线程安全的
4,安全
因为在各种基础交互和类库中都引用String作为参数。所以如果String是可变的将会带来各种安全隐患。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值