xml地图|网站地图|网站标签 [设为首页] [加入收藏]

String真的是不可变的吗,一文读懂String及其包装

来源:http://www.ccidsi.com 作者:集成经验 人气:94 发布时间:2019-05-29
摘要:您也许问一个人String是可变的吗?想必他们都会一口同生的说String是不可变的,因为String是final修饰的,而且它底层的是final修饰的char[]数组。 1. 导读 String类也是惯常开销中常常选用的

您也许问一个人String是可变的吗?想必他们都会一口同生的说String是不可变的,因为String是final修饰的,而且它底层的是final修饰的char[]数组。

1. 导读

String类也是惯常开销中常常选用的类, 明天器重分享下作者在看String源码时想到的四个难点:一.一String为啥是不可变的; 为啥要规划成不可变的;1.2 hashCode; 为何是31;

图片 1

能够看到String源码:

二. String为何是不可变的;

 public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];

那是String源码的前三行代码, 那3行代码给出八个至关心珍视要新闻:.一String类是被final修饰的, 不可被接二连三;.2String字符串的最底层达成是依附char数组的;.三 char数组也是被final修饰的, 他的引用是不足被涂改的;.四 value[]被private修饰, 寻遍整个String类, 没有提供value[]公共的getter 和 setter方法, 那么他的值是不可被外表修改的;基于以上四点, 大家能够找到String是不可变的因由: String类不可被持续, 那么就不存在引用到StringChild这种子类, 换言之任何1个宣称的String类型引用, 他援引到的目的自然是String对象, 而不会是其余对象;当二个String对象被创制时, 其底层的value[]被赋值, 被final修饰, 其引用始终对准同二个内部存款和储蓄器地址, 并且String未有提供修改value的秘籍, 可以以为value[]数组的值是不行被改变的(倘让你非要说通过反射能够修改, 这作者不得不说反射这种方法不在符合规律的设想范围内);

划入眼:.一 基于String整个类, 被final修饰不可一而再, 那么String类正是不可变类;.2 基于String对象, 底层的value[]被final修饰, 不对外提供修改value[]的方法, value[]引用不改变其值不改变, 那么整个String对象也就是不可变的;

图片 2

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

三. String为啥设计成不可变

地方表达了为啥String是不可变的, 不过为啥要把String设计成不可变的啊? 说不准有人要求本性化定制吗?

本身想了下, String类的撰稿人大概有已下几地点的设想:.一 线程安全: 大家都清楚因为共享变量大概会被四个线程修改, 会引起线程安全题材, 把String设计成不可变未来, 就没有须要考虑八线程的安全性了, 因为各样线程唯有读的权限;

.二 关怀下源码中的hash:

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

hash的功用是将日前String对象的hash值缓存起来, 因为String是不可变的, 因此缓存的hash也是不改变的; 那么把hash起来有啥用呢? 举个HashMap的例子, 当String作为HashMap的key时, 数次调用String::hashCode时, 只会在第贰遍总计hash, 后边全体的调用都会取这些Key缓存的hash, 大大升高了成效;

.3 在JVM设计时, 对String有个优化处理:设计了一个String常量池, “”评释的String对象会先去常量池找寻, 不设有则放入常量池, 反之则取常量池中的String对象;纵然10K个"str"的引用, 无需在堆中new 十K个String对象, 而只必要将引用指向同3个String对象就能够;String::intern方法做的也是一样的事务, 关于JVM对这种规划的变迁以及优缺点放到分享intern方法时再说;

.四 在选拔int时, 大家是去思考int是不是可变的, 因为在规划时, int类型就曾经持有了不可变性了, 所以在统一计划Integer这些封装类时, Integer也被规划成了不可变类(前面会有特地分享, 这里不开始展览);而String在java世界中的受招待程度和着力数据类型没什么差别, 乃至有个别java的底层设计都以依照String的, 比方反射的类路线等等; 而且想像一下前壹秒还在对String判空, 下1秒调用时抛出了NPE, 这种时候你会不会问候下String的设计者;从言语设计规模考虑, String在规划之初正是可望把她作为基本数据类型来选择, 那么不可变性是必须的;

