美文网首页
【String类】java 8 源码

【String类】java 8 源码

作者: 静筱 | 来源:发表于2019-01-02 17:17 被阅读0次

    本文基于jdk 1.8.0_181全面分析String源码

    Java类定义

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
    
    • final
      String类是不可变的(Immutable),不可变的好处有很多, 以及String类的不可变是怎么实现的,详见:。
      将String类定义成final的,就是保证String类不可变的一个重要因素 。

      final关键字的好处,及详细分析,详见:tbd
      此处final用于修饰String类时,表示String类不能被继承,否则会有编译期错误

    屏幕快照 2019-01-02 下午2.28.26的副本.png
    • 实现Serializable接口
      Serializable是一个标识性接口,实现了这个接口的类被标识成可序列化的类。

    假设有这样一个类,实现了Serializable接口:

    import java.io.*;
    import java.util.Date;
    
    public class SerializableTest implements Serializable {
    
        private String testName ;
    
        private int testNum;
    
        public String getTestName(){
            return this.testName;
        }
    
        public int getTestNum(){
            return this.testNum;
        }
    
        public void setTestName(String name){
            this.testName = name;
        }
    
        public void setTestNum(int num){
            this.testNum = num;
        }
    
        public String toString(){
            return testName + ", " + testNum;
        }
    
        public static void main(String[] args){
            SerializableTest test = new SerializableTest();
            test.setTestName("seeMe?");
            test.setTestNum(10000);
    
            serialize(test,"serializeTest.ser");
            deserialize("serializeTest.ser");
    
        }
    }
    

    也就是说可以通过以下方式将类的对象在内存中的状态保存下来:

     private static void serialize(SerializableTest test, String s) {
    
            FileOutputStream fs = null;
            try {
                fs = new FileOutputStream("serializeTest.ser");
                ObjectOutputStream os = new ObjectOutputStream(fs);
    
                os.writeObject("testSTring");
                os.writeObject(new Date());
                os.writeObject(test);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    

    也可以通过以下方式,将一个对象在内存中恢复(注意,不只限于本机本地恢复,在其他机器上只要有该类的定义,都可以恢复原来的对象):

        private static void deserialize(String s) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(s);
                ObjectInputStream ois = new ObjectInputStream(fis);
                String str = (String) ois.readObject();
                Date date = (Date) ois.readObject();
                System.out.println(str);
                System.out.println(date.toString());
                SerializableTest test = (SerializableTest)ois.readObject();
                System.out.println(test.toString());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    

    关于java序列化的深入研究,详见:

    • 实现Comparable接口

    实现了Comparable接口的类所构成的数组(Array)或者列表(List)可以使用Collections.sort()或者Arrays.sort()方法进行自动排序。
    实现了Comparable接口的类的对象可以在SortedMap和SortedSet里作为key值,而不用指定Comparator.

    此处Java实现了Comparable接口,方便进行字符串的比较(高频操作),以及使用String作为SortedMap或者SortedSet的key值。

    • 实现CharSequence接口
      CharSequence实际上代表一组有序的字符数组,其实现类主要有三个,分别是String,StringBuffer,StringBuilder。

    该接口定义了有序字符串上常见操作,比如length(长度),charAt(取第几个字符),方便String,StringBuffer,StringBuilder进行一些比较、拼接等操作。
    (没有CharSequence这层抽象,需要先判断对象属于哪个类,再调用该类的同类操作的具体实现; 有了这层抽象,直接调用接口中的方法就可以了,运行时自动会调用对象所属类的具体实现)

    此处需要注意的是,java 8引入了codepoint概念。常规的char对应2个字节,但是有一些特别的unicode字符,比如emoij表情符号,对应4个字节。

    并提供了相应的获得当前字符串的codepoint流的方法,codePoint(), 以及获得当前字符串的char流的方法,chars()。

    成员变量

     /** The value is used for character storage. */
    
        private final char value[]; //字符串实际值
    
        /** Cache the hash code for the string */
        private int hash; // Default to 0  //字符串的哈希值
    
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L; //实现序列化的标识
    

    本质上一个字符串,底层就是靠char[]实现的。

    构造函数

    image.png
    • 通过无参数构造函数new String()初始化对象时不会报错,而其他构造函数,有可能报NullPointerException。
    • 各构造函数对应内存分配情况,详见:
      Java内存详解 - 内存分区实例之String

    重要方法解析

    • hashCode()
        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;
        }
    

    String不可变,因此初始化String对象时,就会计算其hascode,避免重复计算对象的hash值。

    另外这个方法比较有意思的是乘积因子为何会选择为31.
    简要的说有两个原因:

    1. 31是个适合的质数,数值太小,哈希值可能值的范围就较小。 数值太大,哈希值可能值会超过int型的最大数值。
    2. 31可被jvm优化,为2<<5-1

    详见:https://www.cnblogs.com/nullllun/p/8350178.html

    • intern()
      本地方法。
      获得字符串对应的字符串常量池中的位置,避免重复创建字符串对象。
     /**
         * Returns a canonical representation for the string object.
         * <p>
         * A pool of strings, initially empty, is maintained privately by the
         * class {@code String}.
         * <p>
         * When the intern method is invoked, if the pool already contains a
         * string equal to this {@code String} object as determined by
         * the {@link #equals(Object)} method, then the string from the pool is
         * returned. Otherwise, this {@code String} object is added to the
         * pool and a reference to this {@code String} object is returned.
         * <p>
         * It follows that for any two strings {@code s} and {@code t},
         * {@code s.intern() == t.intern()} is {@code true}
         * if and only if {@code s.equals(t)} is {@code true}.
         * <p>
         * All literal strings and string-valued constant expressions are
         * interned. String literals are defined in section 3.10.5 of the
         * <cite>The Java&trade; Language Specification</cite>.
         *
         * @return  a string that has the same contents as this string, but is
         *          guaranteed to be from a pool of unique strings.
         */
        public native String intern();
    

    备注:访方法初衷是提高对象创建的性能,以及重用内存。实际上使用不当会导致内存问题,详见:Java基础源码分析2 - 【String类】intern方法导致的内存问题

    • subString
      本方法需要注意的是在java 6上如果使用不当可能导致OutOfMemory问题
      详见:Java基础源码分析1 - 【String类】subString导致OutOfMemory

    深度探秘

    • 都说Java中String是不可变的?它果真是不可变的么?

    详见:java基础源码分析4 - 【String类】真的不可变么?

    https://www.cnblogs.com/dolphin0520/p/3778589.html

    相关文章

      网友评论

          本文标题:【String类】java 8 源码

          本文链接:https://www.haomeiwen.com/subject/wdttrqtx.html