如何理解java中String的不可变性
文章目录
- 问题
- String类的声明
- final关键字的作用
- String的不变性
- String的不变性有哪些好处
- String真的是绝对不可变吗
问题
为什么大家都在说String是不可变的?
String类的声明
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];......public String(char value[]) {this.value = Arrays.copyOf(value, value.length);}
java中String声明是final
类型,说明String不可继承,String里面核心存储值的value是一个char[]数组也使用了final
修饰,说明value的引用地址
是不改变。
final关键字的作用
使用final修饰的变量,编译器不允许我们做修改的,举例说明:
@Testpublic void finalTest(){final char[] value = {'a','a','a'};char[] value2 = {'b','b','b'};value = value2;//cannot assign a value to final variable}
上面value = value2这一行会报错,无法为final修饰的变量赋值,意思就是使用final修饰的变量的引用不能被更改。但是引用不能被更改不代表不能改变里面的值。如下说所示:
@Testpublic void finalTest() {final char[] value = {'a', 'a', 'a'};System.out.println(value);value[2] = 'b';//将索引位置为2的修改为bSystem.out.println(value);}
打印结果:
aaa
aab
String的不变性
这里简单的例子测试一下String的不可变性
@Testpublic void testString() {String test1 = new String("aaa");StringBuilder test2 = new StringBuilder("aaa");System.out.println("输出原始的test1:" + test1);System.out.println("输出原始的test2:" + test2.toString());//追加字符串之后String afaddStr = addString(test1);StringBuilder afaddStrBuild = addStringBuilder(test2);System.out.println("输出原始的test1:" + test1);System.out.println("输出原始的test2:" + test2.toString());}/*** 向字符串中添加bbb** @param str*/public String addString(String str) {str = str + "bbb";return str;}/*** 向字符串中追加bbb** @param str*/public StringBuilder addStringBuilder(StringBuilder str) {str.append("bbb");return str;}
结果输出:
输出原始的test1:aaa
输出原始的test2:aaa
输出原始的test1:aaa
输出原始的test2:aaabbb
test1在一顿操作之后还保留了原来的样子,但是test2已经被改变了,其实String这种不可变性我们经常无意间使用,最常见的使用场景就是HashSet的值。看如下示例:
@Testpublic void testString2() {String key1 = new String("aaa");StringBuilder key2 = new StringBuilder("aaa");HashSet set = new HashSet<>();set.add(key1);set.add(key2);System.out.println("原始存储的set值:"+ set.toString());/**************模拟业务操作******************/String afaddStr = addString(key1);StringBuilder afaddStrBuild = addStringBuilder(key2);/**************模拟业务操作****end***********/System.out.println("业务执行后set值:"+ set.toString());}/*** 向字符串中添加bbb** @param str*/public String addString(String str) {str = str + "bbb";return str;}/*** 向字符串中追加bbb** @param str*/public StringBuilder addStringBuilder(StringBuilder str) {str.append("bbb");return str;}
如果使用StringBuilder存储则我们无意间的业务操作会改变Set中的值,这可能引起不必要的麻烦。
String的不变性有哪些好处
- 只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。如果字符串是可变的,那么String interning将不能实现(String interning是指对不同的字符串仅仅只保存一个,即不会保存多个相同的字符串),因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。
- 如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。 因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
- 因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
- 因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
String真的是绝对不可变吗
看如下例子:
@Testpublic void testString3() throws IllegalAccessException, NoSuchFieldException {String strObj = new String("aaa");System.out.println("反射执行前字符串:" + strObj);System.out.println("反射执行前的hash值:" + strObj.hashCode());Field field = strObj.getClass().getDeclaredField("value");field.setAccessible(true);char[] value = (char[]) field.get(strObj);value[2] = 'b';System.out.println("反射执行后字符串:" + strObj);System.out.println("反射执行后的hash值:" + strObj.hashCode());}
打印结果如下:
反射执行前字符串:aaa
反射执行前的hash值:96321
反射执行后字符串:aab
反射执行后的hash值:96321
说明通过反射我们是可以修改String的值的
参考如下:
https://www.zhihu.com/question/20618891
如何理解java中String的不可变性相关推荐
- java中String相等问题
判断两个字符串是否相等的问题.在编程中,通常比较两个字符串是否相同的表达式是"==",但在java中不能这么写.在java中,用的是equals(); 例:A字符串和B和字符串比较 ...
- 深入理解Java中的String(原地址https://www.cnblogs.com/xiaoxi/p/6036701.html)
深入理解Java中的String 一.String类 想要了解一个类,最好的办法就是看这个类的实现源代码,来看一下String类的源码: public final class Stringimplem ...
- 深入理解Java中的内存泄漏
理解Java中的内存泄漏,我们首先要清楚Java中的内存区域分配问题和内存回收的问题本文将分为三大部分介绍这些内容. Java中的内存分配 Java中的内存区域主要分为线程共享的和线程私有的两大区域: ...
- 深入理解Java中的final关键字
深入理解Java中的final关键字 http://www.importnew.com/7553.html Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什 ...
- java中字符串的创建_【转载】 Java中String类型的两种创建方式
本文转载自 https://www.cnblogs.com/fguozhu/articles/2661055.html Java中String是一个特殊的包装类数据有两种创建形式: String s ...
- Java中String、StringBuffer、StringBuilder的区别
Java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题.现在总结一下,看看他们的不同与相同. 1.可变与不可 ...
- java中demo接人_return的用法_如何理解java中return的用法?
C语言中return用法?(请熟练者进) return是返回值,这个返回值是和函数的类型有关的,函数的类型是什么,他的返回值就是什么 比方主函数intmain() {}这里就必须有一个return,只 ...
- 深入理解Java中的逃逸分析
转载自 深入理解Java中的逃逸分析 在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件.第二段编译 ...
- 一文带你理解Java中Lock的实现原理
转载自 一文带你理解Java中Lock的实现原理 当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题.java提供了两种方式来加锁,一种是关键字:synchron ...
最新文章
- 现在流行的源程序版本管理软件和项目管理软件
- centos 6推荐使用epel源
- 【Android RTMP】RTMP 数据格式 ( FLV 视频格式分析 | 文件头 Header 分析 | 标签 Tag 分析 | 视频标签 Tag 数据分析 )
- C# 获取utc时间,以及utc datetime 互相转化
- SpingMVC ModelAndView, Model,Control以及参数传递
- AI会率先在汽车、安全和金融领域落地!不服来辩 | AI科技评论
- Linux 的权限表达式
- IoC反转控制初步认识
- java可重复的集合_Java中是否存在任何无序,可重复的Collection类?
- 【招聘内推】推荐策略产品经理-阿里文娱优酷产品直招
- 在 Mac 上如何使用鼠标键来控制指针?
- ipython 安装_IPYTHON安装.DOC
- 被罗马人称为的三大蛮族去向和苏格拉的独立
- java 表单设计器 开源_6款在线表单设计器_Web表单设计器_AnyReport
- Python NLP入门教程
- 再见,Microsoft Academic——你好,开放式研究基础设施?
- win7系统安装高版本的node教程
- 2016 KCon 黑客大会兵器谱
- 爬取免费可用代理IP
- delphi 快速制作通用LiveUpdate升级程序
热门文章
- VOFM、Copy Control与合并开票
- SAP,IBM,AC实施之比较
- SAP:HANA为高性能数据分析保驾护航
- 颠覆智能床垫技术,喜临门开启“深睡时代”
- c语言 求一个数的因数,【代码】求一个数的因数和、求优化、顺便也供新人参考算法...
- 同级选择器_基础选择器
- 大于小于优化_工程优化设计与Matlab实现——优化设计的数学基础
- springwebflux 页面_Spring Webflux 响应式框架入门
- python中的及||
- Python 字符串转义序列及格式化