Java基础之String、StringBuffer、StringBuilder

本系列博客仅仅是自己重学Java所记的笔记和思考,如果对您有帮助,我将会很荣幸,以下的分析均以JDK1.8为准,若有偏颇,欢迎指正。

一、String详解

JDK1.8_101上的String类的定义如下,从源码上看出String类使用final关键字修饰,说明String类不可继承,String类的value属性使用private final修饰,则String是一个不可变类,但是String类真的是一个不可变类么?String类的不可变性真的是使用private final修饰决定的么?

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** 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;

    /**
     * Class String is special cased within the Serialization Stream Protocol.
     *
     * A String instance is written into an ObjectOutputStream according to
     * <a href="{@docRoot}/../platform/serialization/spec/output.html">
     * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

    /**
     * A Comparator that orders {@code String} objects as by
     * {@code compareToIgnoreCase}. This comparator is serializable.
     * <p>
     * Note that this Comparator does <em>not</em> take locale into account,
     * and will result in an unsatisfactory ordering for certain locales.
     * The java.text package provides <em>Collators</em> to allow
     * locale-sensitive ordering.
     *
     * @see     java.text.Collator#compare(String, String)
     * @since   1.2
     */
    public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
}

1、String类真的不可变么?

  • String 类由关键字 final 修饰,说明该类不可继承;
  • char value[] 属性也被 final 所修饰,说明 value 的引用在创建之后,就不能被改变;
  • 所有的成员属性均被 private 关键字所修饰。

如下代码,可以通过反射获取String中的value的值,并进行修改,String对象str由“HELLO WORLD!”变为“HELLO-WORLD!”,这样就轻松改变了String对象的值。

import java.lang.reflect.Field;

public class Test {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		//创建字符串 "HELLO WORLD!",并将它赋值给str引用
		String str = "HELLO WORLD!";
		System.out.println(str); //HELLO WORLD!
		
		//反射修改String类属性
		Field valueField = String.class.getDeclaredField("value");
		valueField.setAccessible(true);
		char[] strArr = (char[])valueField.get(str);
		//修改
		strArr[5] = '-';
		System.out.println(str); //HELLO-WORLD!
	}
}

在思考关于String类是不可变类的过程中,对修改前后的值取hash值,如下:

import java.lang.reflect.Field;

public class Test {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		//创建字符串 "HELLO WORLD!",并将它赋值给str引用
		String str = "HELLO WORLD!";
		System.out.println(str); //HELLO WORLD!
		System.out.println(str.hashCode()); //结果一:435340829
		//反射修改String类属性
		Field valueField = String.class.getDeclaredField("value");
		valueField.setAccessible(true);
		char[] strArr = (char[])valueField.get(str);
		//修改
		strArr[5] = '=';
		System.out.println(str); //HELLO=WORLD!
		System.out.println(str.hashCode()); //结果二:435340829
		System.out.println("HELLO=WORLD!".hashCode()); //结果三:403143802
		System.out.println("HELLO WORLD!".hashCode()); //结果四:435340829
	}
}

如上代码结果一和结果二是一样的值,看String类的hashCode()方法如下:

   /**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    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类的不可变关键在于Java的开发者在设计和开发 String的过程中,没有暴露任何的内部成员,如下代码中的JDK String类的API 的设计是均没有操作 value 的值 , 而是采用 new String() 的方式返回新的字符串,保证了String的不可变。

   /**
     * Returns a string that is a substring of this string. The
     * substring begins at the specified {@code beginIndex} and
     * extends to the character at index {@code endIndex - 1}.
     * Thus the length of the substring is {@code endIndex-beginIndex}.
     * <p>
     * Examples:
     * <blockquote><pre>
     * "hamburger".substring(4, 8) returns "urge"
     * "smiles".substring(1, 5) returns "mile"
     * </pre></blockquote>
     *
     * @param      beginIndex   the beginning index, inclusive.
     * @param      endIndex     the ending index, exclusive.
     * @return     the specified substring.
     * @exception  IndexOutOfBoundsException  if the
     *             {@code beginIndex} is negative, or
     *             {@code endIndex} is larger than the length of
     *             this {@code String} object, or
     *             {@code beginIndex} is larger than
     *             {@code endIndex}.
     */
    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
   
   /**
     * Concatenates the specified string to the end of this string.
     * <p>
     * If the length of the argument string is {@code 0}, then this
     * {@code String} object is returned. Otherwise, a
     * {@code String} object is returned that represents a character
     * sequence that is the concatenation of the character sequence
     * represented by this {@code String} object and the character
     * sequence represented by the argument string.<p>
     * Examples:
     * <blockquote><pre>
     * "cares".concat("s") returns "caress"
     * "to".concat("get").concat("her") returns "together"
     * </pre></blockquote>
     *
     * @param   str   the {@code String} that is concatenated to the end
     *                of this {@code String}.
     * @return  a string that represents the concatenation of this object's
     *          characters followed by the string argument's characters.
     */
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

   /**
     * Returns a string resulting from replacing all occurrences of
     * {@code oldChar} in this string with {@code newChar}.
     * <p>
     * If the character {@code oldChar} does not occur in the
     * character sequence represented by this {@code String} object,
     * then a reference to this {@code String} object is returned.
     * Otherwise, a {@code String} object is returned that
     * represents a character sequence identical to the character sequence
     * represented by this {@code String} object, except that every
     * occurrence of {@code oldChar} is replaced by an occurrence
     * of {@code newChar}.
     * <p>
     * Examples:
     * <blockquote><pre>
     * "mesquite in your cellar".replace('e', 'o')
     *         returns "mosquito in your collar"
     * "the war of baronets".replace('r', 'y')
     *         returns "the way of bayonets"
     * "sparring with a purple porpoise".replace('p', 't')
     *         returns "starring with a turtle tortoise"
     * "JonL".replace('q', 'x') returns "JonL" (no change)
     * </pre></blockquote>
     *
     * @param   oldChar   the old character.
     * @param   newChar   the new character.
     * @return  a string derived from this string by replacing every
     *          occurrence of {@code oldChar} with {@code newChar}.
     */
    public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }

 2、String实现了Serializable、Comparable<T>、CharSequence三个接口

