【String类】java 8 源码

2019-01-02  本文已影响0人  静筱

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

Java类定义

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
屏幕快照 2019-01-02 下午2.28.26的副本.png

假设有这样一个类,实现了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接口的类所构成的数组(Array)或者列表(List)可以使用Collections.sort()或者Arrays.sort()方法进行自动排序。
实现了Comparable接口的类的对象可以在SortedMap和SortedSet里作为key值,而不用指定Comparator.

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

该接口定义了有序字符串上常见操作,比如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

重要方法解析

    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

 /**
     * 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方法导致的内存问题

深度探秘

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

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

上一篇 下一篇

猜你喜欢

热点阅读