这篇文章主要介绍String字符串的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
实现原理
在 Java6 以及之前的版本中,String 对象是对 char 数组进行了封装实现的对象,主要有四个成员变量:char 数组、偏移量 offset、字符数量 count、哈希值 hash。
从 Java7 版本开始到 Java8 版本,String 类中不再有 offset 和 count 两个变量了。这样的好处是 String 对象占用的内存稍微少了些。
从 Java9 版本开始,将 char[]字段改为了 byte[]字段,又维护了一个新的属性 coder,它是一个编码格式的标识。
一个 char 字符占 16 位,2 个字节。这个情况下,存储单字节编码内的字符(占一个字节的字符)就显得非常浪费。JDK1.9 的 String 类为了节约内存空间,于是使用了占 8 位,1 个字节的 byte 数组来存放字符串。
而新属性 coder 的作用是,在计算字符串长度或者使用 indexOf()函数时,我们需要根据这个字段,判断如何计算字符串长度。coder 属性默认有 0 和 1 两个值,0 代表 Latin-1(单字节编码),1 代表 UTF-16。如果 String 判断字符串只包含了 Latin-1,则 coder 属性值为 0,反之则为 1。
不可变
查看String类的代码可以发现,String类被final关键字修饰,因此这个类不能被继承,并且String类里面的变量char 数组也被 final 修饰了,因此String对象不能被修改。
String对象不可变主要有如下几个优点:
第一,保证 String 对象的安全性。假设 String 对象是可变的,那么 String 对象将可能被恶意修改。
第二,保证 hash 属性值不会频繁变更,确保了唯一性,使得类似 HashMap 容器才能实现相应的 key-value 缓存功能。
第三,可以实现字符串常量池。
在 Java 中,通常有两种创建字符串对象的方式:
第一种是通过字符串常量的方式创建,如String str="abc"
。
第二种是字符串变量通过new 形式的创建,如 String str=new String("abc")
。
当代码中使用第一种方式创建字符串对象时,在编译类文件时,”abc”常量字符串将会放入到常量结构中,在类加载时,“abc”将会在常量池中创建;然后,str将引用常量池中的字符串对象。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
String str=new String("abc")
这种方式,首先在编译类文件时,”abc”常量字符串将会放入到常量结构中,在类加载时,“abc”将会在常量池中创建;其次,在调用new时,JVM 命令将会调用 String 的构造函数,String 对象中的 char 数组将会引用常量池中”abc”字符串的char 数组,在堆内存中创建一个 String 对象;最后,str 将引用 String 对象,String对象的引用跟常量池中”abc”字符串的引用是不一样的。
对象与引用:对象的内容存储在内存中,操作系统通过内存地址来找到存储的内容,引用就是指内存的地址。
比如:String str=new String("abc")
,变量str指向的是String对象的存储地址,也就是说 str 并不是对象,而只是一个对象引用。
字符串拼接
常量相加
String str = "ab" + "cd" + "ef";
查看编译后的字节码
0 ldc #22 astore_13 return
可以发现编译器将代码优化成如下所示
String str= "abcdef";
变量相加
String a = "ab";String b = "cd";String c = a + b;
查看编译后的字节码
0 ldc #2,2 astore_1 3, ldc # 3, & lt; cd> ,5 astore_2 6, new # 4, & lt; java/lang/StringBuilder> null 字符串字符串的示例分析