JDK10源码阅读--String
jdk源码里对String的介绍:
String 是不可变的,一旦被创建其值不能被改变. String buffers 支持可变String.
因为String是不可变的, 所以它们可以被共享.
例如:
String str = "abc";
等价于
char data[] = {'a', 'b', 'c'};
String str = new String(data);
源码中提供的其他使用String的例子:
System.out.println("abc");
String cde = "cde";
System.out.println("abc" + cde);
String c = "abc".substring(2,3);
String d = cde.substring(1, 2);
String的方法包括检查字符串里的单个字符,比较字符串,搜索字符串,提取子字符串,创建字符串副本等.实例映射是基于Character
中指定的Unicode标准.
Java语言为String的字符串连接符 +
提供特殊的支持, +
也可以用于转换其他类型的对象为String.
除非另有说明,否则将null作为参数传递给构造方法或者方法会抛出NullPointerException
异常.
String表示一个字符串通过UTF-16(unicode)格式,补充字符通过代理对(参见Character类的 Unicode Character Representations 获取更多的信息)表示。
索引值参考字符编码单元,所以补充字符在String中占两个位置。
1. String的定义
String不能被继承,因为String类有final修饰符.
同时String实现了3个接口:
java.io.Serializable
:String类可序列化(白话解释: 把原本在内存中的对象状态 变成可存储或传输的过程称之为序列化。序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上. )Comparable<String>
:实现compareTo
方法,String的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort )进行自动排序CharSequence
:char值的一个可读字符序列.
java
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
2. String的属性
所有属性如下图:

value[]
@Stable
private final byte[] value;
value
用来存储字符.(记得jdk8的这个value还是 char数组 不是byte数组. 特意下jdk9看了下,发现jdk9已经是byte数组了.)
从final
关键字也可以看出,String一旦被初始化了就不能被更改.@Stable
表示:表示此字段存储在字段中的第一个非null(相应,非零)值永远不会改变。( value不可能为null,从构造方法可以看出~ )
coder
private final byte coder;
code
是value
中字节的编码标识符,现实中支持的是 LATIN1/UTF16
static final byte LATIN1 = 0;
static final byte UTF16 = 1;
hash
private int hash;
缓存字符串的hashcode, 默认值为0
序列化serialVersionUID
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
String
实现了Serializable
接口,所以支持序列化和反序列化支持。Java的序列化机制是通过在运行时判断类的serialVersionUID
来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID
与本地相应实体(类)的serialVersionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException
)。
COMPACT_STRINGS
static final boolean COMPACT_STRINGS;
static {
COMPACT_STRINGS = true;
}
字符串压缩,该字段值由JVM注入.
3. String的构造方法
String作为java中最长用到的类,有很多重载的构造方法,如下图:

无参构造方法
构造一个空字符串,值得注意的是:因为String是不可变的,所以不必要使用这个构造方法
public String() {
this.value = "".value;
this.coder = "".coder;
}
以String为参数
初始化新创建的String对象,与其参数相同.(其实就是参数字符串的副本)
public String(String original) {
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}
使用字符数组组构造String
public String(char value[]) {
this(value, 0, value.length, null);
}
String(char[] value, int off, int len, Void sig) {
if (len == 0) {
this.value = "".value;
this.coder = "".coder;
return;
}
if (COMPACT_STRINGS) {
// compressedCopy char[] -> byte[]
byte[] val = StringUTF16.compress(value, off, len);
if (val != null) {
this.value = val;
this.coder = LATIN1;
return;
}
}
this.coder = UTF16;
this.value = StringUTF16.toBytes(value, off, len);
}
String(char[] value, int off, int len, Void sig)
是私有构造方法 Void sig
为了消除其他共有构造方法的歧义.
将char[]
值存储到byte[]
中.如果char[]
中仅包含latin1字符,则每个字节代表相应字符的8个低位. 或者是由一个byte[]
存储StringUTF16
中定义的字节序列中的所有字符.
在使用字符数组来创建一个新的String对象的时候,不仅可以使用整个字符数组,也可以使用字符数组的一部分,只要多传入两个参数int offset和int count就可以了,如下:
public String(char value[], int offset, int count) {
this(value, offset, count, rangeCheck(value, offset, count));
}
offset
是第一个字符的索引
count
是子数组的长度.
通过字节数组构造String

