IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Java中的String为什么要设计成不可变的 -> 正文阅读

[数据结构与算法]Java中的String为什么要设计成不可变的

一、不可变类和不可变对象

创建一个一旦其内容就不能在改变的对象,称其为一个不可变对象(immutable object),而它的类称为不可变类(immutable class)。

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数组,是对字符串数组的封装,并且是被final修饰的,创建后不可改变。

二、String类不可变性的好处

Java中将String设计成不可变的是综合考虑到各种因素的结果,想要理解这个问题,需要综合内存,同步,数据结构以及安全等方面的考虑

1、字符串常量池

在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆内存中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。

		String str1 = "abc";
        String str2 = "abc";
        String a = "ab";
        String b = "c";
        String str3 = a+b;
        String str4 = "abcd";
        System.out.println("str1地址:"+System.identityHashCode(str1));
        System.out.println("str2地址:"+System.identityHashCode(str2));
        System.out.println("str3地址:"+System.identityHashCode(str3));
        System.out.println("str4地址:"+System.identityHashCode(str4));
        System.out.println("str1.hashCode():"+str1.hashCode());
        System.out.println("str2.hashCode():"+str2.hashCode());
        System.out.println("str3.hashCode():"+str3.hashCode());
        System.out.println("a.hashCode():"+a.hashCode());
        System.out.println("str1 == str2:"+(str1 == str2));
        System.out.println("str1.equals(str2):"+str1.equals(str2));
        System.out.println("str3 == str1:"+(str3 == str1));
        System.out.println("str3.equals(str1):"+str3.equals(str1));
        str1 = "abcd";
        System.out.println("更改后的str1地址:"+System.identityHashCode(str1));
        System.out.println("更改后的str1.hashCode():"+str1.hashCode());
        System.out.println("str1 == str4:"+(str1 == str4));
        System.out.println("str1.equals(str4):"+str1.equals(str4));
        str2 += "d";
        System.out.println("更改后的str2地址:"+System.identityHashCode(str2));
        System.out.println("更改后的str2.hashCode():"+str2.hashCode());
        System.out.println("str1 == str2:"+(str1 == str2));
        System.out.println("str1.equals(str2):"+str1.equals(str2));

输出:

str1地址:23934342
str2地址:23934342
str3地址:22307196
str4地址:10568834
str1.hashCode():96354
str2.hashCode():96354
str3.hashCode():96354
a.hashCode():3105
str1 == str2:true
str1.equals(str2):true
str3 == str1:false
str3.equals(str1):true
更改后的str1地址:10568834
更改后的str1.hashCode():2987074
str1 == str4:true
str1.equals(str4):true
更改后的str2地址:21029277
更改后的str2.hashCode():2987074
str1 == str2:false
str1.equals(str2):true

这个例子可以看到我们想要的结果:如果字符串是可变的,某一个字符串变量改变了其值,那么其指向的变量的值也会改变,String pool将不能够实现!
在这里插入图片描述

str1和str2初始化都是"abc",abc在堆中存在,所以地址是相同的,他们的hashcode()也是相同的。所以str1 == str2:true ;str1.equals(str2):true他们的比较都是true;

- “==” 是判断两个变量是否指向同一个地方,即存储位置。也就是说是否引用同一个变量。

- equals() 是判断两个String类型字符串的内容是否一样。

但是当str3为字符串“ab”+“c”时,是一个新的地址,所以str3 == str1:false,(虽然str3.hashCode()和str1.hashCode()相同,但是地址不同),下面同理。

2、使多线程安全

public class Stringtest {
    public String appendStr(String s){
        s += "efg";
        return s;
    }
    public StringBuilder appendSb(StringBuilder sb){
        sb.append("efg");
        return sb;
    }

    public static void main(String[] args) {
        Stringtest stringtest = new Stringtest();
        String str = "abcefg";
        System.out.println("str地址:"+System.identityHashCode(str));
        String str1 = new String("abc");
        StringBuilder  str2 = new StringBuilder("abc");
        System.out.println("str1地址:"+System.identityHashCode(str1));
        System.out.println("str2地址:"+System.identityHashCode(str2));

        System.out.println("str1 == str2:"+(str1 == str2.toString()));
        System.out.println("str1.equals(str2.toString()):"+str1.equals(str2.toString()));

        String newstr1 = stringtest.appendStr(str1);
        System.out.println("newstr1地址:"+System.identityHashCode(newstr1));


        StringBuilder newstr2 = stringtest.appendSb(str2);
        System.out.println("newstr2地址:"+System.identityHashCode(newstr2));
    }
}

输出:

str地址:23934342
str1地址:22307196
str2地址:10568834
str1 == str2:false
str1.equals(str2.toString()):true
newstr1地址:21029277
newstr2地址:10568834

发现当对String字符串进行修改,地址改变,但是当对StringBuilder字符串进行修改,字符串的地址并没有改变。因为Java对象参数传的是引用,所有可变的StringBuffer参数就被改变了。可以看到变量str2在appendSb(str2)操作之后,就变成了"abcefg"。有的时候这可能不是程序员的本意。所以String不可变的安全性就体现在这里。

3、避免安全问题

在网络连接和数据库连接中字符串常常作为参数,例如,网络连接地址URL,文件路径path,反射机制所需要的String参数。其不可变性可以保证连接的安全性。如果字符串是可变的,黑客就有可能改变字符串指向对象的值,那么会引起很严重的安全问题。
因为String是不可变的,所以它的值是不可改变的。但由于String不可变,也就没有任何方式能修改字符串的值,每一次修改都将产生新的字符串,如果使用char[]来保存密码,仍然能够将其中所有的元素设置为空和清零,也不会被放入字符串缓存池中,用字符串数组来保存密码会更好。

4、加快字符串处理速度

由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-09 12:59:06  更:2022-05-09 13:00:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:40:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码