随笔------浅谈对string的理解

String对象是java中使用比较频繁的一个对象类型,也是我们通常在面试中经常被面试官问到的内容,在极客时间学习了java性能调优实战模块,特写此文章,用于记录自己学习的内容已经对String的理解。
随笔------浅谈对string的理解_第1张图片
这三个输出语句的输出结果是什么,首先先了解一下String的存储原理。

String的存储原理

在java6以及之前的版本中,String的存储对象主要是通过char数组进行封装进行存储的,另外有偏移量offset,字符数量count和hash值这四个属性相互配合存储的。String通过offset和count两个属性来定位char数组,获取字符串,这样可以高效快速的共享String对象,同时节省存储空间,但是,这样做会存在内存泄露的风险。java7到java8,String类做了一些改变,count和offset属性不存在了,并且substring方法不再共享char数组了,这样做的好处是有效避免了内存泄露,因为substring方法的调用使用的new String,这样做会复用原来的char数组,而正常截取的是原字符串中的一小部分,而substring如果一直被引用,它里面的char数组一直指向原字符串,那样原来的字符串就会一直存在,不会被回收而导致内存泄露。java9,工程师将char数组修改成byte数组,因为一个char字符占16位,2个字节,存储单字节编码内的字符就会显得很浪费,修改为byte数组以后,可以节省存储空间,并且还新增了coder属性用于在计算字符串长度或者使用index函数的时候,需要根据这个字段来判断如何计算字符长度,coder 属性默认有 0 和 1 两个值,0 代表 Latin-1(单字节编码),1 代表 UTF-16.

String的不可变性

String的不可变性是通过final和private来保证的,即一旦创建就不可改变,这样做的好处是第一,保证线程安全,第二,保证hash值不会随意变更,确保唯一性,第三是和常量池配合使用。对于初学者来说,常常很困惑,String的不可变性,如String str=“hello”,
str = “world”.最后str的值却是world,这不是改变了吗,其实过程是这样的,第一次在创建字符串的时候,虚拟机会去常量池中寻找是否存在这个字符串,如果存在就返回这个字符串的引用给str,不存在就创建,并把它放入常量池,当第二次给str赋值的时候,hello任然存在内存中,只是str的引用改变了,这才是String的不可变性。这也正是前面比较的输出结果的原理,那三个结果分别是false,false,true。str1会创建并放入到常量池中,str2是通过new创建,存放在堆中,二者的引用指向的是不一样的地方,str3通过了intern方法,这个方法的作用就是在创建的时候查看常量池中是否存在的作用,所以str3和str1指向的是一个引用。

如何在实际应用中声明字符串

我们在实际的使用中常用的是+拼接字符串,其实java虚拟机在底层帮我们做了优化,在拼接的时候自己调用StringBuilder对象对字符串进行拼接,但是如果在循环中拼接的话,每次拼接都会生成一个新的StringBuilder对象,这样就会造成性能问题,所以我们应该在平时做字符串拼接的时候还是要养成显示调用StringBuilder来拼接字符串的习惯,但是入如果涉及到线程安全的话需要使用StringBuffer来对其加锁。

使用intern方法来对字符串存储节省内存

具体的做法就是在每次对字符串进行赋值的时候使用intern方法。若果常量池中有相同的值的话,就会重复使用该对象,返回对象引用,这样一开始的对象就会被回收。在字符串常量池中,默认会将对象放入到常量池中,在字符串变量中,对象会被创建在堆内存中,同时也会在常量池中创建一个字符串对象,复制到堆内存对象中,并返回对内存引用。如果调用intern方法,会去查看字符串常量池中是否有等于该对象的常量,如果没有就会在常量池中新增该对象,并返回该对象的引用,如果有就返回常量池中字符串的引用,堆内存中的原有的对象因为没有引用指向它,就会被垃圾回收器回收。这就是调用intern方法的实现过程。调用intern方法能节省更多的内存空间,但是使用这个方法需要看具体的实现场景。如果项目中对内存空间的要求要高于对于时间的要求,并且存储大量重复的字符串的时候,就可以考虑调用intern方法,使用常量池进行存储。如果对查询的速度要求很高,并且存储的字符串数量比较大,重复率比较低的时候,就不建议使用这宗方式对字符串进行存储了。具体使用哪种方式进行存储还是需要根据实际场景和具体的需求进行取舍。

你可能感兴趣的:(随笔------浅谈对string的理解)