从String 聊源码解读
@
你真的了解String吗?之前一篇博客写jvm时,就觉得String可以单独拎出来写一篇博客,毕竟几乎所有的面试都是以String开始的,由此可以延伸出线程安全问题,jvm内存模型等问题。也以此告诫我们,作为一个技术开发人员,时刻需要关注底层的实现,保持刨根问底的好奇心的重要性!
这里提一下解读源码的思路:1.看其实现、继承->2.看其构造方法->3.看其重写的方法->4.了解其其他方法的实现
源码实现
1.以主流的jdk1.8来说,Spring 内部实际存储的结构为char数组,源码如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
//...其他内容省略
}
构造方法
我们先来看看它重要的4个构造方法:
// String 为参数的构造方法
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
// char[] 为参数的构造方法
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
// StringBuffer 为参数的构造方法
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
// StringBuilder 为参数的构造方法
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
可以看到除了String 为参数的构造方法是直接赋值,其他三个方法都是调用Arrays.copyOf()方法复制一份等长的数据,并且StringBuffer考虑到线程安全的问题,使用了synchronized关键字。
Arrays.copyOf()实际上是调用了底层的实现(native本地方法,实际调用了C的方法库,对内存进行读写操作):System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
equals
String 重写了equals() 方法,源码如下:
/* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
if (this == anObject) { // 对象引用相同直接返回true
return true;
}
if (anObject instanceof String) { // 判断值是否为String类型
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
// 把两个值都转为char[] 数组对比
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 循环比对两个字符串的每一个字符
while (n-- != 0) {
// 如果有一个字符不相同就返回false
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
还有一个和equals()比较类似的方法equalsIgnoreCase(),用于忽略字符串大小写后进行字符串比对!
compareTo()方法,用于比较两个字符串,返回int类型,源码:
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;
// 对比每个字符
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
// 有字符不相等就返回差值(隐式转换 a为1 z为26)
return c1 - c2;
}
k++;
}
return len1 - len2;
}
从源码可以看出,compareTo方法会循环对比所有的字符,当连个字符串中有任意一个字符串不相同时,就返回差值。当相等时返回0;(注:equals 和 compareTo只比较字符层面是否相等,不比较对象的引用是否一致)
例如下代码:
String str1 = "java";
String str2 = "java";
String str3 = new String("java");
String str4 = new String("java");
System.out.println(str1==str2); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1.compareTo(str2)); // 0
System.out.println(str2==str3); // false
System.out.println(str2.equals(str3)); // true
System.out.println(str2.compareTo(str3)); // 0
System.out.println(str3==str4); //false
System.out.println(str3.equals(str4)); // true
System.out.println(str3.compareTo(str4)); // 0
可以看出,equals() 和 compareTo() 方法是等价的,唯一的不同是equals(Object),compareTo(String) 参数不同!
其他方法
- indexOf() 查询字符串首次出现的下标位置
- lastIndexOf() 查询字符串最后出现的下标位置
- contain() 查询字符串是否包含另一个字符串
- toLowerCase() 把字符串全部转为小写
- toUpperCase() 把字符串全部转换为大写
- length() 查询字符串长度 (数组查看长度size(),但是前台数组查看长度依然是length(),有时候前端写忘记了关键还不报错!)
- trim() 去掉首位空格
- replace() 替换字符串中某些字符
- split() 把字符串分割并返回字符串数组
- join() 把字符串数组转换为字符串
常见面试题
1.为什么String 要有final修饰?
2.String中StringBuilder 和StringBuffer 有什么区别?
3.String 的intern() 方法有什么含义?
4.String 类型在jvm 中是如何存储的?编译器对String 做了哪些优化?
接下来我们一个个看下这些问题的答案:
1.为了安全和高效的考虑,如果不是final的话,传参和内部指令调用时,它的值被改变了的话可能会引起不可预知的系统崩溃问题,且传参的时候需要重新拷贝一个新值,性能上会有一定损失!
2.StringBuilder 是非线程安全的,StringBuffer是线程安全的,但是考虑了线程安全就兼顾不了性能,在非并发的操作下我们选择StringBuilder来操作字符串的拼接。
3.intern() 方法是将字符串保存到常量池中。
String s1 = "java";
String s2 = s1.intern();
String s3 = new String("java");
String s4 = s3.intern();
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); //false
System.out.println(s3 == s4);// false s3在堆中 s4在常量池中
4.编译器堆代码进行了优化如下:
String s1 = "ja" + "va";
String s2 = "java";
System.out.println(s1 == s2);//true
其中"ja" + "va"被直接编译成了"java".因此s1==s2才成立!
小结:String的面试点基本就在== equals()和StringBuild和StringBuffer这里!还有要问就会问jvm 线程并发了。还是要多看源码,知其然知其所以然!
从String 聊源码解读的更多相关文章
- java.lang.String 类源码解读
String类定义实现了java.io.Serializable, Comparable<String>, CharSequence 三个接口:并且为final修饰. public fin ...
- String、StringBuffer、StringBuilder源码解读
序 好长时间没有认真写博客了,过去的一年挺忙的.负责过数据库.线上运维环境.写代码.Code review等等东西挺多. 学习了不少多方面的东西,不过还是需要回归实际.加强内功,方能扛鼎. 去年学习M ...
- Flask(4)- flask请求上下文源码解读、http聊天室单聊/群聊(基于gevent-websocket)
一.flask请求上下文源码解读 通过上篇源码分析,我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__call__方法返回了app的wsgi_app(en ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
- AFNetworking 3.0 源码解读 总结(干货)(上)
养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...
- AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking
AFNetworking的源码解读马上就结束了,这一篇应该算是倒数第二篇,下一篇会是对AFNetworking中的技术点进行总结. 前言 上一篇我们总结了 UIActivityIndicatorVie ...
- AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization
这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...
- YYModel 源码解读(二)之NSObject+YYModel.h (1)
本篇文章主要介绍 _YYModelPropertyMeta 前边的内容 首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 ...
- AFNetworking 3.0 源码解读 总结
终于写完了 AFNetworking 的源码解读.这一过程耗时数天.当我回过头又重头到尾的读了一篇,又有所收获.不禁让我想起了当初上学时的种种情景.我们应该对知识进行反复的记忆和理解.下边是我总结的 ...
随机推荐
- ggplot2(6) 标度、坐标轴和图例
6.1 简介 标度控制着数据到图形属性的映射.标度将我们的数据转化为视觉上可以感知的东西:例如大小.颜色.位置和形状.标度也为我们提供了读图时所使用的工具:坐标轴和图例. 执行标度的过程分为三步:变换 ...
- appnium适应之配置
一.session #获取包名和acctivename#这个工具在adk包里面aapt.exe dump badging E:\Wandoujia_851097_web_seo_baidu_binde ...
- Python - 面向对象(三)公共变量,受保护变量,私有变量
前言 在Python的类里面,所有属性和方法默认都是公共的:但Python也可以设置受保护.私有类型的变量or方法 受保护类型的变量.方法 一般称为:protected变量 #!/usr/bin/en ...
- C 2015年真题
1.写出程序输出结果 void main() { char p[10]="abc"; char q[]="xyz"; int i,j; i=0; while(* ...
- leetcode面试题 17.16. 按摩师
leetcode面试题 17.16. 按摩师 又一道动态规划题目 动态规划的核心就是总结出一个通行的方程. 但是这道题似乎不太适合使用递归的方式. 所以使用for循环遍历数组. class Solut ...
- 开源项目OEIP 游戏引擎与音视频多媒体(UE4/Unity3D)
现开源一个项目 OEIP 项目实现的功能Demo展示 这个项目演示了在UE4中,接入摄像机通过OEIP直接输出到UE4纹理上,并直接把UE4里的RenderTarget当做输入源通过OEIP里GPU管 ...
- hdu1455 拼木棍(经典dfs)
给定木棍序列,求解能将木棍拼成相同长度的数根长木棍的情况下长木棍长度的最小值. /*hdu1455dfs */ #include<bits/stdc++.h> using namespac ...
- 动态规划-TSP问题-最短超级串
2020-03-03 22:55:08 问题描述: 给定一个字符串数组 A,找到以 A 中每个字符串作为子字符串的最短字符串. 我们可以假设 A 中没有字符串是 A 中另一个字符串的子字符串. 示例 ...
- Hive知识点
1.Hive是一种建立在Hadoop文件系统上的数据仓库架构,并对存储在HDFS中的数据进行分析和管理:(也就是说对存储在HDFS中的数据进行分析和管理,我们不想使用手工,我们建立一个工具把,那么这个 ...
- 什么是CPU load
最近经常收到告警,CPU load大于阈值告警.查看系统的CPU是12核,告警阈值设置的是8.对于CPU load一直有个模糊的概念,具体是什么意思还真搞不明白,趁这个机会好好搞搞究竟. 1.查看CP ...