前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >必知必会:String不可变的3个好处

必知必会:String不可变的3个好处

作者头像
程序视点
发布2023-11-03 17:11:58
3340
发布2023-11-03 17:11:58
举报
文章被收录于专栏:程序小小事
将程序视点设为星标精品文章第一时间阅读

大家好,欢迎来到程序视点!我是小二哥。今天我们来来聊聊String类型对象不可变的问题。

前言

String是Java中一个不可变的类,所以String对象一旦被实例化就无法被修改。我们知道Java中就是这样定义的。但是,为什么要这样设计呢?

String 类设计成不可变的原因及好处?

什么是不可变对象

从字面意思也能够理解,也就是我们的创建的对象不可改变。即,不可变类的实例一旦创建,其成员变量的值就不能被修改。为了实现创建的对象不可变,java语言要求我们需要遵守以下5条规则:

(1)类内部所有的字段都是final修饰的。 (2)类内部所有的字段都是私有的,也就是被private修饰。 (3)类不能够被集成和拓展。 (4)类不能够对外提供哪些能够修改内部状态的方法,setter方法也不行。 (5)类内部的字段如果是引用,也就是说可以指向可变对象,那我们程序员不能获取这个引用。

如果我们查看 String 类的源码,我们会发现它全部满足上面这5个规则。

代码语言:javascript
复制
/**(3)*/
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** (1)(2)*/
    private final char value[];

    /**(1)(2)*/
    private int hash; // Default to 0
    ......
    /**(4)(5) 没有setter和getter*/
}
原因及好处

String 设计成不可变,主要是从性能和安全两方面考虑。

性能优化,主要体现在字符串常量池的设计和使用上。

字符串常量池(String pool)是 Java 堆内存中一个特殊的存储区域,当创建一个 String 对象时,假如此字符串已经存在于常量池中,则不会创建新的对象,而是直接引用已经存在的对象。这样做能够减少 JVM 的内存开销,提高效率。

代码语言:javascript
复制
String s1 = "abc";
String s2 = "abc";

比如引用 s1和 s2 都是指向常量池的同一个对象 "abc",如果 String 是可变类,引用 s1 对 String 对象的修改,会直接导致引用 s2 获取错误的值。

其次是允许String对象缓存HashCode

String字符串不可变,所以在它创建的时候 hashcode就固定了,且被缓存了起来,之后不需要重新计算。这就使得字符串很适合作为 HashMap 中的 key,效率大大提高。

代码语言:javascript
复制
private int hash;//this is used to cache hash code.

大家在 String 类的源码中能看到这个成员变量。把String实例设计为不可变的,那么该实例的成员变量hash也是不会变的。

再者就是安全性上的考虑我们常用 String 字符串在其他Java类中充当参数,比如网络连接地址URL,文件路径path等。假若String不是固定不变的,将会引起各种安全隐患,例如,你本想访问百度,结果输入“www.baidu.com”后变成了你银行卡的转账地址;就问你心里慌不慌😓

还有就是多线程中的安全问题。多线程下,变量在多个线程之间共享。如果是可变对象,那么多线程下,它的值很可能被其他线程改变,造成不可预期的结果。而不可变的 String 可以自由在多个线程之间共享,不需要同步处理。

ps:要是多细细思量,你会发现这个String类在开发中使用的地方真的是十分频繁!

String类真的不可变吗?

既然有这个标题。那答案肯定就是可变。别忘了我们的反射机制。反射可是能拿到private私有的变量的。看看下面这个例子:

代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        String str = "小二哥";
        System.out.println(str);
        try {
            //我们通过反射获取内部的value字符数组
            Field field = String.class.getDeclaredField("value");
            field.setAccessible(true);
            char[] value;
            value = (char[]) field.get(str);
            //把字符串第一个字符变成”大“
            value[0] = '大';
            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
代码语言:javascript
复制
小二哥
大二哥

我们这不也把字符串”小二哥“改成了”大二哥“了。这就是我们反射机制的使用。但我们还是常说字符串是不可变的!(谁没事儿专门用反射去修改字符串呢?除非你心里有其他想法😓)。

好啦!今天的分享就到这里了!我们下期见。

One more thing

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序视点 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • String 类设计成不可变的原因及好处?
    • 什么是不可变对象
      • 原因及好处
      • String类真的不可变吗?
      • One more thing
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档