String 为啥不可变?因为 String 中的 char 数组被 final 修饰。这套回答相信各位已经背烂了,But 这并不正确!

  • 面试官:讲讲 StringStringBuilderStringBuffer 的区别
  • 我:String 不可变,而 StringBuilderStringBuffer 可变,叭叭叭 …
  • 面试官:String 为什么不可变?
  • 我:Stringfinal 修饰,这说明 String 不可继承;并且String 中真正存储字符的地方是 char 数组,这个数组被 final 修饰,所以 String 不可变
  • 面试官:String 的不可变真的是因为 final 吗?
  • 我:是…是的吧
  • 面试官:OK,你这边还有什么问题吗?
  • 我:卒…

什么是不可变?

《Effective Java》中对于不可变对象(Immutable Object)的定义是:对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化。这就意味着,一旦我们将一个对象分配给一个变量,就无法再通过任何方式更改对象的状态了。

String 不可变的表现就是当我们试图对一个已有的对象 “abcd” 赋值为 “abcde”,String 会新创建一个对象:

全文收录在 小牛肉 | 大厂面试火箭计划,整理了很多大厂面试题,提供背诵版和详解版,应该对小伙伴们有所帮助~

String 为什么不可变?

String 用 final 修饰 char 数组,这个数组无法被修改,这么说确实没啥问题。

但是!!!这个无法被修改仅仅是指引用地址不可被修改(也就是说栈里面的这个叫 value 的引用地址不可变,编译器不允许我们把 value 指向堆中的另一个地址),并不代表存储在堆中的这个数组本身的内容不可变。举个例子:

如果我们直接修改数组中的元素,是完全 OK 的:

那既然我们说 String 是不可变的,那显然仅仅靠 final 是远远不够的

1)首先,char 数组是 private 的,并且 String 类没有对外提供修改这个数组的方法,所以它初始化之后外界没有有效的手段去改变它;

2)其次,String 类被 final 修饰的,也就是不可继承,避免被他人继承后破坏;

3)最重要的!是因为 Java 作者在 String 的所有方法里面,都很小心地避免去修改了 char 数组中的数据,涉及到对 char 数组中数据进行修改的操作全部都会重新创建一个 String 对象。你可以随便翻个源码看看来验证这个说法,比如 substring 方法:

为什么要设计成不可变的呢?

1)首先,字符串常量池的需要

我们来回顾一下字符串常量池的定义:大量频繁的创建字符串,将会极大程度的影响程序的性能。为此,JVM 为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化:

  • 为字符串开辟了一个字符串常量池 String Pool,可以理解为缓存区
  • 创建字符串常量时,首先检查字符串常量池中是否存在该字符串
  • 若字符串常量池中存在该字符串,则直接返回该引用实例,无需重新实例化;若不存在,则实例化该字符串并放入池中。

如下面的代码所示,堆内存中只会创建一个 String 对象:

String str1 = "hello";
String str2 = "hello";System.out.println(str1 == str2) // true

假设 String 允许被改变,那如果我们修改了 str2 的内容为 good,那么 str1 也会被修改,显然这不是我们想要看见的结果。

2)另外一点也比较容易想到,String 被设计成不可变就是为了安全

作为最基础最常用的数据类型,String 被许多 Java 类库用来作为参数,如果 String 不是固定不变的,将会引起各种安全隐患。

举个例子,我们来看看将可变的字符串 StringBuilder 存入 HashSet 的场景:

我们把可变字符串 s3 指向了 s1 的地址,然后改变 s3 的值,由于 StringBuilder 没有像 String 那样设计成不可变的,所以 s3 就会直接在 s1 的地址上进行修改,导致 s1 的值也发生了改变。于是,糟糕的事情发生了,HashSet 中出现了两个相等的元素,破坏了 HashSet 的不包含重复元素的原则。

另外,在多线程环境下,众所周知,多个线程同时想要修改同一个资源,是存在危险的,而 String 作为不可变对象,不能被修改,并且多个线程同时读同一个资源,是完全没有问题的,所以 String 是线程安全的。

String 真的不可变吗?

想要改变 String 无非就是改变 char 数组 value 的内容,而 value 是私有属性,那么在 Java 中有没有某种手段可以访问类的私有属性呢?

没错,就是反射,使用反射可以直接修改 char 数组中的内容,当然,一般来说我们不这么做。

看下面代码:

总结

全文收录在 小牛肉 | 大厂面试火箭计划,整理了很多大厂面试题,提供背诵版和详解版,应该对小伙伴们有所帮助~

总结来说,并不是因为 char 数组是 final 才导致 String 的不可变,而是为了把 String 设计成不可变才把 char 数组设置为 final。下面是一些创建不可变对象的简单策略,当然,也并非所有不可变类都完全遵守这些规则:

  • 不要提供 setter 方法(包括修改字段的方法和修改字段引用对象的方法);
  • 将类的所有字段定义为 final、private 的;
  • 不允许子类重写方法。简单的办法是将类声明为 final,更好的方法是将构造函数声明为私有的,通过工厂方法创建对象;
  • 如果类的字段是对可变对象的引用,不允许修改被引用对象。

