在java中String类为什么要设计成final?
大神链接:在java中String类为什么要设计成final? - 程序员 - 知乎
我进行了重新排版,并且更换了其中的一个例子,让我们更好理解。
String很多实用的特性,比如说“不可变性”,是工程师精心设计的艺术品!艺术品易碎!用final就是拒绝继承,防止世界被熊孩子破坏,维护世界和平!
1. 什么是不可变?
String不可变很简单,如下图,给一个已有字符串"abcd"第二次赋值成"abcedl",不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。
2. String为什么不可变?
翻开JDK源码,java.lang.String类起手前三行,是这样写的:
- public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
- /** String本质是个char数组. 而且用final关键字修饰.*/
- private final char value[];
- ...
- ...
- }
首先String类是用final关键字修饰,这说明String不可继承。再看下面,String类的主力成员字段value是个char[ ]数组,而且是用final修饰的。final修饰的字段创建以后就不可改变。
有的人以为故事就这样完了,其实没有。因为虽然value是不可变,也只是value这个引用地址不可变。挡不住Array数组是可变的事实。Array的数据结构看下图
也就是说Array变量只是stack上的一个引用,数组的本体结构在heap堆。String类里的value用final修饰,只是说stack里的这个叫value的引用地址不可变。没有说堆里array本身数据不可变。看下面这个例子,
- final int[] value={1,2,3}
- int[] another={4,5,6};
- value=another; //编译器报错,final不可变
value用final修饰,编译器不允许我把value指向堆区另一个地址。但如果我直接对数组元素动手,分分钟搞定。
- final int[] value={1,2,3};
- value[2]=100; //这时候数组里已经是{1,2,100}
或者更粗暴的反射直接改,也是可以的。
- final int[] array={1,2,3};
- Array.set(array,2,100); //数组也被改成{1,2,100}
所以String是不可变,关键是因为SUN公司的工程师,在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。
private final char value[]这一句里,private的私有访问权限的作用都比final大。而且设计师还很小心地把整个String设成final禁止继承,避免被其他人继承后破坏。所以String是不可变的关键都在底层的实现,而不是一个final。考验的是工程师构造数据类型,封装数据的功力。
3. 不可变有什么好处?
这个最简单的原因,就是为了安全。
示例1
- package _12_01字符串;
- public class 为什么String要设计成不可变类你 {
- public static void main(String[] args) {
- String a, b, c;
- a = "test";
- b = a;
- c = b;
- String processA = processA(a);
- String processB = processB(b);
- String processC = processC(c);
- System.out.println(processA);
- System.out.println(processB);
- System.out.println(processC);
- }
- static String processA(String str){
- return str + "A";
- }
- static String processB(String str){
- return str + "B";
- }
- static String processC(String str){
- return str + "C";
- }
- }
- //OUTPUT
- // testA
- //testB
- //testC
当String支持非可变性的时候,它们的值很好确定,不管调用哪个方法,都互不影响。
如果String是可变的,就可能如下例,我们使用StringBuffer来模拟String是可变的
- package _12_01字符串;
- public class 为什么String要设计成不可变类2 {
- public static void main(String[] args) {
- StringBuffer a, b, c;
- a = new StringBuffer("test");
- b = a;
- c = b;
- String processA = processA(a);
- String processB = processB(b);
- String processC = processC(c);
- System.out.println(processA);
- System.out.println(processB);
- System.out.println(processC);
- }
- static String processA(StringBuffer str){
- return str.append("A").toString();
- }
- static String processB(StringBuffer str){
- return str.append("B").toString();
- }
- static String processC(StringBuffer str){
- return str.append("C").toString();
- }
- }
- //OUTPUT
- // testA
- //testAB
- //testABC
能看出b=a,c=b;程序员的本意是希望变量是不变的。所以String不可变的安全性就体现在这里。实际上StringBuffer的作用就是起到了String的可变配套类角色。
示例2
再看下面这个HashSet用StringBuilder做元素的场景,问题就更严重了,而且更隐蔽。
- class Test{
- public static void main(String[] args){
- HashSet<StringBuilder> hs=new HashSet<StringBuilder>();
- StringBuilder sb1=new StringBuilder("aaa");
- StringBuilder sb2=new StringBuilder("aaabbb");
- hs.add(sb1);
- hs.add(sb2); //这时候HashSet里是{"aaa","aaabbb"}
- StringBuilder sb3=sb1;
- sb3.append("bbb"); //这时候HashSet里是{"aaabbb","aaabbb"}
- System.out.println(hs);
- }
- }
- //Output:
- //[aaabbb, aaabbb]
StringBuilder型变量sb1和sb2分别指向了堆内的字面量"aaa"和"aaabbb"。把他们都插入一个HashSet。到这一步没问题。但如果后面我把变量sb3也指向sb1的地址,再改变sb3的值,因为StringBuilder没有不可变性的保护,sb3直接在原先"aaa"的地址上改。导致sb1的值也变了。这时候,HashSet上就出现了两个相等的键值"aaabbb"。破坏了HashSet键值的唯一性。所以千万不要用可变类型做HashMap和HashSet键值。
不可变性支持线程安全
还有一个大家都知道,就是在并发场景下,多个线程同时读一个资源,是不会引发竟态条件的。只有对资源做写操作才有危险。不可变对象不能被写,所以线程安全。
不可变性支持字符串常量池
最后别忘了String另外一个字符串常量池的属性。像下面这样字符串one和two都用字面量"something"赋值。它们其实都指向同一个内存地址。
- String one = "someString";
- String two = "someString";
这样在大量使用字符串的情况下,可以节省内存空间,提高效率。但之所以能实现这个特性,String的不可变性是最基本的一个必要条件。要是内存里字符串内容能改来改去,这么做就完全没有意义了。
转自:http://blog.csdn.net/u013905744/article/details/52414111
在java中String类为什么要设计成final?的更多相关文章
- 在java中String类为什么要设计成final
在java中String类为什么要设计成final? - 胖胖的回答 - 知乎 https://www.zhihu.com/question/31345592/answer/114126087
- 在Java中String类为什么要设计成final?String真的不可变吗?其他基本类型的包装类也是不可变的吗?
最近突然被问到String为什么被设计为不可变,当时有点懵,这个问题一直像bug一样存在,竟然没有发现,没有思考到,在此总结一下. 1.String的不可变String类被final修饰,是不可继承和 ...
- java中String类为什么要设计成final?
1 将方法或类声明为final主要目的是:确保它们不会在子类中改变语义.String类是final类,这意味着不允许任何人定义String的子类. String基本约定中最重要的一条是immutabl ...
- 【笔记】在java中String类为什么要设计成final?
部分内容转自知乎:https://www.zhihu.com/question/31345592 从自己的理解进行加工,压缩. String本质上是一个final类 public final clas ...
- Java中String类为什么被设计为final?
Java中String类为什么被设计为final 首先,String是引用类型,也就是每个字符串都是一个String实例.通过源码可以看到String底层维护了一个byte数组:private f ...
- java里String类为何被设计为final
前些天面试遇到一个非常难的关于String的问题,"String为何被设计为不可变的"?类似的问题也有"String为何被设计为final?"个人认为还是前面一 ...
- java中String类为什么不可变?
在面试中经常遇到这样的问题:1.什么是不可变对象.不可变对象有什么好处.在什么情景下使用它,或者更具体一点,java的String类为什么要设置成不可变类型? 1.不可变对象,顾名思义就是创建后的对象 ...
- Java中String类的方法及说明
String : 字符串类型 一. String sc_sub = new String(c,3,2); // String sb_copy = new String(sb) ...
- java中String类学习
java中String类的相关操作如下: (1)初始化:例如,String s = “abc”; (2)length:返回字符串的长度. (3)charAT:字符操作,按照索引值获得字符串中的指定字符 ...
随机推荐
- nrf52810学习笔记——三
在开发nRF52系列的蓝牙方案的时候,会用到IDE.SDK.softdevice.nrfgoStudio等开发软件,这里做一个小小的总结. 首先,下载SDK,里面有适合keil4号iar7(iar8也 ...
- PAT Basic 1054
1054 求平均值 本题的基本要求非常简单:给定 N 个实数,计算它们的平均值.但复杂的是有些输入数据可能是非法的.一个“合法”的输入是 [−1000,1000] 区间内的实数,并且最多精确到小数点后 ...
- bootshiro---开源的后台管理框架--基于springboot2+ shiro+jwt的真正rest api资源无状态认证权限管理框架,开发人员无需关注权限问题,后端开发完api,前端页面配置即可
https://gitee.com/tomsun28/bootshiro
- DDLog-不同颜色打印信息
(一)下载安装 1.安装插件 XcodeColors Github 链接:https://github.com/robbiehanson/XcodeColors 打开XcodeColors项目,编译即 ...
- iOS学习笔记06-手势识别
一.UIGestureRecognizer简单介绍 我们已经学习了触摸事件处理,但触摸事件处理起来很麻烦,每个触摸事件处理都需要实现3个touches方法,比较繁琐,实际上我们可以使用更加简单的触摸事 ...
- BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡 ——广义后缀自动机
神奇的性质,叶子节点不超过20个. 然后把这些节点提出来构成一颗新树,那么这些树恰好包含了所有的情况. 所以直接广义后缀自动机. 然后统计本质不同的字符串就很简单显然了. #include <c ...
- BZOJ 4259 残缺的字符串 ——FFT
[题目分析] 同bzoj4503. 只是精度比较卡,需要试一试才能行O(∩_∩)O 用过long double,也加过0.4.最后发现判断的时候改成0.4就可以了 [代码] #include < ...
- 还是Tomcat,关于类加载器的趣味实验
一.前言 类加载器,其实是很复杂一个东西,想等到我完全什么都弄明白了再写出来,估计不太现实...现在只能是知道多少写多少吧. 首先,我提一个问题:在我们自己的servlet中(比如ssm中,contr ...
- 修路 BZOJ 4774
修路 [问题描述] 村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路.对于边带权的无向图 G = (V, E),请选择一些边,使得1 <= i <= d, i号节点和 n ...
- [C/C++] 结构体内存对齐用法
一.为什么要内存对齐 经过内存对齐之后,CPU的内存访问速度大大提升; 内存空间按照byte划分,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内 ...