JDK8源码解析-深入探索String内部变量(二)

       源码解析基于jdk1.8.0_261进行分析,如果各位读者的jdk是该版本之前或之后的,可能与本文有所出入,但终究是一脉相承,万世一系
 
前言
 
        为了贯穿上下文,在阅读此篇文章之前,推荐各位看客优先看下 JDK8源码解析-String-修饰符及接口实现(一)
本次我们要讲的是String类的成员变量,因篇幅较广,构造函数我们放在下一期讲解,本期让大家知晓在java中如果new String() 或者定义一个String类型的常量,jvm在栈,堆及常量池中的使用
 
 
成员变量
 
        在讲解之前,我们先粘个截图,让大家看下,jdk8 中String类的成员变量
 
        从截图中我们可以看出,目前String类中共定义了5个成员变量,都是private修饰的,即每个成员变量,都不可以被外部所引用,其中有4个成员变量是final修饰的,上一篇文章我们已经介绍过,final修饰的变量是无法修改,那有些的同学就问题了,最后一个不是final修饰的变量是做什么用的,看上面截图,有个hash,这是干什么的呢?说到这个hash,通过代码上的注释,我们可以了解到 “缓存字符串的hash code”,那hash code又是什么?
 
private int hash
 
        想要了解hash code ,那我们需要了解什么是hash? 说得通俗一下,hash其实就是一种算法,音译为”哈希",也可以称呼为:散列算法,如果有同学是密码学专业的,可能散列算法熟悉到家了,如果不太熟悉,那没关系,我们这里也会稍微给大家带一下散列的逻辑,但不会太细致地说散列,毕竟我们今天的主题是String的成员变量及构造函数,如果感兴趣,后期我会补上hash算法的文章内容
 
         hash算法:就是将做任意长度的消息压缩到某一固定长度的值。hash算法有优点也有确定,它可以使用有限的计算机资源且有限时间内,得到hash值,且hash值不可逆(因为可能同一个hash code,对应这多个不同的明文),但它也是有缺点的,正如上面所说,同一个hash code ,可能对应着不同的明文,这就会存在哈希碰撞,如果在Java中出现类似的问题,推荐大家重写hashCode()方法,hashCode在java中是Object类的方法,每个类都是Object类的子类,我们只需要重写hashCode的方法即可,如何重写,先欠着,我后期会大家补上.
 
        通过上面的介绍,大家可能对这里的hash变量有所了解,那就是存储hashCode值的.那是不是每个String变量都会有hash值呢?当然,不只是String类,Java中的每个类的实例化都会有hashCode.
 
 
private final char value[];
 
      介绍完hash code ,我们来了解下这里的value.   
        可能有人面试时候,面试官会问,String底层是用什么实现的,看源码即可得知,底层是一个char类型的数组,且是final类型,不可修改,不过有一点,如果有面试官问 String中的 “+” 实现原理,那可不是char类型的数据了,而是用StringBuilder拼接出来的,这个点需要注意下,后面我们聊StringBuilder时,会细细的聊下String和StringBuilder的关系.
        我们每个字符串,都会将其转为char类型,如果有同学debug的话,查看String修饰的变量,就会看到实际上是存储到value中的
 
 
 
private static final long serialVersionUID = -6849794470754667710L;
 
        上节我们提到java.io.Serializable,这个接口是一个标志性接口,没有定义任何属性与方法.只是用于标识此接口的实现类可以被序列化与反序列化表示序列化.那什么是序列化,什么是反序列化呢?
 
        首先要阐明实现Serializable接口的目的:就是为了数据的持久化.如保存到数据库或进行远程网络传输.我们用一个小故事来讲吧.
 
        我住在北京的清河某小区,有天我觉得想换个居住环境,于是我搬到了上地嘉园,那我需要做什么呢?我需要将清河家中的所有物件,该打包的打包,该装箱子的装箱子,并列了个清单明细,交给搬家师傅,告诉他,这一车里有冰箱,衣柜,电视,桌子等等,司机师傅拿着清单开车来到上地嘉园,将清单交给我事先安排的朋友,我朋友拿到清单,对货车上的物件,一件一件地进行比对,发现少了个摄像头,我朋友不干了,气呼呼地说你这车上少东西了,赶紧给我找找,不然我们给你扣钱,这一听,搬家师傅也急了,急忙开始在车上找,最后发现摄像头在货车一块帘布下面,遂将摄像头交给了我朋友,我朋友闲暇没事,就在我新家中帮忙组装桌子,电视机等...
 
 
将冰箱,衣柜,电视,桌子来类比我们的数据,进行打包的操作就是序列化
明细清单 可以理解为serialVersionUID,作为唯一的标识
货运师傅将货车从清河某小区,开到上地家园,这就是网络传输
搬家师傅送到上地嘉园后,我朋友发现少了摄像头,与清单上写的不一致(serialVersionUID),我朋友便很生气的报了一个InvalidCastException
师傅找到摄像头后,给了我朋友,我朋友在新家中帮忙组装桌子,电视机等,这就是反序列化
 
        通过以上小故事,大家可能有些明了了,serialVersionUID:在进行反序列化时,JVM会把传来的字节流中的serialVersionUID 和本地实体类的serialVersionUID进行比较,如果相同说明是一致的,可以进行反序列化,否则就会出现:InvalidCastException异常
 
private static final ObjectStreamField[] serialPersistentFields new ObjectStreamField[0];
 
        通过代码上的注解,我们可以了解到:Serializable 类中 Serializable 字段的描述.ObjectStreamField 的数组用于声明类的 Serializable 字段.换言之:这个用来保存要进行序列化的字段.默认情况下,所有的非 transient 非 static 修饰的字段都会被序列化,但可以用这个来进行选择性序列化的字段。目前String类中只是定义了该属性,并没有添加字段描述,我们暂且不去聊它,后期我们用到的时候,再聊聊它.
 
 
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
 
        给String方法compareToIgnoreCase 提供了一种对字符串对象忽略大小写的排序比较器.后期我们将方法compareToIgnoreCase的时候聊下这个比较器的实现,下面为原始代码,感兴趣的小伙伴可以先看一下
        
 
总结:
  1. String底层由final char数组实现
  2. String的 “+” 逻辑实际为StringBuilder的拼接(后期我们详细介绍)
  3. hash算法,就是将做任意长度的消息压缩到某一固定长度的值
  4. serialVersionUID为了数据的持久化及网络传输做版本验证
  5. ObjectStreamField 设置选择行序列化字段(后期我们详细介绍)
  6. String中compareToIgnoreCase的比较器,在String中实现(后期我们讲到此方法时详细介绍)
 
 
 
 
        如果有小伙伴们对以上描述有所疑惑的,可以在文章下方留言,感谢各位支持
 
 
您还没有登录,请先 登录或者 注册后,添加评论