前言

说到String的不可变性,我猜肯定有同学要说可以通过反射来修改。所以我们在分享之前,在这边先出一个反射的题目,大家看看能不能答对。

题目

String name = "jionghui";
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[]) field.get(name);
value[0] = 'a';
System.out.println("jionghui" == name);

大家可以思考一下这个题目,我会在文末给出答案和解析。

不可变是什么意思

不可变类(immutable):类的实例一旦创建后,其内容(状态)就不可改变。

简单理解就是:一个对象一旦被创建后,整个对象就是不可变的。包括属任何性和状态。

可能有同学会拿下面这段代码来说,这不是变了吗?

public void testFinal() {String str = "程序员囧辉";str = "屌丝囧辉";
}

我们看下第2行代码,这行代码中有两部分组成。

等号左边:一个局部变量 str,类型是 String,这个变量是放在栈上的。

等号右边:一个字符串对象,放在堆中。

我们说的不可变,指的是字符串对象。

我们通过第3行代码,将这个 str 变量赋值为另一个字符串,对原来的字符串对象是没有任何改变的。

final修饰value数组?

我猜有不少同学在回答这个问题的时候,会答说是因为string底层的这个value 数组被 final 修饰,所以 String 不可变,这个说法其实不正确。

我们来看一个例子:

这个例子中,我们的 demo 变量使用了 final 修饰,但是我们仍然改变了其内容。所以,final 并不能保证对象的一个不可变性。

final修饰变量的含义

基础数据类型:一旦初始化,便不能改变其值。

引用类型:一旦初始化,便不能改变其引用,也就是不能指向一个新的对象,但是仍然可以修改引用指向的对象内容。

为什么不可变?

1)value使用final修饰

保证value一旦被初始化,就不可改变其引用。

2)没有暴露成员变量

成员变量的访问权限为 private,同时没有提供方法将字段暴露出来,想要修改只能通过 String 提供的方法。

3)内部方法不会改动 value

一旦初始化之后,String 类中的方法就不会去改动 value 中的元素,需要的话都是直接新建一个 String 对象。

4)类使用final修饰,不可继承

这个设计主要是避免有人定义一个子类继承 String,然后重写 String 的方法,将这个子类设计成可变对象。我们知道在 java 中,有父类引用指向子类对象这种用法,这种情况下,我们需要一个String 对象,可能返回的是String 子类的对象,这会导致 String 看起来是可变的。所以  java 直接将 String定义成不可继承,避免出现这种情况。

不只是 String 类,其实所有的不可变类大致的设计思想都是按这四步来。后续如果我们自己想要设计一个不可变类,也可以按这四点来设计。

不可变的好处?为什么这么设计?

1)安全性

String 是 Java 中最基础也是最长使用的类,经常用于存储一些敏感信息,例如用户名、密码、网络连接等。因此,String 类的安全性对于整个应用程序至关重要。

我们来看下面这个例子:

private static void dangerousOperation(MyString myString) throws InterruptedException {if (!securityCheck(myString)) {System.out.println("校验失败");return;}// 一些七的八的操作doSomething();// 执行危险操作dangerous(myString);
}

我们通过一个方法来模拟一个危险的一个系统操作。

首先在这个方法的入口会进行一个安全检查。如果检查失败,会直接返回。

然后接着我们会最终去执行这个比较危险的操作。

如果此时这个方法的参数是可变对象,那么它可能在通过安全检查的时候,是一个合法的入参。但是当最终执行到下面的危险操作时,他可能被调用方给修改了,变成一个不合法的参数。但是这个时候他已经通过检查了,所以我们没办法对他进行拦截,最终可能会导致我们的系统被攻击或者存在安全隐患。

2)节省空间——字符串常量池

通过使用常量池,内容相同的字符串可以使用同一个对象,从而节省内存空间。如果 String 是可变的,试想一下,当字符串常量池中的某个字符串对象被很多地方引用时,此时修改了这个对象,则所有引用的地方都会改变,这可能会导致预期之外的情况。

典型的使用字符串常量池的场景:json 工具类,fastjson、jackson 等。

3)线程安全

String 对象是不可修改的,如果线程尝试修改 String 对象,会创建新的 String,所以不存在并发修改同一个对象的问题。

4)性能

String 被广泛应用于 HashMap、HashSet 等哈希类中,当对这些哈希类进行操作时,例如 HashMap 的 get/put,hashCode 会被频繁调用。

由于不可变性,String 的 hashCode 只需要计算1次后就可以缓存起来,因此在哈希类中使用 String 对象可以提升性能。

Java 之父的观点

对于不可变性,Java 之父詹姆斯高斯林在一次采访中谈过这个话题,他表示:只要可以,他就会使用不可变性。可以看出他对不可变性的评价非常高。

至于不可变的好处,高斯林主要谈到了几个观点:

1)不可变对象往往更不容易出问题;

2)安全性问题;

3)缓存。

这三点在我们之前的内容里也基本都提到了,原文如下,有兴趣的可以去看一下。

https://www.artima.com/articles/james-gosling-on-java-may-2001#part13

题目答案

文章开头题目的答案是 true。

解释:当这段代码被编译之后,这两个被双引号修饰的 jionghui 字符串字面量,由于它们的值是相同的,所以它们会指向同一个符号引用。

当这个符号引用被解析时,我们会在字符串常量池中创建一个 jionghui 字符串。最终这两个字符串字面量都会指向我们字符串常量池里面的这个 jionghui,所以他们其实指向的是同一个字符串对象。

因为他们的引用是相同的,所以这个地方输出结果的是 true。

