String为什么是不可变的?
面试官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为什么是不可变的?的更多相关文章
- Java中的String为什么是不可变的?
转载:http://blog.csdn.net/zhangjg_blog/article/details/18319521 什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那 ...
- Java基础知识强化101:Java 中的 String对象真的不可变吗 ?
1. 什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对 ...
- 聊聊JAVA中 String类为什么不可变
前言 "我的风格比较偏传统和经典" 小明说,"我们在打扮自己的问题上还是蛮冒险的...我觉得当你是只狗的时候,穿什么都hold的住!" 哈哈哈,脱离单身狗快两年 ...
- 为什么Java中的String类是不可变的?
String类是Java中的一个不可变类(immutable class). 简单来说,不可变类就是实例在被创建之后不可修改. 在<Effective Java> Item 15 中提到了 ...
- 为什么String类是不可变的?
为什么String类是不可变的? String类 什么是不可变对象 当满足以下条件时,对象才是不可变的: 对象创建以后其状态就不能修改. 对象的所有域都是final类型的. 对象是正确创建的(在对象的 ...
- Java中的String为什么是不可变的? -- String源码分析
众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不能改变状态的意思是, ...
- Java 中的 String 真的是不可变吗?
我们都知道 Java 中的 String 类的设计是不可变的,来看下 String 类的源码. public final class String implements java.io.Seriali ...
- java中String类为什么不可变?
在面试中经常遇到这样的问题:1.什么是不可变对象.不可变对象有什么好处.在什么情景下使用它,或者更具体一点,java的String类为什么要设置成不可变类型? 1.不可变对象,顾名思义就是创建后的对象 ...
- String为什么是不可变的?
前几天一个面试被问到String为什么是不可变的?, 自我感觉当时回答的不太理想, 事后总结一下 不可变的是什么 我们谈论的String不可变, 指的是字符串的值不可变 例: String s = & ...
随机推荐
- LYK loves graph(graph)
题目: LYK loves graph(graph) Time Limit:2000ms Memory Limit:128MB LYK喜欢花花绿绿的图片,有一天它得到了一张彩色图片,这张图片可以看 ...
- POJ3237 Tree 树链剖分 线段树
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ3237 题意概括 Description 给你由N个结点组成的树.树的节点被编号为1到N,边被编号为1 ...
- P1865 A % B Problem 素数筛
题目描述 区间质数个数 输入输出格式 输入格式: 一行两个整数 询问次数n,范围m 接下来n行,每行两个整数 l,r 表示区间 输出格式: 对于每次询问输出个数 t,如l或r∉[1,m]输出 Cros ...
- Free DIY Tour HDU1224
一道很好的dfs加储存路径的题目 :路径保存:每次dfs都存i 当大于max时 将临时数组保存到答案数组 并不是当 当前值大于最大值时更新路径 还要加上一个条件:能回去 #include<bi ...
- Redis数据结构之字符串
学习阶段分成两个部分,一个是redis客户端,一个是java客户端操作 一:在redis客户端操作 1.先删除里面的几个key 2.set与get与getset 3.数值的增减 值递增1,或者减一 如 ...
- POJ 1655 Balancing Act (求树的重心)【树形DP】(经典)
<题目链接> 题目大意:给你一棵树,任意去除某一个点后,树被分成了几个联通块,则该点的平衡值为所有分成的连通块中,点数最大的那个,问你:该树所有点中,平衡值最小的那个点是什么? 解题分析: ...
- Javascript实现对象的继承
在Java和C#中,你可以简单的理解class是一个模子,对象就是被这个模子压出来的一批一批月饼.压个啥样,就得是个啥样,不能随便动,动一动就坏了.而在Javascript中,没有模子,月饼被换成了面 ...
- AGC01 A - BBQ Easy
目录 题目链接 题解 代码 题目链接 AGC01 A - BBQ Easy 题解 贪心 排序之后从大到小,没两组取小的那个 代码 #include<cstdio> #include< ...
- PHP系统级函数 get_headers() 包含有服务器响应一个 HTTP
get_headers() 是PHP系统级函数,他返回一个包含有服务器响应一个 HTTP 请求所发送的标头的数组.如果失败则返回 FALSE 并发出一条 E_WARNING 级别的错误信息(可用来判断 ...
- java后端发送请求
package com.ty.mapapisystem.util; import java.io.BufferedReader;import java.io.FileInputStream;impor ...