高效的使用字符串,可以提升系统的整体性能。
从以下方面入手学习:String对象的实现、特性、实际使用中的优化
面试题:以3种方式创建对象,两两比较是否相等
一、实现方式
1、java6及之前版本
成员变量:4个,char[]数组、offset偏移量、count字符数量、hash哈希值
优点,通过offer + count 定位 char[]数组。高效、快速共享数组对象、节约内存
缺点,可能导致内存泄漏
2、java7、java8
成员变量:2个,char[]数组、hash哈希值
优点,1、减少2个变量,占用内存减少。2、String.substring()不共享char[]数组,避免该方法导致的内存泄漏
解决,jdk6及之前版本中,内存泄漏的问题
3、java9及之后版本
成员变量:3个,byte[]数组、hash哈希值、coder编码格式
coder用于判断字符串长度,如length/indexOf()。coder=0(Latin-1 单字节编码)/1(utf-16)
解决,jdk8及之前版本中内存空间浪费问题。char占2个字节/16位,若存储1个字节的数据很浪费。jdk9采用1个字节/8位byte数组存放,节约内存空间。
二、特性,不可变
String对象的不可变性(一旦创建成功,则不能改变),表现如下:
1、final class String ,final修饰类 表示类不可被继承
2、private final char value[],final+private 表示 对象不能更改
这样做的好处?
1、安全,防止恶意修改
2、hash属性值不变,保证唯一性,因此HashMap才能实现key-value的缓存功能
3、实现字符串常量池,节约内存
String str="abc"。
创建字符串对象时,jvm判断字符串常量中包含该对象吗?包含,则返回该对象引用;不包含,则在字符串常量池中创建该对象,并返回对象引用。防止重复创建,节约内存。
String str= new String("abc")
编译类文件时,“abc”常量字符串被放入常量结构
类加载时,常量池中创建“abc”
调用new时,jvm命令调用String的构造函数,同时引用常量池中的“abc”字符串-->
在堆内存中创建String对象-->str将引用String对象
经典反例
问题,先 String str="hello"; 后 str="world" 为啥值改变了呢?
答案
== 比较两对象是否相等;equals 比较两对象值是否相等
“hello”“world”是内存中的一块内存地址,是对象本身;而str 是指向该内存地址的引用,是对象引用
str="hello",创建“hello”对象,str引用指向“hello”地址;
str=”world“,创建“world”对象,str引用指向“world”地址
三、优化
1、字符串构建,构建超大字符串
字符串常量拼接,会被编译器自动优化
理论分析,这段代码是低效的,会生成ab、abcd、abcdef三个对象。
实际运行发现,编译器自动优化为 String str="abcdef"。
字符串变量拼接
编译器把1000次的+,优化为 1000次new StringBuilder,降低系统性能!推荐显示使用StringBuilder。
并发环境中,可使用StringBuffer线程安全,但涉及到锁竞争,所以性能比StringBuilder差一些。
2、对象存储,intern节省内存
Twitter发布消息状态时,需要32G内存存储地址信息,初步优化降低为20G,二次优化降低为几百M。
原版
初次优化,抽象公共字段(国家、省份、城市)
二次优化,赋值使用intern方法,通过常量池使用重复的地址信息
intern介绍
intern原理解析
字符串常量中,常量池中创建对象
字符串变量中,堆内存中创建对象、常量池中创建字符串对象。引用赋值到堆内存对象中,并返回堆内存对象的引用。
如果调用intern方法,会查看字符串常量池中是否包含该字符串,若包含,则直接返回常量池中字符串的引用;若不包含,则在常量池中新增该对象,后返回引用。由于堆内存中原有的对象 没有引用指向它,将会通过垃圾回收器回收。
String a = new String("abc").intern() 解析
在一开始创建a变量时,会在堆内存中创建一个对象,同时会在加载类时,在常量池中创建一个 字符串对象,在调用intern方法之后,会去常量池中查找是否有等于该字符串的对象,有就返回 引用。
在创建b字符串变量时,也会在堆中创建一个对象,此时常量池中有该字符串对象,就不再创 建。调用intern方法则会去常量池中判断是否有等于该字符串的对象,发现有等于"abc"字符串的 对象,就直接返回引用。而在堆内存中的对象,由于没有引用指向它,将会被垃圾回收。所以a 和b引用的是同一个对象。
一张图总结String字符串的创建分配内存地址的情况
注意事项!常量池中不宜放太多数据
结合实际场景。常量池的实现类似于HashTable的实现方式,存储数据越多,遍历的时间复杂度会增加。
3、字符串分割
谨慎使用split(),建议使用 indexOf()替代。
split()使用正则表达式实现分割功能,因此性能非常不稳定,使用不当会引发“回溯”问题,导致cpu高居不下。
总结,
高效的使用字符串,可以提升系统的整体性能。
java版本迭代中通过修改String成员变量,避免内存泄漏问题,节约内存
String对象的不可变性,实现了字符串常量池,复用重复的字符串对象,节约内存。应用中,借用intern特性,避免创建重复对象,进而节约内存。
千里之堤,溃于蚁穴。日常编码,更要谨小慎微。
Java 字符串限制不能有非常规符号 java数组字符串不可能溢出
转载本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
Java-长字符串加密
加密:为你的长字符串提供最高级别的保护!!!
加密算法 JAVA -
java json字符串转 jsonobject
java json字符串转 jsonobject
JSON json 字符串转换