面试官Q1:请问为什么String是不可变的,能谈谈吗?

我们知道不管是面试初级、中级还是高级Java开发工程师,String永远都是一个绕不开的话题,而且问的问题也是各不相同,下面我们从几个角度来看看为什么String是不可变的?

什么是不可变对象?

   如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。

我们来看下面一段代码:

public class Demo {
String str = "ABC";
System.out.println("s = " + str); str = "123";
System.out.println("s = " + str);
}

打印结果为:

1s = ABC
2s = 123

对于上述代码,我们简单的分析一下:首先创建一个String对象str,然后让str的值为“ABC”,然后又让str的值为“123”。从打印结果可以看出,str的值确实改变了。

那还说String对象是不可变的呢?

这里存在一个误区:str只是一个String对象的引用,并不是对象本身。对象在内存中是一块内存区,放在堆中,成员变量越多,这块内存区占的空间越大。引用只是一个4字节的数据,里面存放了它所指向的对象的地址,通过这个地址可以访问对象,而这个引用存放在Java虚拟机栈栈帧的局部变量表中。也就是说,str只是一个引用,它指向了一个具体的对象,当str=“123”; 这句代码执行过之后,又创建了一个新的对象“123”, 而引用str重新指向了这个新的对象,原来的对象“ABC”还在内存中存在,并没有改变。

我们用一张内存结构图来看看整个变化过程:

其实上面的"ABC","123"是字符串常量,按照JVM规范应该是存放在方法区的常量池里面。但是Java1.7之后HotSpot虚拟机并没有区分方法区和堆,所以,这里统一就当做是放在堆里面的吧。

String源码构成

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; /** Cache the hash code for the string */
private int hash; // Default to 0

通过源码我们可以知道String底层是由char数组构成,我们创建一个字符串对象的时候,其实是将字符串保存在char数组中,因为数组是引用对象,为了防止数组可变,JDK加了final修饰,但是加了final修饰的数组只是代表了引用不可变,不代表数组内容不可变,因此JDK为了真正防止不可变,又加了private修饰符。

String对象是真的不可变吗?

从上文可知String的成员变量是private final 的,也就是初始化之后不可改变。那么在这几个成员中,value比较特殊,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗?我们来看下面的代码:

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}

所以String是不可变,关键是因为设计源代码的工程师,在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。private final char value[]这一句里,private的私有访问权限的作用都比final大。而且设计师还很小心地把整个String设成final禁止继承,避免被其他人继承后破坏。所以String是不可变的关键都在底层的实现,而不是一个final。

不可变有什么好处?

1、多线程下安全性

最简单地原因,就是为了安全。因为String是不可变的,因此多线程操作下,它是安全的,我们来看下面一段代码:

public String get(String str){
str += "aaa";
return str;
}

试想一下,如果String是可变的,那么get方法内部改变了str的值,方法外部str也会随之改变。

2、类加载中体现的安全性

类加载器要用到字符串,不可变提供了安全性,以便正确的类被加载,例如你想加载java.sql.Connection类,而这个值被改成了xxx.Connection,那么会对你的数据库造成不可知的破坏。

3、使用常量池可以节省空间

像下面这样字符串one和two都用字面量"something"赋值。它们其实都指向同一个内存地址

String one = "someString";
String two = "someString";

这样在大量使用字符串的情况下,可以节省内存空间,提高效率。但之所以能实现这个特性,String的不可变性是最基本的一个必要条件。要是内存里字符串内容能改来改去,这么做就完全没有意义了。

总结了这么多,面试答案就靠大家自己总结了,或者可以通过留言分享给他人哟。

String为什么是不可变的?的更多相关文章

  1. Java中的String为什么是不可变的?

    转载:http://blog.csdn.net/zhangjg_blog/article/details/18319521 什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那 ...

  2. Java基础知识强化101:Java 中的 String对象真的不可变吗 ?

    1. 什么是不可变对象?       众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对 ...

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

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

  4. 为什么Java中的String类是不可变的?

    String类是Java中的一个不可变类(immutable class). 简单来说,不可变类就是实例在被创建之后不可修改. 在<Effective Java> Item 15 中提到了 ...

  5. 为什么String类是不可变的?

    为什么String类是不可变的? String类 什么是不可变对象 当满足以下条件时,对象才是不可变的: 对象创建以后其状态就不能修改. 对象的所有域都是final类型的. 对象是正确创建的(在对象的 ...

  6. Java中的String为什么是不可变的? -- String源码分析

    众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不能改变状态的意思是, ...

  7. Java 中的 String 真的是不可变吗?

    我们都知道 Java 中的 String 类的设计是不可变的,来看下 String 类的源码. public final class String implements java.io.Seriali ...

  8. java中String类为什么不可变?

    在面试中经常遇到这样的问题:1.什么是不可变对象.不可变对象有什么好处.在什么情景下使用它,或者更具体一点,java的String类为什么要设置成不可变类型? 1.不可变对象,顾名思义就是创建后的对象 ...

  9. String为什么是不可变的?

    前几天一个面试被问到String为什么是不可变的?, 自我感觉当时回答的不太理想, 事后总结一下 不可变的是什么 我们谈论的String不可变, 指的是字符串的值不可变 例: String s = & ...

随机推荐

  1. 解决asp.net 报错 无法获取所需的权限错误

    asp.net 报错 无法获取所需的权限 无法获取所需的权限.说明: 执行当前 Web 请求期间,出现未处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息. 无法获取所 ...

  2. BZOJ2843 极地旅行社 LCT

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2843 题意概括 有n座岛 每座岛上的企鹅数量虽然会有所改变,但是始终在[0, 1000]之间.你的 ...

  3. 017 在SecureCRT中安装rz小工具

    1.安装yum 2.上传本地的文件进虚拟机 3.注意点 只是属于SecureCRT的命令,同时,在上传的位置是现在所在的位置 4.测试

  4. AngularJS移动端页面input无法输入

    用angularJS写手机页面,有时候会发现input输入框点击了却不能输入,或者长按才能输入,可能是因为input绑定了ng-click导致,可去掉ng-click,将ng-click绑定的方法改用 ...

  5. DNS信息收集命令nslookup

    DNS信息收集--NSLOOKUP 前面文章我介绍了dig命令,NSLOOKUP命令用法差不多 简单查询 语法:nslookup domain [dns-server] nslookup www.si ...

  6. Burp Suite之截断代理功能及相关设置(一)

    Burpsuite 1.burpsuite 简介2.设置代理3.Target4.爬网模块5.扫描模块6.扩展模块7.intrude8.Repeater9.Sequencer10.Decoder11.C ...

  7. [洛谷P3014][USACO11FEB]牛线Cow Line (康托展开)(数论)

    如果在阅读本文之前对于康托展开没有了解的同学请戳一下这里:  简陋的博客    百度百科 题目描述 N(1<=N<=20)头牛,编号为1...N,正在与FJ玩一个疯狂的游戏.奶牛会排成一行 ...

  8. shell 环境变量

    Ubuntu系统设置的环境变量 .profile .bashrc 在 .profile中 有一段代码: if [ -d "$HOME/bin" ] ; then PATH=&quo ...

  9. CSDN 个性 博客 栏目 自定义 栏目 酷炫 音乐 视频

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 自定义 栏目 酷炫 音乐 视频 ========

  10. Cocos2d-X开发教程-捕鱼达人 Cocos2-x development tutorial - fishing talent

    Cocos2d-X开发教程-捕鱼达人 Cocos2-x development tutorial - fishing talent 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱 ...