String源码解析
来源:互联网 发布:js判断ip能否ping通 编辑:程序博客网 时间:2024/05/21 11:35
String类的声明和定义
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
final关键字的作用
- final修饰类:这个类不能被继承
- final修饰方法:不能被重写
- final修饰属性:此属性就是一个常量,一旦初始化就不可再被赋值。
可以从定义看的出String类是不能被继承,是线程安全的,因为它是final类型的。
String类实现了三个接口,分别为java.io.Serializable, Comparable, CharSequence。
String类的属性
/** The value is used for character storage. */private final char value[];
很多java程序员都知道String值是不可变的,其因由就是该属性造成。String其实就是一个被final关键字修饰的字符数组。关于String值是不可变的疑问,有如下代码:
String str = "abc";str = "cde";
这里str是一个指向字符串”abc”的引用对象,str = “cde”是改变str的引用对象,并没有改变字符串的值。
/** Cache the hash code for the string */private int hash; // Default to 0
由官方注释可以得知,这个属性是用于存放字符串的hash值的,并且其默认值为0。
/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = -6849794470754667710L;/** * Class String is special cased within the Serialization Stream Protocol. * * A String instance is written into an ObjectOutputStream according to * <a href="{@docRoot}/../platform/serialization/spec/output.html"> * Object Serialization Specification, Section 6.2, "Stream Elements"</a> */private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
由于String类实现了Serializable接口,故支持序列化和反序列化。
- Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。
- 如果要将一个java对象序列化,那么对象的类需要是可序列化的。要让类可序列化,那么这个类需要实现如下两个接口:
- Serializable- Externalizable
- 关于对象的序列化,有以下注意事项:
- 对象的类名、Field(包括基本类型、数组及对其他对象的引用)都会被序列化,对象的static Field,transient Field及方法不会被序列化;- 实现Serializable接口的类,如不想某个Field被序列化,可以使用transient关键字进行修饰;- 保证序列化对象的引用类型Filed的类也是可序列化的,如不可序列化,可以使用transient关键字进行修饰,否则会序列化失败。- 反序列化时必须要有序列化对象的类的class文件;当通过文件网络读取序列化对象的时候,必需按写入的顺序来读取。
String类的方法
- String的各个构造方法,各方法具体作用见其方法上的注释。
/** 不含参数的构造函数,虽然这个方法是公开的,但一般没什么用,因为字符串是不可变的。 */public String() { this.value = "".value;}/** 参数为String类型的构造函数 */public String(String original) { this.value = original.value; this.hash = original.hash;}/** 参数为char数组类型的构造函数,使用java.utils包中的Arrays类复制,Arrays.copyOf方法和Arrays.copyOfRange方法, 是将原有的字符数组中的内容逐一的复制到String中的字符数组中 */public String(char value[]) { this.value = Arrays.copyOf(value, value.length); }/** 同上方法注释 */ public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count <= 0) { if (count < 0) { throw new StringIndexOutOfBoundsException(count); } if (offset <= value.length) { this.value = "".value; return; } } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); }/** 参数为byte数组,其作用是从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码, 拷贝到String类的value中,最后一个参数还可以为String类型,Charset类型是1.6版本才开始支持的 */ 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); }/** 调用上一个方法,默认转化整个byte数组 */public String(byte bytes[], String charsetName) throws UnsupportedEncodingException { this(bytes, 0, bytes.length, charsetName); }/** 参数为StringBuffer对象,一般不去使用这个方法,使用StringBuffer的toString方法 */ public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } }/** 参数为StringBuilder对象,一般不去使用这个方法,使用StringBuilder的toString方法 */ public String(StringBuilder builder) { this.value = Arrays.copyOf(builder.getValue(), builder.length()); }/** 一个特殊的私有构造方法,区别于String(char[] value)方法,该方法性能更优,但可能引起内存泄漏,在1.7后的版本基本不再使用, 其并非将字符数组逐一复制,而是直接引用传入的字符数组,这个方法构造出来的String和参数传过来的char[] value共享同一个数组。 */String(char[] value, boolean share) { // assert share : "unshared not supported"; this.value = value;}
- String类的常用方法
- 简单方法,见方法上的注释
//取字符串长度public int length() { return value.length; }//字符串判空public boolean isEmpty() { return value.length == 0; }//取字符串某个位置的字符 public char charAt(int index) { if ((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } return value[index]; }//在本字符串后添加传入的字符串 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);}//去空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;}
- equals方法
public boolean equals(Object anObject) { //如果引用的是同一个对象,返回真 if (this == anObject) { return true; } //如果传入对象不是String类型,返回假 if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; //如果char数组长度不相等,返回假 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; }
- compareTo方法
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; /** 从第一个字符开始到lim处为止,如果存在字符不相等,返回(返回值为对象不相等处字符和被比较对象不相等字符的差值)。 否则,返回两个字符串对象的长度之差 */ while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; }
- hashCode方法
public int hashCode() { int h = hash; //如果hash没有被计算过,并且字符串不为空,则进行hashCode计算 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; }
hashCode的实现其实就是使用数学公式:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。选择数字31的原因,大概率是它是一个质数而且不会太过大(以至于造成数据溢出的情况)。
- substring方法
/** 字符串截取方法 */public String substring(int beginIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } //截取后的长度 int subLen = value.length - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } //截取的字符长度 int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
这里的String(value, beginIndex, subLen)方法会创建一个新的String并返回,虽然会降低一些效率,但避免了内存泄漏的问题。在1.7版本之前,是使用之前讲到的私有构造方法的。
阅读全文
1 0
- String源码解析
- Java String 源码解析
- java String源码解析
- Java String源码解析
- String 源码解析
- Java源码解析:String
- String源码解析
- Java String源码解析
- String类源码解析
- String编码源码解析
- String源码解析
- String 源码解析
- JAVA源码解析-String源码
- Java源码解析-String详解
- String的replzce源码解析
- jdk1.8 String源码解析
- String为什么不可变,String源码解析
- String 源码解析,深入认识String
- 利用余弦定理计算文本相似度
- C/C++整行读入字符串
- jquery获取子元素的方法 find()的用法
- cordova插件汇总
- 【面试】最近某大厂算法工程师面试体会
- String源码解析
- 滴滴国际化项目 Android 端演进
- 二分查找java实现
- Android图片缓存之初识Glide
- Java并发编程实战--笔记二
- JavaScript新的对象创建方式---Object.create()
- 如何在android studio里快速查找dugug签名
- iterable
- Android Launcher3分析——LauncherModel