JDK1.8源码学习-String
JDK1.8源码学习-String
目录
一、String简介
String类是Java中最常用的类之一,所有字符串的字面量都是String类的实例,字符串是常量,在定义之后不能被改变。
二、定义
public final class String implements java.io.Serializable, Comparable<String>, CharSequence{}
1.String类是由final修饰的,表明String类不能被继承,并且String类中的成员方法都默认是final方法。
2.String类是由final修饰的,表明String类一旦创建,就无法被改变,对String对象的任何操作都不会影响到原对象,任何的change操作都会产生新的String对象。
3.java.io.Servializable,标识序列化,Comparable<String>中只有一个compareTo(T o)方法,用于两个实例化对象比较大小,CharSequence表示一个char值的可读序列,CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。
三、属性
private final char value[]; private int hash; // Default to 0 private static final long serialVersionUID = -6849794470754667710L; private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
1.String的内容本质上是使用不可变的char类型的数组来存储的。
2.hash是String实例化对象的hashcode的一个缓存值,这是因为String对象经常被用来进行比较,如果每次比较都重新计算hashcode值的话,是比较麻烦的,保存一个缓存值能够进行优化。
3.serialVersionUID为序列化ID.
4.serialPersistentFields属性用于指定哪些字段需要被默认序列化,具体用法为:
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("name", String.class),
new ObjectStreamField("age", Integer.Type)
}
transient用于指定哪些字段不会被默认序列化,两者同时使用时,transient会被忽略。
四、构造方法
4.1、无参构造函数
public String() {
this.value = "".value;
}
空字符串" "
4.2、参数为String类型
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
其实就是一个克隆的过程,但是String是不可变的,所以没有太多必要。
4.3、参数为char数组
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
传入一个字符数组,将该数组拷贝一份给value。
4.4、参数为char数组
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);
}
从char数组中的offset位置开始,截取count个字符,拷贝到value。
4.5、参数为bytes数组
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);
}
从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到value
五、创建String对象
1.直接使用" ",也就是使用"字面量"赋值
String name = "张三";
2.使用连接符"+"来赋值
String name = "张" + "三";
3.使用关键字new来创建对象
String name = new String("张三");
六、常用方法
6.1、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;
}
这里重写了Object中的equals方法,用来判断两个对象实际意义上是否相等
6.2、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;
}
用于比较两个字符串的大小,如果两个字符串长度相等则返回0,如果长度不相等,则返回当前字符串的长度减去被比较的字符串的长度。
6.3、hashCode方法
public int hashCode() {
int h = hash;
//如果hash没有被计算过,并且字符串不为空,则进行hashCode计算
if (h == 0 && value.length > 0) {
char val[] = value; //计算过程
//s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
//hash赋值
hash = h;
}
return h;
}
这里重写了hashCode方法,采用多项式进行计算,可以通过不同的字符串得到相同的hash,所以两个String对象的hashCode相同,并不代表两个String是相同的。
算法:假设n = 3
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
//如果起始地址小于0或者(起始地址+所比较对象长度)大于自身对象长度,返回假
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
//从所比较对象的末尾开始比较
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
} public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
} public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
startsWith和endWith方法也是比较常用的方法,常用来判断字符串以特定的字符开始或结尾。
6.5、concat方法
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);
}
concat方法用于将指定的字符串参数连接到字符串上。
6.6、replace方法
public String replace(char oldChar, char newChar) {
//新旧值先对比
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; //找到旧值最开始出现的位置
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
//从那个位置开始,直到末尾,用新值代替出现的旧值
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
replace的参数是char和charSequence,即可以支持字符的替换,也支持字符串的替换(charSequence即字符串序列的意思)
replaceAll的参数是regex,即基于规则表达式的替换,比如可以通过replaceAll("\\d","*")把一个字符串所有的数字字符都替换成星号;
相同点:都是全部替换,即把源字符串中的某一字符或者字符串全部替换成指定的字符或者字符串。
不同点:replaceAll支持正则表达式,因此会对参数进行解析(两个参数均是),如replaceAll("\\d","*"),而replace则不会,replace("\\d","*")就是替换"\\d"的字符串,而不会解析为正则。
6.7、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;
}
trim用于删除字符串的头尾的空格。
JDK1.8源码学习-String的更多相关文章
- JDK1.8源码学习-Object
JDK1.8源码学习-Object 目录 一.方法简介 1.一个本地方法,主要作用是将本地方法注册到虚拟机中. private static native void registerNatives() ...
- JDK1.8源码学习-HashMap
JDK1.8源码学习-HashMap 目录 一.HashMap简介 HashMap 主要用来存放键值对,它是基于哈希表的Map接口实现的,是常用的Java集合之一. 我们都知道在JDK1.8 之前 的 ...
- JDK源码学习--String篇(二) 关于String采用final修饰的思考
JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...
- JDK1.8源码学习-LinkedList
JDK1.8源码学习-LinkedList 目录 一.LinkedList简介 LinkedList是一个继承于AbstractSequentialList的双向链表,是可以在任意位置进行插入和移除操 ...
- JDK1.8源码学习-ArrayList
JDK1.8源码学习-ArrayList 目录 一.ArrayList简介 为了弥补普通数组无法自动扩容的不足,Java提供了集合类,其中ArrayList对数组进行了封装,使其可以自动的扩容或缩小长 ...
- Java JDK1.8源码学习之路 2 String
写在最前 String 作为我们最常使用的一个Java类,注意,它是一个引用类型,不是基本类型,并且是一个不可变对象,一旦定义 不再改变 经常会定义一段代码: String temp = " ...
- 源码学习-String类
最近在扫描CodeDex时报了一个不能使用String.intern()的字符串来做锁对象的告警,对这个问题有疑问查了些资料,顺便学习一下String类的源码. 1.类定义 String 被final ...
- Java JDK1.8源码学习之路 1 Object
写在最前 对于一个合格的后端程序员来说,现行的流行框架早已经能胜任基本的企业开发,Springboot 任何的框架都把重复的工作更佳简单/优化的解决掉,但是完全陷入在这样的温水里面, 好比温水煮青蛙, ...
- JDK源码学习--String篇(四) 终结篇
StringBuilder和StringBuffer 前面讲到String是不可变的,如果需要可变的字符串将如何使用和操作呢?JAVA提供了连个操作可变字符串的类,StringBuilder和Stri ...
随机推荐
- 完美解决pycharm 不显示代码提示问题
pycharm 不显示代码提示 1.检查IDE省电模式是否关闭状态!!! file → power save mode 取消掉 2.检查代码提示是否成功开启. setting → Inspection ...
- Kubernetes中强制删除Pod、namespace
Kubernetes中强制删除Pod.namespace 解决方法 可使用kubectl中的强制删除命令 # 删除POD kubectl delete pod PODNAME --force --gr ...
- CSS样式基础2
CSS: 一.常用样式:字体,颜色,背景 二.布局:浮动 定位 标签特性 三.标签盒子模型: 边距 边框 四.动画:旋转 渐变 注意:子标签会继承父标签的样式但不是所有的样式都会被继承. 1.1 ...
- PHP filectime() 函数
定义和用法 filectime() 函数返回指定文件的上次修改时间. 该函数将检查文件的日常修改情况和 inode 修改情况.inode 修改情况是指:权限的修改.所有者的修改.用户组的修改或其他元数 ...
- P4491 [HAOI2018]染色 广义容斥 NTT 生成函数
LINK:染色 算是比较常规的广义容斥. 算恰好k个 可以直接转成至少k个. 至少k个非常的好求 直接生成函数. 设\(g_k\)表示至少有k个颜色是满足的 那么有 \(g_k=C(m,k)\frac ...
- P4274 [NOI2004]小H的小屋 dp 贪心
LINK:小H的小屋 尽管有论文 但是 其证明非常的不严谨 结尾甚至还是大胆猜测等字样... 先说贪心:容易发现m|n的时候此时均分两个地方就是最优的. 关于这个证明显然m在均分的时候的分点一定是n的 ...
- windows:shellcode 原理
shellcode,一段短小精干的代码,放在任何地方都能执行,不依赖当前所处环境,那么就有这么几点要求: 不能有全局变量:函数里的局部变量在栈空间,地址是执行的时候动态分配的:但全局变量在编译时,会由 ...
- 解决 IntelliJ IDEA占用C盘过大空间问题
原文地址:https://blog.csdn.net/weixin_44449518/article/details/103334235 问题描述: 在保证其他软件缓存不影响C盘可用空间的基础上,当我 ...
- 100% 展示 MySQL 语句执行的神器-Optimizer Trace
在上一篇文章<用Explain 命令分析 MySQL 的 SQL 执行>中,我们讲解了 Explain 命令的详细使用.但是它只能展示 SQL 语句的执行计划,无法展示为什么一些其他的执行 ...
- 嵌入式Linux串口编程简介
文章目录 简介 用到的API函数 代码 简介 嵌入式Linux下串口编程与Linux系统下的编程没有什么区别,系统API都是一样的.嵌入式设备中串口编程是很常用的,比如会对接一些传感器模块,这些模块大 ...