看过我上一个文章/视频的同学应该不难理解。如果你不理解,或者说你对字符串常量池、符号引用有一些疑问,你可以去看一下我的上一个文章/视频。我在上一个文章/视频里有详解介绍字符串常量池和符号引用的相关内容。

推荐阅读

全网最实用的 IDEA Debug 调试技巧(超详细案例)

Java 基础高频面试题(2021年最新版)

Java 集合框架高频面试题(2021年最新版)

面试必问的 Spring,你懂了吗?

面试必问的 MySQL,你懂了吗?

String 为什么不可变?不可变有什么好处?相关推荐

  1. Java的知识点21——String类、StringBuffer和StringBuilder、不可变和可变字符序列使用陷阱

    String类 String 类对象代表不可变的Unicode字符序列,因此我们可以将String对象称为"不可变对象" substring()是对字符串的截取操作,但本质是读取原 ...

  2. String的内存模型,为什么String被设计成不可变的

    String是Java中最常用的类,是不可变的(Immutable), 那么String是如何实现Immutable呢,String为什么要设计成不可变呢? 前言 关于String,收集一波基础,来源 ...

  3. Interview:算法岗位面试—10.11下午—上海某公司算法岗位(偏机器学习,互联网数字行业)技术面试考点之XGBoost的特点、python的可变不可变的数据类型、赋值浅拷贝深拷贝区别

    ML岗位面试:10.11下午-上海某公司算法岗位(偏机器学习,互联网数字行业)技术面试考点之XGBoost的特点.python的可变不可变的数据类型.赋值浅拷贝深拷贝区别 Interview:算法岗位 ...

  4. java如果把字符串转成对象_为什么Java中的字符串对象是不可变的,有什么好处?...

    专注于Java领域优质技术号,欢迎关注 原创: 阿杜的世界 阅读本文大概需要 4分钟. 所谓不可变对象,是指一个对象在创建后,它的内部状态不会被改变的对象.这意味着当我们将一个不可变对象的引用赋值给某 ...

  5. 【软件构造】数据类型、类型检查、可变不可变数据类型

    文章目录 一.基本数据类型 和 对象数据类型 1.基本数据类型: 2.对象数据类型: 二.静态类型检查 和 动态类型检查 1.静态类型检查 2.动态类型检查 三.Mutable可变对象 和 Immut ...

  6. 2017.7.18可变/不可变类型,符号运算及其流程控制

    1.可变/不可变类型 可变数据类型:指的是数据id不变的情况下,数值可变 例子:列表   字典 不可变数据类型:是数据本身的id.value都不可改变的数据 例子:数字   字符串   布尔值 2.运 ...

  7. scala不可变和可变_Scala使期货变得轻松

    scala不可变和可变 by Martin Budi 马丁·布迪(Martin Budi) Scala使期货变得轻松 (Futures Made Easy with Scala) Future is ...

  8. python的内置数据结构可变不可变_如何在Python中实现不可变的数据结构?

    问题 您需要在Python中实现不可变的数据结构. 介绍.. 当您要防止多个人同时在并行编程中修改一条数据时,不可变数据结构非常方便.可变数据结构(例如Array)可以随时更改,而可变数据结构则不能更 ...

  9. String被设计成不可变和不能被继承的原因

    String是所有语言中最常用的一个类.我们知道在Java中,String是不可变的.final的.Java在运行时也保存了一个字符串池(String pool),这使得String成为了一个特别的类 ...

  10. JAVA day13,14 API、Object类、日期时间类(long,Date,Calendar,DateFormat)、String类(字符串,可变长字符串)、正则表达式、包装类

    1.API API(Application Programming Interface),应⽤程序编程接⼝.Java API是⼀本程序员的"字 典",是JDK提供给我们使⽤的类的说 ...

最新文章

  1. 手机版html页面左右滑动切换页面,移动端手指左右滑动切换内容demo
  2. 第三篇:属性_第二节:控件属性在页面及源码中的表示方式
  3. 数据交换格式Json与XML
  4. Vue.js(17)之 插槽
  5. python学习笔记(10)--组合数据类型(序列类型)
  6. 顺丰快递单号的规律_顺丰速运顺丰快递单号查询大全
  7. [bzoj1059]矩阵游戏
  8. 新代系统plc梯形图说明书_东莞自动化PLC编程需要多少钱
  9. 追佛信道不如追求真理。。
  10. Opencv之以图像直方图可视化
  11. 如何制作SCI论文中的Figure(二)
  12. 最简单的基于FFmpeg的封装格式处理:视音频分离器(demuxer)
  13. SSD1306 - OLED显示屏
  14. 7.camera驱动08-全志-media framework
  15. 笔记本重装win10系统
  16. H5开发html文件转换pdf,将HTML页面转换为PDF文件并导出
  17. html中如何改变鼠标样式,HTML中常用鼠标样式
  18. H264和aac 封装成为ts,并生成m3u8
  19. Html5网页录音,js录音mp3
  20. mysql的字段空格是null_空字符与空格字符、NULL、空字符串

热门文章

  1. 可穿戴设备在娱乐领域的应用:音乐、电影和游戏
  2. NumPy 布尔索引
  3. HTTPS生成数字证书
  4. Python之wxPython的使用
  5. 车载部标服务器源码,部标JT T808-2013协议与样例代码
  6. K8S集群rook部署ceph集群
  7. 莆田中专计算机专业都学什么,莆田所有中专学校 中专院校名单
  8. lnmp一键安装包 安装php-fpm,LNMP一键安装包常用命令及配置文件路径
  9. 移动办公平台如何选?好用的移动办公平台推荐
  10. java osgi 文件_OSGi 系列(一)之什么是 OSGi :Java 语言的动态模块系统