探索java中的字符串

137 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
说到字符串,可能对大部分人来说并不默认,“Hello world”、“数据库”等等都属于字符串,很好理解,但是它以什么形式在内存中存在,可能对于一些刚入门的人来说是陌生的。本文介绍字符串的特点、创建方式、以及在jvm内存模型中String的存储。

String的特性

  • String使用final修饰,不可被继承
  • String实现了Serializable接口,表示字符串是支持序列化的
  • String实现了Comparable接口,表示字符串可以比较大小
  • String内部定义了private final char[]value数组,用于存储字符串数据
  • String具有不可变性

下面主要讨论一下String的不可变性

Snipaste_2022-10-17_14-32-26.jpg 从String类源码中可以看到

  • 这里使用final修饰了String类,表示该类是无法被任何其他类继承的,意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。
  • 用于存储字符串值的char[]数组用private和final修饰,其中,final可以保证value的引用地址不会被修改,但是不能保证数组中的值不会被修改,而配合private修饰符,能够保证值不会被外部修改。这样就能保证String类的不可变性。

value变量在栈中保存的是一个引用地址,使用final修饰后,不可修改,但是这个引用地址指向的array数组是可以改变的。

Snipaste_2022-10-17_15-48-15.jpg 所以String实现不可变性,不仅仅是只使用final就可以,更多的还是依赖于String的底层实现,在实现时不变动array数组。

那么不可变性是如何体现的?

未命名文件 (8).png String不可变很简单,给一个已有字符串s1 = "abcd"第二次赋值成"数据库",不是在原内存地址上修改数据,而是重新指向一个新对象的地址。

接着说说String的不可变性有什么用?
简单来说有两方面作用: 第一:有利于实现JVM字符串常量池,字符串常量池可以在程序运行时节约很多内存空间,因为不同的字符串变量指向相同的字面量时,都是指向字符串常量池中的同一个对象。这样一方面能够节约内存,另一方面也提升了性能。

Snipaste_2022-10-17_16-02-54.jpg 第二:主要考虑安全问题,如果String类是可变的,那么会引起很严重的安全问题。在很多情况下都是直接通过字符串传递数据,比如数据库的用户名密码、网络编程中的ip和端口,因为字符串是不可变的,所以它的值不能被修改,如果字符串是可变的,那么可以通过改变引用地址指向的值去修改字符串的值,从而导致安全漏洞。

String对象是如何存储的

不同的创建方式,存储方式是不同的。

Snipaste_2022-10-17_16-33-17.jpg 这里经常有这样一道面试题

采用String s = new String("hello world");方式创建,在内存中创建了几个对象?

答案是两个,理解了字符串如何存储,这道题就简单了,虚拟机会在堆空间创建new对象,在字符串常量池创建“hello world”。

字符串还有很多需要琢磨研究的,未完待续~~