详细的String源码解析
我们常常把String类型的字符串作为HashMap的key,为什么要这样做呢?
因为String是不可变的,一旦初始化就不再改变了,如果被修改将会是一个新对象。
@Test
public void testString() {
String s = "Java";
logger.info(s);
System.out.println(s.hashCode());
s = "code";
logger.info(s);
System.out.println(s.hashCode());
}
//输出结果:
/* 15:12:22.453[main] INFO base.AnalString -Java
2301506
15:12:42.794[main] INFO base.AnalString -code
3059181*/
从输出结果来看是s的值已经被改变了,但是在debug的过程中我们可以发现,s的引用指向一个新的String这个String就是“code”.
这时我们从源码中一探究竟。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
- String被final修饰,说明String类绝不可能被继承,也就说任何对String的操作方法,都不会被继承重写。
- String中保存的数据是一个被final修饰的字符数组value。也就是说value一旦被赋值成功后,内存地址是无法改变的。并且这个字符数组的访问权限是private,外部绝对访问不到,String也没有开放出对value进行赋值的方法,所以说value一旦产生,内存地址根本无法修改。
因为String具有不可变性,所以String的大多数操作方法,都会返回新的String。
编码问题
来写一个demo
@Test
public void testEncoding() throws UnsupportedEncodingException {
String str = "你好,大涛";
byte[] bytes = str.getBytes("ISO-8859-1");
//bytes 数组转换成字符串
String s2 = new String(bytes);
logger.info(s2);
//结果:?????
//再纠正一下
String s3 = new String(bytes,"ISO-8859-1");
logger.info(s3);
//?????
//咋还不对,全部改为UTF-8编码
byte[] strBytes = str.getBytes("UTF-8");
String s4 = new String(strBytes,"UTF-8");
System.out.println(s4);
//结果:你好,大涛
//唉,舒服了
}
这里说一下为啥ISO-8859-1编码方式不行呢?主要是因为他不支持中文编码,导致中文会显示乱码。唯一的解决方法是换成UTF-8编码。
常用的字符串操作方法
substring()字符串截取
substring有两个方法。
String substring(int beginIndex)
String substring(int beginIndex,int endIndex)
这两内部实现基本一样,只有一个参数的那个,直接从开始的下标截取到字符串长度-1.
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);
}
//如果开始下标是0并且结束下标是长度-1,直接返回原先字符串;否则再new一个字符串,value是字符数组
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
new String(value, beginIndex, subLen); 来看看这个构造方法是如何实现的。
字符串的相等判断
判断方式有两种,一种是equals方法,另一种是忽略大小写判断相等,equalsIgnoreCase。
来看一下equals的源代码。
public boolean equals(Object anObject) {
//如果对象的内存地址相等直接返回true
if (this == anObject) {
return true;
}
//再判断她得是String类型,不是的话,返回false
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
//如果字符串长度不等,返回false
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
//循环依次判断每个字符是否相等,如果有字符不等,返回false
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
//循环结束,长度相等,类型相等,每个字符页相等,返回true
return true;
}
}
return false;
}
字符串替换
replace(char oldChar, char newChar)方法,是较为常用的字符串替换方法,传入一个旧的字符,再传入一个新的字符,接下来看看源码中是怎样实现的?
public String replace(char oldChar, char newChar) {
//开始先判断新的字符和旧的字符是不是一样的,如果一样的话就不用替换,直接返回原先的字符串
if (oldChar != newChar) {
//字符串长度
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
//val字符数组
//找到第一个将要替换的字符,break,跳出循环
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;
}
以上是博主对String源码的一些理解,如果以上有理解有误的地方还请大家多多指出,共同学习!
详细的String源码解析的更多相关文章
- 超详细的Eureka源码解析
Eureka简介 Eureka是什么? Eureka是基于REST(Representational State Transfer)服务,主要以AWS云服务为支撑,提供服务发现并实现负载均衡和故障转移 ...
- 超详细的Ribbon源码解析
Ribbon简介 什么是Ribbon? Ribbon是springcloud下的客户端负载均衡器,消费者在通过服务别名调用服务时,需要通过Ribbon做负载均衡获取实际的服务调用地址,然后通过http ...
- String源码解析(二)
方法的主要功能看代码注释即可,这里主要看函数实现的方式. 1.getChars(char dst[], int dstBegin) /** * Copy characters from this st ...
- Java String源码解析
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { ...
- String,StringBuffer和StringBuilder源码解析[基于JDK6]
最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西.其实,JDK的源码是越读越有味的.下面总结一下 ...
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
- Java 集合系列07之 Stack详细介绍(源码解析)和使用示例
概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...
- Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...
- Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...
随机推荐
- 微信小程序日期转换、比较、加减
直接上干货: 在utils目录下新建一个dateUtil.js,代码如下:(在需要用的地方引入这个js,调用相关方法传入对应参数就可以使用了) 该工具脚本,实用性很高,通用于各类前端项目,熟悉后亦可以 ...
- 学好Spark/Kafka必须要掌握的Scala技术点(三)高阶函数、方法、柯里化、隐式转换
5. 高阶函数 Scala中的高阶函数包含:作为值的函数.匿名函数.闭包.柯里化等,可以把函数作为参数传递给方法或函数. 5.1 作为值的函数 定义函数时格式: val 变量名 = (输入参数类型和个 ...
- Vue-router hash模式微信登录授权验证,#号处理
微信授权登录验证会把网址中的#号去掉,这样在跳转的时候Vue拿不到Code.所以做了以下处理 let href = window.location.href; if (href.includes(&q ...
- MySQL数据库死锁分析
背景说明: 公司内部一套自建分布式交易服务平台,在POC稳定性压力测试的时候出现了数据库死锁.(InnoDB引擎)由于保密性,假设是app_test表死锁了. 现象: 发生异常:Deadlock fo ...
- 网站开发学习Python实现-Django项目部署-介绍(6.2.1)
@ 目录 1.第一步:找源码 2.第二步:在windows中更改代码 2.第三步:同步到linux中 3.第三步:部署 4.第四步:运行 关于作者 1.第一步:找源码 从github上找一个djang ...
- java_day_02
一.return的两个作用 1.停止当前方法 2.将后面的结果数据返回值还给调用处 二.方法的三种调用格式 1.单独调用:方法名(参数): public class Method { public s ...
- ElasticSearch 史上最全文章
老规矩,本篇文章 不做 ElasticSearch 的 编码讲解 ,只介绍 文章学习的一些优秀文章 重点在于不要循规蹈矩,教程 这样走,你不一定要按他这样走,按自己的方式来,学习效率会更高,网上的教程 ...
- 2020 .NET 开发者峰会顺利在苏州落幕,相关数据很喜人以及线上直播回看汇总
在2019年上海中国.NET开发者大会的基础上,2020年12月19-20日 继续以"开源.共享.创新" 为主题的第二届中国 .NET 开发者峰会(.NET Conf China ...
- Python爬取热搜存入数据库并且还能定时发送邮件!!!
一.前言 微博热搜榜每天都会更新一些新鲜事,但是自己处于各种原因,肯定不能时刻关注着微博,为了与时代接轨,接受最新资讯,就寻思着用Python写个定时爬取微博热搜的并且发送QQ邮件的程序,这样每天可以 ...
- rocketMq broker.conf全部参数解释
#4.7.1版本 #所属集群名字brokerClusterName=rocketmq-cluster#broker名字,名字可重复,为了管理,每个master起一个名字,他的slave同他,eg:Am ...