字符串相关类的底层实现原理分析

  • 字符串存储的内存原理
  • ==的比较原理
  • 字符串拼接的储存原理
  • 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站视频>>

posted @ 2022-12-17 10:55  一往而深,  阅读(55)  评论(0编辑  收藏  举报