美文网首页
String类源码笔记(一):成员变量和构造器

String类源码笔记(一):成员变量和构造器

作者: LuckyBuzz | 来源:发表于2020-04-17 17:47 被阅读0次

    String类表示字符串,所有类似"abc"形式的字符串(或魔法字符串)都被看作是这个类的实例。String是不可变的,当一个字符串在常量池中被创建时,他的值就不会被改变。

    不可变类指的是其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并且在对象的整个生命周期内固定不变。为了使类不可变,要遵循下面五条规则:
    1. 不要提供任何会修改对象状态的方法
    2. 保证类不会被扩展。 一般的做法是让这个类称为 final 的,防止子类化,破坏该类的不可变行为。
    3. 使所有的域都是 final 的
    4. 使所有的域都成为私有的。 防止客户端获得访问被域引用的可变对象的权限,并防止客户端直接修改这些对象。
    5. 确保对于任何可变性组件的互斥访问。 如果类具有指向可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用。

    public final class String implements java.io.Serializable, Comparable<String>, CharSequence
    

    所在路径:\java\lang\String.java

    String类的成员变量 String类的构造器

    一、成员变量

    为了保证String类是一个不可变类,String类的成员变量多为私有和不可变的。

    /** 存储String对象的值 */ 
    private final char value[]; 
    /** hashcode */ 
    private int hash; 
    /** serialVersionUID */ 
    private static final long serialVersionUID = -6849794470754667710L;
     /** 序列化时使用,表明需要序列化的部分 */ 
    private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
    

    其中serialPersistentFields在序列化时使用:

    For class that implements Serializable interface there are 2 ways to define what specific fields get streamed during the serialization:
    1、By default all non-static, non-transient fields that implement Serializable are preserved.
    2、By definning ObjectStreamField [] serialPersistentFields and explicitly declaring the specific fields saved.
    The 'advantage' is that it does what it says in the Javadoc: defines which fields are serialized. Without it, all non-transient non-static fields are serialized. Your choice.

    二、构造器

    JDK8的String类一共有16个构造器,其中两个是@Deprecated,一个是私有构造器,剩下的13个是可以调用的。

    1、无参构造器

    无参构造器直接将""的value赋值给当前类的value。""的value是一个空的char[],其length为0。调用使用了无参构造器的String对象的isEmpty()方法会得到true,调用length()方法会得到0,判断其==null会得到false。

    public String() { 
        this.value = "".value; 
    }
    
    2、入参为String对象

    入参为String对象时,构造器会对其进行直接取值。

    public String(String original) { 
        this.value = original.value; 
        this.hash = original.hash; 
    }
    
    3、入参为char[]

    入参为字符串数组时,构造器调用的是Arrays.copyOf()方法。

    public String(char value[]) { 
        this.value = Arrays.copyOf(value, value.length);
     }
    

    其中Arrays.copyOf()方法是为了将入参的字符串序列深拷贝到this.valuie中,他的源码为:

    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    

    其中System.arraycopy()方法的源码为:

    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
    

    String和char[]的相互转换:
    https://www.cnblogs.com/rrttp/p/7922202.html

    4、入参为char[],偏移量和有效长度

    这个方法支持直接传入想要生成的String的母串,通过偏移量和有效长度找出需要赋值给this.value的部分,然后调用Arrays.copyOfRange()方法进行深拷贝。

    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }
    
    5、其他

    当需要将一个Unicode编码序列转换为String时,可以使用以下构造器:

    public String(int[] codePoints, int offset, int count)
    

    参考:https://blog.csdn.net/qq_39477410/article/details/82469104

    当需要将一个bytes[]转换为String时,可以使用以下构造器:

    // 可以传入JDK支持的字符集名称
    public String(byte bytes[], int offset, int length, String charsetName)
        throws UnsupportedEncodingException
    
    // 也可以传入自定义的解码字符集
    public String(byte bytes[], int offset, int length, Charset charset)
    

    此外,还有一些将上述构造器进一步封装的构造器,其本质都是简化入参。另外,String类的构造器同样支持传入StringBuffer和StringBuilder。如果传入的是StringBuffer,构造器会为其加锁。如果传入的是StringBuilder则不会加锁。

    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
    
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }
    

    事实上,StringBuffer和StringBuilder的toString()方法调用的也是String类的构造器,他们最终的底层实现都是Arrays.copyOf()。

    // StringBuffer的toString()方法
    @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
    
    // StringBuilder的toString()方法
    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
    

    最后,String类还提供了一个保护类型的构造方法。该方法相比入参为char[]的构造器多了一个share参数,这个参数并没有实际作用,只是用来和其他构造器进行区分。当String类内部调用该构造器时:

    • 直接赋值而不是使用Arrays.copyOf()方法可以获得更高的效率;
    • 同时共享字符串数组的方式也可以节省内存。

    该构造器不能对外暴露的原因是需要保持String类的不可变性。

    /*
    * Package private constructor which shares value array for speed.
    * this constructor is always expected to be called with share==true.
    * a separate constructor is needed because we already have a public
    * String(char[]) constructor that makes a copy of the given char[].
    */
    String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = value;
    }
    

    相关文章

      网友评论

          本文标题:String类源码笔记(一):成员变量和构造器

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