【面试精讲】String是如何实现的?String源码分析
目录
一、String实现机制
二、String不可变性(使用final修饰)
三、String 和 StringBuilder、StringBuffer 的区别
四、==和equals的区别
五、String创建对象与JVM辨析
六、String源码解析
1、compareTo()
2、 equals()
总结
博主v:XiaoMing_Java
在Java中,String
类是使用最频繁的类之一。由于其不可变性、效率以及如何影响内存使用等方面的特点,String
类成为了Java编程语言的一个核心组成部分。本文深入探讨String
的实现机制、它的源码结构、特点以及用final
修饰的好处。
一、String实现机制
在Java中,String
被设计为不可变(immutable)的对象。这意味着一旦String
对象被创建,它所包含的字符序列就不能被修改。每次对字符串进行操作时(例如拼接、替换字符等),实际上都会创建一个新的String
对象。
从JDK 1.0到JDK 8,String
内部是通过一个char
数组来实现的,保存着所有的字符数据。从JDK 9开始,String
类的内部实现改为使用byte
数组加上一个编码标记(coder
),以更有效地处理不同的字符集,优化内存使用和性能。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {// 用于存储字符串的值private final char value[];// 缓存字符串的 hash codeprivate int hash; // Default to 0
}
自JDK 9起,为了提升空间效率和性能,String
的内部表示发生了变化,使用byte
数组加上一个编码标志字段coder
来存储字符串,如下所示:
// JDK 9及以后的String内部结构
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {// 实际存储字符串内容的byte数组private final byte[] value;// 字符串使用的编码标识(用于区分使用Latin-1还是UTF-16编码)private final byte coder;// 缓存字符串的哈希码private int hash; // 默认为0// 构造方法之一,用于从字符数组初始化字符串public String(char[] value) {// 这里简化了实际的实现细节this.value = StringLatin1.toBytes(value);this.coder = LATIN1;}// length() 方法实现public int length() {return value.length >> coder;}
}
二、String不可变性(使用final修饰)
String
对象一旦被创建,其内容就不能被改变。任何对String
的修改操作都会导致新的String
对象的生成。这个特性带来了以下好处:
安全性:String
常作为参数传递,其不可变性保证了数据不会被意外改变。防止子类改变父类(String
)的行为,确保所有String
实例都具有String
定义的行为。
线程安全:在多线程环境下,String
可以被自由共享而无需同步控制。
高效:因为String
的内容不变,所以其哈希码可以被缓存,提高哈希表操作的效率。性能优化:
简化设计:由于不需要考虑子类的影响,使得String
类的设计更简单、更稳定
三、String 和 StringBuilder、StringBuffer 的区别
String
类重载了+
运算符,使得字符串连接操作非常方便。然而,频繁的字符串连接操作会产生大量临时字符串对象,影响性能。为了解决这个问题,Java引入了StringBuilder
和StringBuffer
两个类,它们允许在单个字符序列上执行可变操作。
因为 String 类型是不可变的,所以在字符串拼接的时候如果使用 String 的话性能会很低,因此我们就需要使用另一个数据类型 StringBuffer,它提供了 append 和 insert 方法可用于字符串的拼接,它使用 synchronized 来保证线程安全。
在 JDK 1.5 有了 StringBuilder,它同样提供了 append 和 insert 的拼接方法,在非并发操作的环境下可使用 StringBuilder 来进行字符串拼接。
四、==和equals的区别
== 对于基本数据类型来说,是用于比较 “值”是否相等的;而对于引用类型来说,是用于比较引用地址是否相同的。
// Object 中的 equals() 方法其实就是 ==
public boolean equals(Object obj) {return (this == obj);
}// 而String重写equals()方法把它修改成比较两个字符串的值是否相等
public boolean equals(Object anObject) {// 对象引用相同直接返回 trueif (this == anObject) {return true;}// 判断需要对比的值是否为 String 类型,如果不是则直接返回 falseif (anObject instanceof 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) {// 如果其中有一个字符不相等就 true false,否则继续对比if (v1[i] != v2[i])return false;i++;}return true;}}return false;
}
五、String创建对象与JVM辨析
String 常见的创建方式有两种,new String() 的方式和直接赋值的方式
直接赋值的方式会先去字符串常量池中查找是否已经有此值,如果有则把引用地址直接指向此值,否则会先在常量池中创建,然后再把引用指向此值;
new String() 的方式一定会先在堆上创建一个字符串对象,然后再去常量池中查询此字符串的值是否已经存在,如果不存在会先在常量池中创建此字符串,然后把引用的值指向此字符串
JDK 1.7 之后把永生代换成的元空间,把字符串常量池从方法区移到了 Java 堆上。
六、String源码解析
1、compareTo()
public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;// 获取到两个字符串长度最短的那个 int 值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) {// 有字符不相等就返回差值return c1 - c2;}k++;}return len1 - len2;
}
2、 equals()
public boolean equals(Object anObject) {// 对象引用相同直接返回 trueif (this == anObject) {return true;}// 判断需要对比的值是否为 String 类型,如果不是则直接返回 falseif (anObject instanceof 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) {// 如果其中有一个字符不相等就 true false,否则继续对比if (v1[i] != v2[i])return false;i++;}return true;}}return false;
}
总结
String
在Java中的地位极其重要,其设计精巧、使用广泛。通过不可变性设计,String
提供了高度的安全性和线程安全,同时final
修饰符进一步确保了其不变契约的稳定性。了解String
的内部实现和特点对于写出高效、安全和易维护的Java代码至关重要。
Java开发者应当熟练掌握String
的使用,并在合适的场景选择使用StringBuilder
或StringBuffer
以优化性能。此外,深入理解String
的设计哲学也有助于开发者设计自己的不可变类,为构建稳定和高效的Java应用打下坚实的基础。
如果本文对你有帮助 欢迎 关注 、点赞 、收藏 、评论, 博主才有动力持续记录遇到的问题!!!
博主v:XiaoMing_Java
📫作者简介:嗨,大家好,我是 小明(小明Java问道之路),互联网大厂后端研发专家,2022博客之星TOP3 / 博客专家 / CSDN后端内容合伙人、InfoQ(极客时间)签约作者、阿里云签约博主、全网 6 万粉丝博主。
🍅 文末获取联系 🍅 👇🏻 精彩专栏推荐订阅收藏 👇🏻
专栏系列(点击解锁)
学习路线(点击解锁)
知识定位
🔥Redis从入门到精通与实战🔥
Redis从入门到精通与实战
围绕原理源码讲解Redis面试知识点与实战
🔥MySQL从入门到精通🔥
MySQL从入门到精通
全面讲解MySQL知识与企业级MySQL实战 🔥计算机底层原理🔥
深入理解计算机系统CSAPP
以深入理解计算机系统为基石,构件计算机体系和计算机思维
Linux内核源码解析
围绕Linux内核讲解计算机底层原理与并发
🔥数据结构与企业题库精讲🔥
数据结构与企业题库精讲
结合工作经验深入浅出,适合各层次,笔试面试算法题精讲
🔥互联网架构分析与实战🔥
企业系统架构分析实践与落地
行业最前沿视角,专注于技术架构升级路线、架构实践
互联网企业防资损实践
互联网金融公司的防资损方法论、代码与实践
🔥Java全栈白宝书🔥
精通Java8与函数式编程
本专栏以实战为基础,逐步深入Java8以及未来的编程模式
深入理解JVM
详细介绍内存区域、字节码、方法底层,类加载和GC等知识
深入理解高并发编程
深入Liunx内核、汇编、C++全方位理解并发编程
Spring源码分析
Spring核心七IOC/AOP等源码分析
MyBatis源码分析
MyBatis核心源码分析
Java核心技术
只讲Java核心技术