聊聊JAVA中 String类为什么不可变

前言

"我的风格比较偏传统和经典" 小明说,"我们在打扮自己的问题上还是蛮冒险的...我觉得当你是只狗的时候,穿什么都hold的住!"

哈哈哈,脱离单身狗快两年了,生活中除了爱情,不变的还有对代码的挚爱,总之始于热爱,忠于爱情,陷于代码。

前半年规划人生,后半年开始规划,最近发生的一些事情还是让自己倍感压力的,生活可以知足常乐,但人生不可以,如果你不把生命体验到极致,也许会被未来的自己所鄙视。

前世今生

String不可变这个话题应该是老生长谈了,你可以说它就是设计者的龟腚,然后巴拉巴拉说出一大堆优点,也可以说它忠于爱情,只要JVM存活,它便万年不变。

String自打娘胎一出生就跟他们的兄弟姐妹不一样,好好的娃被戴了一个final的帽子,以至于byte,int,short,long等基本类型的小伙们都不带它玩。

但是,String并不是一个简单的人设,如果各位小伙伴们仔细查阅其源码,那可是浩浩荡荡的3000+行代码了,如果你一个controller能这样的代码量也是非常不错的。

如果你仔细阅读源码注释,你会发现这样一句话:

Strings are constant; their values cannot be changed after they are created

大致意思就是String是个常量,从一出生就注定不可变。

我觉得到这里各位小伙们应该就知道为什么String不可变了,戴了个final的帽子,官方注释说明创建后不能被改变,但是为什么String要使用final修饰呢?

面试精选

在了解String不可变之前,我觉得有必要分析一道经典的面试题:

public class Apple {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a==b);
        System.out.println(a.equals(b));
        System.out.println(a==c);
        System.out.println(a.equals(c));
    }
}

答案是:true、true、false、true

如图所示,代码中的abc对应个的abc:

我觉得有必要把a、b、c替换成小明、红、光,abc替换成红苹果重新梳理一下流程:

  1. 小明想吃红苹果,小明的爸爸首先会在树上寻找是否有红苹果,有的话,爸爸就说,这个苹果归你了,如果没有,假设万能的小明爸爸也可以给他造一个。
  2. 这时候小红过来了,爸爸我也想吃那个红苹果,并且强烈要求要拿一个,就这样小明和小红共享了这个苹果。
  3. 小光是个听话的孩子,只要是红苹果就行,我可不想跟他俩争什么,爸爸就这样从超市里给小光买了一个红苹果。
  4. 小明和小红的是同一个苹果,这个是不变的事实,无论你怎么比较。
  5. 小红,小明和小光的都是红苹果,但却不是同一个苹果。
  6. 你可以把苹果树理解成常量池,爸爸购买苹果的过程理解为new对象,当然,举例可能不是太恰当,只是为了大家更好的理解。

回到代码本来来说,因为String太过常用,JAVA类库的设计者在实现时做了个小小的变化,即采用了享元模式,每当生成一个新内容的字符串时,他们都被添加到一个共享池中,当第二次再次生成同样内容的字符串实例时,就共享此对象,而不是创建一个新对象,但是这样的做法仅仅适合于通过=符号进行的初始化。

需要说明一点的是,在object中,equals()是用来比较内存地址的,但是String重写了equals()方法,用来比较内容的,即使是不同地址,只要内容一致,也会返回true,这也就是为什么a.equals(c)返回true的原因了。

不可变的好处

首先,我们应该站在设计者的角度思考问题,而不是觉得这不好,那不合理:

  • 可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销。
  • 我们的程序中大量使用了String字符串,有可能是出于安全性考虑。
  • 当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。

其次,我们再分析有没有更好的解决方案:

  • 然,对于我来说,并没有!!!
  • 然,对于我来说,并没有!!!
  • 然,对于我来说,并没有!!!

总结

了解到String是不可变的,知道了常量池是怎么个东西。

重温了面试题,有兴趣的小伙伴也可以去阅读下String的源码,浩浩荡荡的3000+。

String 被new时是要创建对象的,+ 号拼接同理,程序中尽量不要使用 + 拼接,推荐使用StringBuffer或者StringBuilder。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Albert陈凯

Scala兴衰史:暂时的没落或许是一个新的开始

5年前,Scala 似乎曾要成为编程语言中下一个佼佼者,因为它能够优雅得使用面向对象编程范式进行函数编程。 现如今,随着像 LinkedIn 和 Yammer ...

3824
来自专栏java一日一条

由字符串反转(使用递归)引申出来一道Java面试题

在Java中,最好的实现就是用JDK中StringBuffer的反转方法,它不仅速度快,效率高,而且还知道如何处理unicode代理对(surrogate p...

841
来自专栏一个会写诗的程序员的博客

编程范式 (Programming paradigm)

范,模范、典范也。范式即模式、方法。常见的编程范式有:函数式编程、程序编程、面向对象编程、指令式编程等。

3491
来自专栏大史住在大前端

javascript基础修炼(8)——指向FP世界的箭头函数

箭头函数是ES6语法中加入的新特性,而它也是许多开发者对ES6仅有的了解,每当面试里被问到关于“ES6里添加了哪些新特性?”这种问题的时候,几乎总是会拿箭头函数...

1073
来自专栏Coding01

浅谈 Laravel Collections

这两天看了两本书《Laravel Collections Unraveled》和 《Refactoring to Collections》。

3492
来自专栏黑泽君的专栏

java基础学习_面向对象(上)01_day07总结

============================================================================= ==...

992
来自专栏牛客网

阿里 Java研发工程师[内推] 电话面试

3月20号 阿里巴巴 Java研发工程师[内部推荐] 电话面试 1. 自我介绍(问了我哪里人、去杭州工作有没有问题、什么时间可以去实习) 2. 介绍一下自己做过...

4619
来自专栏平凡文摘

国外大神总结的 10 个 Java 编程技巧!

1242
来自专栏专知

【LeetCode 290】 关关的刷题日记28 Word Pattern

关关的刷题日记28 – Leetcode 290. Word Pattern 题目 Given a pattern and a string str, find...

3458
来自专栏精讲JAVA

面向对象VS面向过程

1 前言 向伟人致敬 其实这个问题真的是被问烂了,特别是刚入门的同行,我感觉这个问题应该是大家都听说过了,但是有多少人真的是理解这两个区别呢,这两...

2095

扫码关注云+社区

领取腾讯云代金券