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

这里插播一下对象和对象的引用之间的区别 ,对象的引用是放在栈中的 ,而对象是放在堆中的 ,看这个例子 String s = "123" ; s = "456" ; 表面上 s 看是变了 ,但是要搞清楚 ,变的只是 String 对象的引用 s ,而 “123” 这个对象是没有变化的 。看一个图 :

我们都说 String 是不可变对象 ,那我们就分析一下 String 的源码来看看它是怎么保证 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 public String(String original) {
this.value = original.value;
this.hash = original.hash;
} public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
} public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
...

我们通过分析源码可知 ,首先 String 类和存放字符串的 char 型数组都是 final 类型的 ,保证了 String 类不能被继承 ,value 这个引用本身不会该变 ,但是这样还不能说明属性是不可变的 ,因为我们可以通过改变 value 数组中具体的值来达到改变 value 的目的 。

public static void main(String[] args) {
char[] value = {'a','b','c','d'};
System.out.println(value);
for (int i = 0; i < value.length; i++) {
if(i == 1){
value[i] = 'B';
}
}
System.out.println(value); //aBcd
}

但是进一步研究会发现 ,对于 value 这个属性 ,被定义为 private ,而且 String 并没有提供相对应的 get set 方法 ,所以我们也不能操作它 。而那些我们常认为改变了 String 对象的方法 ,比方说 subString concat toUpperCase tirm 看了源码之后发现 ,这些方法都是重新创建了一个 char 数组 ,并没有改动之前的对象 。这也就是我们常说 String 是不可变对象的原因 。但是我们还是可以通过反射的方式来改变 value 的值 ,看个例子就好 ,但是我们一般不这么做 。

public static void main(String[] args) throws Exception {
//创建字符串"Hello World", 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); //Hello World //获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改变value属性的访问权限
valueFieldOfString.setAccessible(true); //获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
//改变value所引用的数组中的第5个字符
value[5] = '_'; System.out.println("s = " + s); //Hello_World
}

为什么要将 String 设计为不可变对象呢 ?当然是为了提高效率和安全 ,也是由于 String 的超高使用频率 。效率主要体现在当我们复制 String 对象的时候我只需要复制引用即可 ,不需要复制具体的对象 ,而在多线程环境中 ,若是不同的线程同时修改 String 对象 ,相互之间也不会有影响 。因为一旦改变都会创建一个新的字符串 ,保证了线程的安全 。

另外在堆中有个字符串常量池 ,我们创建的字符串都会存在这里 ,当创建相同的字符串的时候 ,其实指向的是同一个地方 。这就节省了大量的空间 ,当然 ,能出现字符串常量池也是因为 String 是不可变对象 。

因为 String 不可变 ,所以在创建对象的时候就已经将 hashCode 的值计算出来缓存在字段 hash 中 。这样在 Map 中 String 就很适合作为主键 ,速度快 。(因为在散列表中我们定位时会计算主键的 hash 值 。)

/** Cache the hash code for the string */
private int hash; // Default to 0

还有在数据库连接的时候我们通过字符串来传递用户名 ,密码 ,连接的库等信息 ,若字符串可变 ,则很可能被黑客篡改 。

PS. 基本类型对应的包装类都是不可变对象 ,这样设计的原因还是因为使用太频繁 ,好处在上面已经说过了 。突然感觉学到了好多之前没注意到的细节 。

源码版本为 JDK 1.7

推荐阅读:https://www.cnblogs.com/YJK923/p/9479805.html

参考资料:https://zhuanlan.zhihu.com/p/38144507

