字符串相关类的底层实现原理分析
- 字符串存储的内存原理
- ==的比较原理
- 字符串拼接的储存原理
- StringBuilder提高效率的原理
字符串储存的内存原理
对于字符串的储存最大的特点应该就是其字符串不可变了,如果是直接赋值创建的字符串(类似String str="abc")会应用到常量池技术,如果常量池中没有该字符串将会new,如果有将直接使用常量池里的字符串
具体详解参见:字符串内容不可变
==的比较原理
- ==运用于基本数据类型是比较他们的数值
- ==运用于类则是比较他们的地址的
Object类中equals方法的实现源码:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到Object类中的equals方法也是比较其地址的,所有的类如果没有覆写Object类的equals方法,将会默认调用Object类的equals方法实现地址的比较。但是这显然是无法实现对象的比较的,可能有些对象地址不相等,但是其属性全部相等,所以大多数类想属性对象的比较一般会覆写equals方法
参考String2种创建对象方式的内存分析
字符串拼接的底层原理
上述图示说明字符串的2中拼接方式
对于第一种拼接方式,即都是字符串常量的拼接,会触发字符串的优化机制,其在编译阶段就变成了"abc",
即在运行时就是,String s="abc",由于字符串不可变的特性,可见这种方式拼接将会新new出更少的对象
内部原理浅析
我们通过对字符串拼接的源代码进行分析,可以看到+拼接的底层也是通过StringBuilder进行实现的,但是由于其不可变的特点,将会每次拼接一次将会使用一个StringBuilder对象储存,然后将会通过toString方法返回一个新的String对象
可以看到以+的方式进行拼接,每拼接一次将会在内存中创建2个对象,如果拼接的数量一多,将会创建很多的对象,将会带来严重的效率问题,而使用StringBuilder多次拼接也只会有一个对象
在jdk8时,字符串的拼接有所优化,它会先预估拼接后字符串的长度,然后创建一个数组用于储存这些待拼接的字符串,然后将这个数组变成一个String,最后也只用创建一个String,虽然有所优化,但是预估也需要时间,不建议使用
StringBuilder提高效率原理
- StringBuilder的append函数源码
public StringBuilder append(String str) {
super.append(str);
return this;
}
上面的return this; 返回是本类的对象,说明进行一系列操作后返回的还是原来的对象,而没有去重新new一个对象
拼接a b c 三个字符串,都会将他们放在同一个对象里面,而不会重新去new新的对象
实例对比 String StringBuilder StringBuffer 直接拼接 间接拼接的效率问题
public class Main {
private static int time = 50000;
public static void main(String[] args) {
testString();
testStringBuffer();
testStringBuilder();
test1String();
test2String();
}
public static void testString () {
String s="";
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
s += "java";
}
long over = System.currentTimeMillis();
System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
public static void testStringBuffer () {
StringBuffer sb = new StringBuffer();
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
sb.append("java");
}
long over = System.currentTimeMillis();
System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
public static void testStringBuilder () {
StringBuilder sb = new StringBuilder();
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
sb.append("java");
}
long over = System.currentTimeMillis();
System.out.println("操作"+sb.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
public static void test1String () {
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
String s = "I"+"love"+"java";
}
long over = System.currentTimeMillis();
System.out.println("字符串直接相加操作:"+(over-begin)+"毫秒");
}
public static void test2String () {
String s1 ="I";
String s2 = "love";
String s3 = "java";
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
String s = s1+s2+s3;
}
long over = System.currentTimeMillis();
System.out.println("字符串间接相加操作:"+(over-begin)+"毫秒");
}
}
在jdk8字符串拼接采用了新的优化进制,所以直接拼接和间接拼接的效率比String一个一个拼接的效率高
关于这些原理的常见面试题:参见https://www.cnblogs.com/dolphin0520/p/3778589.html
本文参考:https://www.cnblogs.com/dolphin0520/p/3778589.html
<<黑马程序员b站视频>>