//第一个接口
public interface Serializable {
}

//第二个接口
public interface Comparable<T> {
    public int compareTo(T o);
}

//第三个接口
public interface CharSequence {
	    int length();
	    char charAt(int index);
	    CharSequence subSequence(int start, int end);
	    public String toString();
	    public default IntStream chars() {
	        class CharIterator implements PrimitiveIterator.OfInt {
	            int cur = 0;
	            public boolean hasNext() {
	                return cur < length();
	            }
	            public int nextInt() {
	                if (hasNext()) {
	                    return charAt(cur++);
	                } else {
	                    throw new NoSuchElementException();
	                }
	            }
	            @Override
	            public void forEachRemaining(IntConsumer block) {
	                for (; cur < length(); cur++) {
	                    block.accept(charAt(cur));
	                }
	            }
	        }
	        return StreamSupport.intStream(() ->
	                Spliterators.spliterator(
	                        new CharIterator(),
	                        length(),
	                        Spliterator.ORDERED),
	                Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED,
	                false);
	    }
	    public default IntStream codePoints() {
	        class CodePointIterator implements PrimitiveIterator.OfInt {
	            int cur = 0;

	            @Override
	            public void forEachRemaining(IntConsumer block) {
	                final int length = length();
	                int i = cur;
	                try {
	                    while (i < length) {
	                        char c1 = charAt(i++);
	                        if (!Character.isHighSurrogate(c1) || i >= length) {
	                            block.accept(c1);
	                        } else {
	                            char c2 = charAt(i);
	                            if (Character.isLowSurrogate(c2)) {
	                                i++;
	                                block.accept(Character.toCodePoint(c1, c2));
	                            } else {
	                                block.accept(c1);
	                            }
	                        }
	                    }
	                } finally {
	                    cur = i;
	                }
	            }

	            public boolean hasNext() {
	                return cur < length();
	            }

	            public int nextInt() {
	                final int length = length();

	                if (cur >= length) {
	                    throw new NoSuchElementException();
	                }
	                char c1 = charAt(cur++);
	                if (Character.isHighSurrogate(c1) && cur < length) {
	                    char c2 = charAt(cur);
	                    if (Character.isLowSurrogate(c2)) {
	                        cur++;
	                        return Character.toCodePoint(c1, c2);
	                    }
	                }
	                return c1;
	            }
	        }

	        return StreamSupport.intStream(() ->
	                Spliterators.spliteratorUnknownSize(
	                        new CodePointIterator(),
	                        Spliterator.ORDERED),
	                Spliterator.ORDERED,
	                false);
	    }
	}

二、StringBuffer详解

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient char[] toStringCache;

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    static final long serialVersionUID = 3388685877147921107L;

    /**
     * Serializable fields for StringBuffer.
     *
     * @serialField value  char[]
     *              The backing character array of this StringBuffer.
     * @serialField count int
     *              The number of characters in this StringBuffer.
     * @serialField shared  boolean
     *              A flag indicating whether the backing array is shared.
     *              The value is ignored upon deserialization.
     */
    private static final java.io.ObjectStreamField[] serialPersistentFields =
    {
        new java.io.ObjectStreamField("value", char[].class),
        new java.io.ObjectStreamField("count", Integer.TYPE),
        new java.io.ObjectStreamField("shared", Boolean.TYPE),
    };
}

三、StringBuilder详解

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值