String源码阅读(一)

本节只介绍String的构造方法。 

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

    String实现了Serializable, Comparable<String>, CharSequence三个接口。

    String中的字符串值存储在常量池中,jvm的PermGen 中,不同的类都有不同的常量池。new String(“abc”)在堆中存储了一个指向常量池中“abc”的指针。

    其中charSequence是一个接口,表示char值的一个可读序列。此接口对许多不同种类的char序列提供统一的自读访问。此接口不修改该equals和hashCode方法的常规协定,因此,通常未定义比较实现 CharSequence 的两个对象的结果。他有几个实现类:CharBuffer、String、StringBuffer、StringBuilder。

 CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。

 对于一个抽象类或者是接口类,不能使用new来进行赋值,但是可以通过以下的方式来进行实例的创建: 
  CharSequence cs=”hello”;
 

//value存储String的值
private final char value[];

   //hashcode
    private int hash; // Default to 0

   //序列化ID
    private static final long serialVersionUID = -6849794470754667710L;

   //声明序列化字段
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
//返回空的字符串对象,文档中不推荐使用 
public String() {
        this.value = "".value;
    }

//返回相同String值的不同引用,不推荐使用
 public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

//复制一个新的字符数组给String的value成员,这样改变参数中的数组后不会影响到String字符串
 public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

//从offest开始截取count个字符来创建String
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);
    }

 

传入参数为代码点。

代码点即为Unicode中的字符编号,代码单元指字符可以被编码的最小单元,例如UTF-8中一个字符可以被编码为1个字节或两个字节,3个,4个....,UTF-16中一个字符可以被编码成两个字节,3个。。。。。。。

String内部采用char数组形式存储Unicode字符串,由于char是16位,也可以说是UTF-16编码。但并不是一个char存储一个字符,当字符在BMP范围以外时,会用两个char存储一个字符。

//传入代码点,codePoints数组中的截取位置与截取数量
public String(int[] codePoints, int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= codePoints.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count;

        // Pass 1: Compute precise size of char[]
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            //判断代码点是否在Basic Multilingual Plane(BMP)中
             //在BMP范围内的字符,可以用4位十六进制数表示(16bit),而在BMP以外的字符,需要不止 
               //4位十六进制数表示。
            if (Character.isBmpCodePoint(c))
                continue;
            //判断代码点是否为一个有效的Unicode代码点的值
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            //如果代码点在Basic Multilingual Plane(BMP)中则转化为char放入v中
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            //否则说明代码点不在Basic Multilingual Plane(BMP)中,但是一个有效的Unicode代码 
               //点值
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }

 

传入参数为字节数组。

decode为将字节转化为字符,encode为将字符转化为字节。

//传入字节数组,截取位置以及截取长度以及编码方式
public String(byte bytes[], int offset, int length, String charsetName)
            throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        //检查截取位置以及截取长度是否合法,如不合法则抛出异常
        checkBounds(bytes, offset, length);
        //调用StringCoding的decode方法
        this.value = StringCoding.decode(charsetName, bytes, offset, length);
    }

StringCoder类的结构,其中有两个静态内部类,StringDecoder与StringEncoder。

StringCodeing使用ThreadLocal来保证线程安全,使用SoftReference来保证内存不足时GC,来防止OOM。ThreadLocal以后会写。

StringDecoder的成员以及构造方法。

首先通过deref当前线程threadlocal中的StringCoder对象,如果当前线程中没有StringCoder对象,或者参数传入的编码与当前线程中的StringCoder编码不一致,那么就新建一个StringCoder对象,并放入当前线程中。并调用重载方法decode。

static char[] decode(String charsetName, byte[] ba, int off, int len)
        throws UnsupportedEncodingException
    {
        
        StringDecoder sd = deref(decoder);
        //默认编码为ISO-8859-1
        String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
        if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
                              || csn.equals(sd.charsetName()))) {
            sd = null;
            try {
                Charset cs = lookupCharset(csn);
                if (cs != null)
                    sd = new StringDecoder(cs, csn);
            } catch (IllegalCharsetNameException x) {}
            if (sd == null)
                throw new UnsupportedEncodingException(csn);
            set(decoder, sd);
        }
        return sd.decode(ba, off, len);
    }

此方法为decode的重载方法,并且为对象方法。

 char[] decode(byte[] ba, int off, int len) {
            //获取len与当前字符编码最大字节数的乘积
            int en = scale(len, cd.maxCharsPerByte());
            char[] ca = new char[en];
            if (len == 0)
                return ca;
            /*如果当前字符编码属于ArrayDecoder接口(ArrayDecoder可自行去看源码)
            *则使用ArrayDecoder中定义好的API
               */
            if (cd instanceof ArrayDecoder) {
                int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
                /*safeTrim为通过返回的长度以及ca数组的长度以及isTrusted和管理员权限来判断
                  *返回当前ca数组或者Arrays.copyOf一个新数组。
                 */
                return safeTrim(ca, clen, cs, isTrusted);
            } else {
                /*如果当前编码不属于ArrayDecoder,则调用CharsetDecoder的decode方法
                 *
                 */
                cd.reset();
                ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
                CharBuffer cb = CharBuffer.wrap(ca);
                try {
                    CoderResult cr = cd.decode(bb, cb, true);
                    if (!cr.isUnderflow())
                        cr.throwException();
                    cr = cd.flush(cb);
                    if (!cr.isUnderflow())
                        cr.throwException();
                } catch (CharacterCodingException x) {
                    // Substitution is always enabled,
                    // so this shouldn't happen
                    throw new Error(x);
                }
                return safeTrim(ca, cb.position(), cs, isTrusted);
            }
        }
    }

原理与上面相同,不同的是传入编码对象而不是编码名称。

public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }

传入byte数组的构造方法好有很多,但原理相同,这里就不一一列举了。如果不传入编码方式,默认为"ISO-8859-1"。

 

传入参数为StringBuffer,由于StringBuffer线程不安全,所以要使用synchronized锁来保证线程安全。

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

 

传入参数为 StringBuilder。。

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

 

最后问一个困扰许久的问题,在String源码中打断点时发现构造方法参数的值与我传入的不同。求有缘的大牛帮我解答一下。太感谢!!!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值