.伍 在java的别的设计中也利用了String, 依然拿HashMap举个例子, 有个节点A的key是"一", value是二; 假诺String是可变的, A的key产生了"二", 那时插入的多寡, 会将原先的多寡覆盖, 但依照插入之初的值, 应该新增添一个节点的;以至HashMap中有八个节点A, B, 要是String可变, A节点产生了; 使用"一"去赢得时开掘数目不设有, 使用"叁"获取时收获的是漏洞百出数据;上述三种状态分明违反了HashMap设计的初衷, HashMap希望将数据缓存起来, 再一次获得时能够取到正确的值, 这是依附key的不可变达成的; 所以了社会风气的和平, String的设计者就不通熊孩子的念想, 把String设计成了不可变;划重视:.壹String在JAVA语言设计时就预订了不可变;.2String的不可变性保障了线程安全;.叁 String的不可变性提供了不改变的hash, 落成了hash的懒加载, 升高了效能;.4JVM的String常量池是依附String的不可变性完成的;.5String的不可变性维护了社会风气的和平, 幸免JAVA底层的局地企划出难点(也许有希望String的小编以为本身的筹算相当厉害, 幸免艺术被污染, 把String设计成了不可变);

图片 3

就此说String是不足变得,可是大家忽略了反光,利用反射大家能够变动String的值,话不多说看反射是什么样成功的:

四. hashCode以及为啥是3一

 public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i  ) { h = 31 * h   val[i]; } hash = h; } return h; }

通过hashCode的源码, 我们得以见到:.1 那是对Object::hashCode的重写;.二完结了懒加载, 唯有在第四回调用时才会持筹握算String对象的hash值, 以往具有的调用都会重临缓存的hash值;.三String::hashCode的算法是:value[0]31^ value[1]3一^ ... valuen-一;.四 3一? String::hashCode的算法中31老大眼看, 在看源码的时候会想怎么用3壹?四.一 3一*h能够被JVM优化成 (h << 5) - h; 远近有名, JAVA中的位运算的快慢比乘法运算要快; 那是五个效用优化的设想; 那么那又引出了另一个主题素材: 陆三 可能 壹伍都能够被JVM优化, 照旧不曾解答为何接纳3一;肆.2String的hashCode其实用到了三个字符串散列化的算法----djb贰; 那是由丹尼尔勒J. Bernstein提议字符串散列算法; 感兴趣的能够去wiki;

以此算法用大白话讲正是字符串不断得乘3三, 所以又被叫作"Time3三"算法;看到这里, 是或不是意识String::hashCode只是把3三替换到了31, 本质还是"Time3三";

划注重:.1 String::hashCode同一个String对象只会持筹握算三回hash;.二使用了"Time3三"算法来重写Object::hashCode;

好了这次分享就到这里, 上面表述有怎么着难题, 迎接指正; 若还大概有String的标题也招待一齐搜求, 谢谢阅读;

String作为Java中最常用的引用类型,相对来讲基本上都相比较熟知,无论在经常的编码进程中依旧在笔试面试中,String都十分受到赏识,但是,在动用String进度中,又有较多须求专注的底细之处。

public static void main(String[] args) throws Exception {
        // 创建字符串"Hello World", 并赋给引用s
        String s = "Hello World";
        //把这个s保存一份用于对比
        String temp = s;
        System.out.println("s和temp是否相等? "   s.equals(temp));
        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
        System.out.println("s和temp是否相等? "   s.equals(temp));
    }
@Testpublicvoid contact () { //1连接方式 String s1 = "a"; String s2 = "a"; String s3 = "a"   s2; String s4 = "a"   "a"; String s5 = s1   s2; //表达式只有常量时,编译期完成计算 //表达式有变量时,运行期才计算,所以地址不一样 System.out.println; //f System.out.println; //f System.out.println(s4 == "aa"); //t}

publicvoid intern () { //2:string的intern使用 //s1是基本类型,比较值。s2是string实例,比较实例地址 //字符串类型用equals方法比较时只会比较值 String s1 = "a"; String s2 = newString; //调用intern时,如果s2中的字符不在常量池,则加入常量池并返回常量的引用 String s3 = s2.intern(); System.out.println; System.out.println;}

//字符串的equals方法// public boolean equals(Object anObject) {// if (this == anObject) {// return true;// }// if (anObject instanceof String) {// String anotherString = anObject;// int n = value.length;// if (n == anotherString.value.length) {// char v1[] = value;// char v2[] = anotherString.value;// int i = 0;// while  {// if (v1[i] != v2[i])// return false;// i  ;// }// return true;// }// }// return false;// }

本文由68399皇家赌场发布于集成经验,转载请注明出处:String真的是不可变的吗,一文读懂String及其包装

关键词: 读懂 java 基础 String 基本功

上一篇:写给自己的面试题,Java个人知识点总结

下一篇:没有了

最火资讯