jdk10中,String
内部就是用byte[]
存储,上图红框中的构造方法中大同小异,
都是以byte[]
为参数,两个int分别是:其实位置和子byte[]
长度.
参数byte[]
保存到String的byte[]
中需要再次进行解码.String
参数或者Charset
参数是指定的字符集.
public String(byte bytes[], int offset, int length, String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null)
throw new NullPointerException("charsetName");
checkBoundsOffCount(offset, length, bytes.length);
StringCoding.Result ret =
StringCoding.decode(charsetName, bytes, offset, length);
this.value = ret.value;
this.coder = ret.coder;
}
如果没有指定字符集,默认使用ISO-8859-1
进行解码.
static Result decode(byte[] ba, int off, int len) {
String csn = Charset.defaultCharset().name();
try {
// use charset name decode() variant which provides caching.
return decode(csn, ba, off, len);
} catch (UnsupportedEncodingException x) {
warnUnsupportedCharset(csn);
}
try {
return decode("ISO-8859-1", ba, off, len);
} catch (UnsupportedEncodingException x) {
// If this code is hit during VM initialization, err(String) is
// the only way we will be able to get any kind of error message.
err("ISO-8859-1 charset not available: " + x.toString() + "\n");
// If we can not find ISO-8859-1 (a required encoding) then things
// are seriously wrong with the installation.
System.exit(1);
return null;
}
}
还有用StringBuilder
和StringBuffer
作为参数构造String
, 但是基本不这样用,因为StringBuilder
和StringBuffer
都有.toString()
方法.
4. String的一些方法
length
返回字符串的长度
public int length() {
return value.length >> coder();
}
isEmpty
判断字符串是否为空
public boolean isEmpty() {
return value.length == 0;
}
charAt
返回索引处的char值,范围为 0到length()-1
,
序列第一位为0,下一位为1,依此类推
public char charAt(int index) {
if (isLatin1()) {
return StringLatin1.charAt(value, index);
} else {
return StringUTF16.charAt(value, index);
}
}
equals
字符串与指定对象比较,结果为true
表示参数是String
对象并且与此对象有相同的字符序列.
public boolean equals(Object anObject) {
//同一个对象,为真
if (this == anObject) {
return true;
}
if (anObject instanceof String) {//String类型
String aString = (String)anObject;
if (coder() == aString.coder()) {//并且编码相同
//不同的编码,不同的比较方式
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
codePointAt
返回指定索引处的字符(Unicode code值.范围为 0到length()-1
,
序列第一位为0,下一位为1,依此类推
public int codePointAt(int index) {
//latin1字符集
if (isLatin1()) {
checkIndex(index, value.length);
return value[index] & 0xff;
}
int length = value.length >> 1;
checkIndex(index, length);
// utf16字符集
return StringUTF16.codePointAt(value, index, length);
}
//例如:
System.out.println("A".codePointAt(0)); //结果为 65
offsetByCodePoints
通过Unicode code值返回索引偏移位置
public int offsetByCodePoints(int index, int codePointOffset) {
if (index < 0 || index > length()) {
throw new IndexOutOfBoundsException();
}
return Character.offsetByCodePoints(this, index, codePointOffset);
}
getChars
将此字符串中的字符复制到目标字符数组中.
/**
* @param srcBegin要复制的字符串中第一个字符的索引。
* @param srcEnd索引要复制的字符串中的最后一个字符。
* @param dst目标数组。
* @param dstBegin目标数组中的起始偏移量。
* */
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
checkBoundsBeginEnd(srcBegin, srcEnd, length());
checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);
if (isLatin1()) {
StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin);
} else {
StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin);
}
}
getBytes
按照指定的字符集把String转成字节数组返回.
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, coder(), value);
}
参数也可以是java.nio.charset.Charset
或者无参使用默认字符集