String 与不可变对象的更多相关文章

  1. Java String是不可变对象

    基本数据类型和String类型都是值传递,数组,对象等是引用传递 经多方面查找,String很奇特,虽然是引用数据类型,但是采用的却是值传递!!!基本数据类型采用的都是值传递,数组和对象都是引用传递( ...

  2. java String不可变对象,但StringBuffer是可变对象

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

  3. String为什么不可变

    转载:http://www.importnew.com/7440.html https://www.cnblogs.com/leskang/p/6110631.html 什么是不可变对象? 众所周知, ...

  4. Java 不可变对象

    不可变对象(immutable objects):一旦对象被创建,它们的状态就不能被改变(包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变),每次对他 ...

  5. String 的不可变真的是因为 final 吗?

    尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...

  6. Java进阶知识点4:不可变对象与并发 - 从String说起

    一.String的不可变特性 熟悉Java的朋友都知道,Java中的String有一个很特别的特性,就是你会发现无论你调用String的什么方法,均无法修改this对象的状态.当确实需要修改Strin ...

  7. 有没有想过String为什么设计为不可变对象

    1.声明为final类的目的: 主要目的就是保证String是不可变(immutable).不可变就是第二次给一个String 变量赋值的时候,不是在原内存地址上修改数据,而是重新指向一个新对象,新地 ...

  8. Scalaz(26)- Lens: 函数式不可变对象数据操作方式

    scala中的case class是一种特殊的对象:由编译器(compiler)自动生成字段的getter和setter.如下面的例子: case class City(name:String, pr ...

  9. 【Python】可变对象和不可变对象

    Python在heap中分配的对象分成两类:可变对象和不可变对象.所谓可变对象是指,对象的内容是可变的,例如list.而不可变的对象则相反,表示其内容不可变. 不可变对象:int,string,flo ...

随机推荐

  1. Minicap使用分析

    想起前段时间研究过的minicap,抱着无果的心情再次看了源码,这次竟然比上次清晰了一点点,难道是因为这两天被android源码折磨得身心疲惫然而却在不知不觉中增长了?不懂怎么样,看懂了大概. Min ...

  2. 51nod1312 最大异或和

    题目来源: TopCoder 基准时间限制:1 秒 空间限制:131072 KB 分值: 320  有一个正整数数组S,S中有N个元素,这些元素分别是S[0],S[1],S[2]...,S[N-1]. ...

  3. Linux环境下如何查看内存CPU和GPU使用情况以及界面标题栏实现

    查看内存和CPU    单独查看内存使用情况的命令:free -m    查看内存及cpu使用情况的命令:top    也可以安装htop工具,这样更直观,    安装命令如下:sudo apt-ge ...

  4. UNIX环境高级编程 第3章 文件I/O

    前面两章说明了UNIX系统体系和标准及其实现,本章具体讨论UNIX系统I/O实现,包括打开文件.读文件.写文件等. UNIX系统中的大多数文件I/O只需要用到5个函数:open.read.write. ...

  5. plsql链接数据库配置

    一. 目录结构 D:\install\PLSQL        |-- instantclient_11_2            |-- tnsnames.ora        |-- PLSQL ...

  6. mysql 在windons下的备份命令

    1. @echo off set "Ymd=%date:~,4%%date:~5,2%%date:~8,2%" mysqldump -uroot -proot jy510 > ...

  7. Oracle Certified Java Programmer 经典题目分析(二)

    ...接上篇 what is reserved(保留) words in java? A. run B. default C. implement D. import Java 关键字列表 (依字母排 ...

  8. 88.modelsim仿真do文件相关技巧

    网上的关于DO文件的编写好像资料不多,比较杂,所以本人总结一下常用的简单语法,方便大家查看.其实本人也刚接触DO文件没多久,有纰漏很正常,欢迎指正批评,互相学习.PS:写得有点乱   还有一个值得注意 ...

  9. python 结构化数据解析

    # -*- coding: utf-8 -*- # @Time : 2018/8/31 14:32 # @Author : cxa # @File : glomtest.py # @Software: ...

  10. find命令的使用

    在以.conf结尾的文件里面查找含有aaa字符串的那一行   ( -name后面可以写  "*.*"   即匹配所有的文件 ) find / -name "*.conf& ...