面试官问我 “String 的不可变真的是因为 final 吗“,我回答 “是“ 然后就被挂了。。。。。。相关推荐

  1. 美女面试官问我Python如何优雅的创建临时文件,我的回答....

    [摘要] 本故事纯属虚构,如有巧合,他们故事里的美女面试官也肯定没有我的美,请自行脑补... 小P像多数Python自学者一样,苦心钻研小半年,一朝出师投简历. 这不,一家招聘初级Python开发工程 ...

  2. java的map 使用string数组多了双引号_奥奥奥利给!!!再也不怕面试官问我String源码了!来吧...

    简述 字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了String 类来创建和操作字符串.字符串缓冲区支持可变字符串.因为String对象是不可变的,因此可以共享 ...

  3. 当面试官问你如何进行性能优化时,你该这么回答(一)

    背景 在开发好页面后,如何让页面更快更好的运行,是区分一个程序猿技术水平和视野的一个重要指标.所以面试时,面试官总会问你一个问题,如何进行性能优化呢? 如果你这时是头脑一片空白,或是像之前的我一样,靠 ...

  4. 面试官问我:未来五年的职业规划..我是这样回答的

    "能否简述下你未来5年的职业规划呢?" 相信大多数求职者在面试的时候都会被问到过这个问题. 实际面试时,HR和业务部门的面试考核维度不同,这也代表着公司评估一个应聘者的能力不仅仅是 ...

  5. 面试官问我:什么是JavaScript闭包,我该如何回答

    闭包,有人说它是一种设计理念,有人说所有的函数都是闭包.到底什么是闭包?这个问题在面试是时候经常都会被问,很多小白一听就懵逼了,不知道如何回答好. 这个问题也有很多朋友在公众号给李老师留言了,问题表达 ...

  6. 面试官问我:什么是JavaScript闭包,我该如何回答?

    闭包,有人说它是一种设计理念,有人说所有的函数都是闭包.到底什么是闭包?这个问题在面试的时候经常都会被问,很多小白一听就懵逼了,不知道如何回答好. 这个问题也有很多朋友在公众号给李老师留言了,问题表达 ...

  7. 以后面试官问你 为啥不建议使用Select *,请你大声回答他!

    前言 不建议使用 select  *  这几个字眼,做开发的都不陌生吧. 阿里的开发手册上面也是有提到: 昨晚收到一个小兄弟的反馈: (称呼打码了,这是我的隐私,不可能让你们知道的) 随后也问了下学习 ...

  8. 【283期】面试官问:高并发场景下,如何保证全局唯一分布式 ID 生成?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... 前言 系统 ...

  9. 面试官问你期待工资多少时,该怎么回答?

    面试时,面试官问我期望工资是多少,我想都没想就要了6000元月薪,然后顺利入职了,可直到2年后,老板给我加薪时,我才知道当初面试的工资要少了,老板教会了我,如果面试官问期望工资是多少,该如何回答,但是 ...

最新文章

  1. linux命令安装组件,Linux安装各种组件
  2. android 图片放大缩小 多点触摸,Android 多点触摸(图片放大缩小)
  3. 顺序栈初始化,判空,进栈,出栈,打印
  4. [Aaronyang] 写给自己的WPF4.5 笔记6[三巴掌-大数据加载与WPF4.5 验证体系详解 2/3]
  5. 多线程编程学习笔记——使用并发集合(三)
  6. windows内核初窥(二)-----系统机制
  7. [C++程序设计]字符数组的赋值与引用
  8. .net System.Web.Mail发送邮件
  9. Swift 使用AVPlayer 和 AVPlayerItem 做语音播放
  10. MATLAB学习笔记02-MATLAB的数据类型
  11. 游戏本地化翻译有哪些内容需要注意
  12. OpenWrt 内的阿里云盘 WebDAV 做磁盘使用
  13. 从Spring为什么要用IoC的支点,我撬动了整个Spring的源码脉络
  14. C#发送邮件 SMTP
  15. mysql slave 1062_mysql主从同步slave错误1062
  16. 【以太网通信】PHY 芯片回环测试
  17. java数字家谱管理系统设计与实现计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
  18. Codeforces Round #750 (Div. 2)A-F1补题题解
  19. rem、em、px、rpx、vw、vh、%等
  20. 三角测量(triangulation)

热门文章

  1. Linux常用命令大全(史无前例的命令大全)
  2. Windows服务器双网卡绑定的方法(HP/Broadcom网卡)
  3. Windows循环渐进-Ping程序实现代码
  4. 小程序导出数据到excel表,借助云开发后台实现excel数据的保存
  5. idea生成__jb_old__文件无法删除
  6. 天池赛:宝可梦数据分析–龙系小精灵分享
  7. torchtext中文文本预处理使用流程文档
  8. C# 将OFD转为PDF
  9. 计算机考研复试之软件工程三十问
  10. 快解析教你,快速修改3389端口