contentEquals
字符串与指定的CharSequence进行比较,只有当此字符串与参数有相同的char值序列是返回true.
可以看到方法中根据参数的类型不同而采用不同的比较方法,AbstractStringBuilder
,String
都实现了CharSequence
接口.
这个方法包含了equals(String)
方法,当参数类型为String时,复用equals
方法.
public boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence)sb);
}
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
int n = cs.length();
if (n != length()) {
return false;
}
byte[] val = this.value;
if (isLatin1()) {
for (int i = 0; i < n; i++) {
if ((val[i] & 0xff) != cs.charAt(i)) {
return false;
}
}
} else {
if (!StringUTF16.contentEquals(val, cs, n)) {
return false;
}
}
return true;
}
comepareTo
按字典顺序比较两个字符串。比较基于字符串中每个字符的Unicode值.
如果当前String对象按照字典顺序在参数字符串之前,则结果为负整数.
反之为负整数.
public int compareTo(String anotherString) {
byte v1[] = value;
byte v2[] = anotherString.value;
if (coder() == anotherString.coder()) {
return isLatin1() ? StringLatin1.compareTo(v1, v2)
: StringUTF16.compareTo(v1, v2);
}
return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
: StringUTF16.compareToLatin1(v1, v2);
}
@param toffset此字符串中子区域的起始偏移量。
@param其他字符串参数。
@param ooffset字符串中子区域的起始偏移量
论据。
@param len要比较的字符数。
@return 如果指定该字符串的子区域完全匹配字符串参数的指定子区域返回true,否则返回false;
public boolean regionMatches(int toffset, String other, int ooffset, int len)
从索引toffset
开始的当前字符串的子串 是否以制定的prefix
前缀开头.
public boolean startsWith(String prefix, int toffset)
返回指定子字符串第一次出现的字符串中的索引。
public int indexOf(String str)
返回当前字符串的子串,从索引处开始直到字符串结尾.
public String substring(int beginIndex)
例如:
"unhappy".substring(2) 返回 "happy"
"Harbison".substring(3) 返回 "bison"
"emptiness".substring(9) 返回 "" (an empty string)
返回一个子字符串,子字符串从指定的beginIndex
开始,并扩展到索引endIndex-1
处的字符。 因此子串的长度是endIndex-beginIndex
public String substring(int beginIndex, int endIndex) {
int length = length();
checkBoundsBeginEnd(beginIndex, endIndex, length);
int subLen = endIndex - beginIndex;
if (beginIndex == 0 && endIndex == length) {
return this;
}
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
: StringUTF16.newString(value, beginIndex, subLen);
}
例如:
"hamburger".substring(4, 8) 返回 "urge"
"smiles".substring(1, 5) 返回 "mile"
将参数字符串连接到此字符串的末尾
public String concat(String str)
例如:
"cares".concat("s") 返回 "caress"
"to".concat("get").concat("her") 返回 "together"
返回替换所有出现的字符串.
如果当前字符串中未出现oldChar,则返回当前字符串.
`每次`出现的`oldChar` 都会被`newChar`替换.
public String replace(char oldChar, char newChar)
例如:
"the war of baronets".replace('r', 'y')
返回 "the way of bayonets"
判断此字符串是否与指定字符串匹配
public boolean matches(String regex)
把当前字符串按照`regex`参数拆分,返回一个`String`数组.
数组中的子串按它们在此字符串中出现的顺序排列.
如果匹配不到`regex`则数组中只有一个元素,就是该字符串.
limit 参数控制模式应用的次数,因此影响所得数组的长度。如果该限制 n 大于 0,则模式将被最多应用 n - 1 次,数组的长度将不会大于 n,而且数组的最后一项将包含所有超出最后匹配的定界符的输入。如果 n 为非正,那么模式将被应用尽可能多的次数,而且数组可以是任何长度。如果 n 为 0,那么模式将被应用尽可能多的次数,数组可以是任何长度,并且结尾空字符串将被丢弃。
public String[] split(String regex, int limit)
例如:
String ss = "1,,5,6";
String sss[] = ss.split(",",3);
System.out.println(sss.length);//返回数组长度3,后面的5,6会被合并为一项
把字符串所有字符转成(大写/小写) 返回新的字符串.
public String toLowerCase()
public String toUpperCase()
其他对象转成String
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
public static String valueOf(char data[]) {
return new String(data);
}
public static String valueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
public static String valueOf(boolean b) {
return b ? "true" : "false";
}
public static String valueOf(char c) {
if (COMPACT_STRINGS && StringLatin1.canEncode(c)) {
return new String(StringLatin1.toBytes(c), LATIN1);
}
return new String(StringUTF16.toBytes(c), UTF16);
}
public static String valueOf(int i) {
return Integer.toString(i);
}
public static String valueOf(long l) {
return Long.toString(l);
}
public static String valueOf(float f) {
return Float.toString(f);
}
public static String valueOf(double d) {
return Double.toString(d);
}
返回此字符串的hashCode.
public int hashCode()
hashcode计算公式:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
s[i]是string的第i个字符,n是String的长度。计算机的乘法涉及到移位计算,因为是二进制所以,当一个数乘以2时,就直接拿该数左移一位即可!选择31原因是因为31是一个素数!
在存储数据计算hash地址的时候,我们希望尽量减少有同样的hash地址,所谓“冲突”。如果使用相同hash地址的数据过多,那么这些数据所组成的hash链就更长,从而降低了查询效率!所以在选择系数的时候要选择尽量长的系数并且让乘法尽量不要溢出的系数,因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。
31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化,使用31的原因可能是为了更好的分配hash地址,并且31只占用5bits!
在java乘法中如果数字相乘过大会导致溢出的问题,从而导致数据的丢失.
最近公司和家里的事情都比较多,抽时间看了下String的源码,并不是非常的细致. 其实可以发现,因为String内部两种不同的编码集, 好多主要的代码都在StringLatin1
中和StringUTF16
中.
JDK10源码阅读--String的更多相关文章
- java源码阅读String
1类签名与注释 public final class String implements java.io.Serializable, Comparable<String>, CharSeq ...
- JDK源码阅读--String
public final class String implements java.io.Serializable, Comparable<String>, CharSequence St ...
- 【原】AFNetworking源码阅读(二)
[原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...
- [PHP源码阅读]explode和implode函数
explode和implode函数主要用作字符串和数组间转换的操作,比如获取一段参数后根据某个字符分割字符串,或者将一个数组的结果使用一个字符合并成一个字符串输出.在PHP中经常会用到这两个函数,因此 ...
- 源码阅读系列:EventBus
title: 源码阅读系列:EventBus date: 2016-12-22 16:16:47 tags: 源码阅读 --- EventBus 是人们在日常开发中经常会用到的开源库,即使是不直接用的 ...
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- 如何阅读Java源码 阅读java的真实体会
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比 ...
- SparkConf加载与SparkContext创建(源码阅读一)
即日起开始spark源码阅读之旅,这个过程是相当痛苦的,也许有大量的看不懂,但是每天一个方法,一点点看,相信总归会有极大地提高的.那么下面开始: 创建sparkConf对象,那么究竟它干了什么了类,从 ...
- CI框架源码阅读笔记5 基准测试 BenchMark.php
上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功 ...
随机推荐
- rtf格式 C#设置字间距 CharacterSpacing
richtextbox空间中操作行间距段间距都可以用发送消息解决,但是字间距却鲜有人关注,无法通过PARAFORMAT2消息解决,只能直接操作rtf格式 字间距主要就是要控制 expand expan ...
- 浅谈Java泛型中的? extends E和?super E
https://blog.csdn.net/zymx14/article/details/78073757
- ZooKeeper概念与应用
Zookeeper是开源的分布式协调服务,提供了分布式数据一致性的解决方案. Zookeeper 可用作配置中心和分布式锁服务,在 Dubbo.Kafka.Spark等分布式集群上得到广泛应用. ZN ...
- typeof() 和 GetType()区是什么
1.typeof(x)中的x,必须是具体的类名.类型名称等,不可以是变量名称. 2.GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,它的作用和typeof() ...
- 启动设置mongodb
启动 ①:启动之前,我们要给mongodb指定一个文件夹,这里取名为”db",用来存放mongodb的数据. ②:微软徽标+R,输入cmd,首先找到“mongodb”的路径,然后运行mong ...
- Hive 表类型简述
Hive 表类型简述 表类型一.管理表或内部表Table Type: MANAGED_TABLE example: create table Inner(id int,name string, ...
- Fundebug前端JavaScript插件更新至1.2.0
摘要: Fundebug的前端JavaScript错误监控插件更新至1.2.0:支持监控WebSocket连接错误:修复了监控unhandledrejection错误的BUG,即未用catch处理的P ...
- js keyup、keypress和keydown事件
js keyup.keypress和keydown事件都是有关于键盘的事件 当一个按键被pressed 或released在每一个现代浏览器中,都可能有三种客户端事件. keydown event k ...
- react的生命周期需要知道的。
有关React生命周期: 1.组件生命周期的执行次数是什么样子的??? 只执行一次: constructor.componentWillMount.componentDidMount 执行多次:ren ...
- 【读书笔记】iOS-使用传感器
和其他的高端智能机一样,iPhone携带了很多传感器:照相机,加速度计,GPS模块和数字指南针. 使用Core Motion框架,你的应用可以读取来自于加速度计,磁力计以及陀螺仪的运动数据. 近距离传 ...