Java String详解

背景知识1:

Java的内存区域分为,静态区存放静态变量,堆区,栈区,静态区,和不可变的方法区。
Java的八种基本变量和引用的句柄都直接放在栈区。栈区的好处是访问快,效率高,但是申请的内存必须是确定的。放在C中的意思就是,静态分配的内存就在栈区。动态new 或者malloc出来的内存则放在堆区。
堆区具有不必实现声明所要申请内存大小的优点。

背景知识2:

Java 的String pool。 Java虚拟机在方法区中专门有一块内存作为存放String pool的区域。在代码中的常量字符串都会在编译的时候确定,运行时放在String pool中。String pool的大小可通过JVM参数来确定。

String的构造方法

总体来讲String的构造方法有两种,一种是通过赋值字符串的方法,一种是new的方法。
赋值字符串的方法,虚拟机会做些优化,然后把对应的字符常量放入String pool. 而new的方式会直接在堆上创建对应的对象。下面示例中,s1,s2会指向String pool中的字符串“abc”。

public class StringTest {
	public static void main(String args[])
	{
		String s1 = "abc";
		String s2 = "a"+"bc";
		String s3 = new String("abc");
	}

}

问题1: 为什么要在String pool中缓存这些字符串?
答案是因为效率。试想如果某个字符串被用到的次数很多,而每次都需要向堆申请内存的话,这将消耗大量内存。
问题2: 为什么能把这些字符串放在String pool中,而new 的却不可以?
可以从C语言的角度来看,char * s = "abc", 这样的语句在编译期间字符串就确定,所以可以放在栈区,而动态malloc出来的字符串则不可以。

有了这样的知识,下面代码的输出也就不难猜出了
public class StringTest {
	public static void main(String args[])
	{
		String s1 = "abc";
		String s2 = "a"+"bc";
		String s3 = new String("abc");
		String s4 = new String("abc");
		System.out.println("s1==s2:"+ (s1==s2));
		System.out.println("s1==s3:"+ (s1==s3));
		System.out.println("s4==s3:"+(s4==s3));
	}

}

String的intern方法

接上一节,那么如果不直接在代码中声明字符串,是否就没有办法把String放在栈区了?String提供了Native的intern方法,可以使得虚拟机把特定的字符串放在String pool中。
有了这样的背景知识,下面的代码也就了然于心了。
public class StringTest {
	public static void main(String args[])
	{
		String s1 = new String("abc");
		String s2 = s1.intern();
		String s3 = new String("abc");
		String s4 = s3.intern();
		
		System.out.println("s1==s2:"+ (s1==s2));
		System.out.println("s2==s3:"+(s2==s3));
		System.out.println("s3==s4:"+(s3==s4));
		System.out.println("s2==s4:"+(s2==s4));
		
	}

}


String 的immutable属性

String 是不可变类。关于Java不可变类的优点前面已经讲过。
String之所以设计成不可变类,原因也是效率和安全。下图为String 代码的节选,并附上解释
//继承了Serializable, Comparable<String>, CharSequence 接口,因为设计成Immutable属性,故需要final
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    //存放字符串的字符数组。
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    
    //因为String是immutable类,故无需再拷贝一份,简单的把哈希值和字符串数值赋值
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    //不能直接赋值,需要拷贝,因为char数组是可变的,如果直接赋值的话,改变
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

   
    //equals 方法,对比字符数组中的每一个字符
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

    //计算hash
    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的拷贝
   public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }
	
	//每次字符串相加的时候,都返回一个新的连接后的字符串,所以轻易别用大量字符串concat
	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);
    }
	
}

StringBuffer和StringBuilder类

作为同为实现CharSequence 的字符串处理类,StringBuffer 是可变并且线程安全的。StringBuilder也是可变,但是并不是线程安全的。
//StringBuffer和StringBuilder的基类
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     *存储的字符串数组
     */
    char[] value;

    /**
     * 当前用到的在字符串数组中字符个数
     */
    int count;

    /**
     * 创建capacity容量的字符数组,最好能预估出自己使用String大小.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

	
	//当字符数组不够用的时候扩容
    public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }

	//append在原有字符串数组上扩容
    public AbstractStringBuilder append(char str[], int offset, int len) {
        if (len > 0)                // let arraycopy report AIOOBE for len < 0
            ensureCapacityInternal(count + len);
        System.arraycopy(str, offset, value, count, len);
        count += len;
        return this;
    }

   //替换,在原因字符数组中替换
    public AbstractStringBuilder replace(int start, int end, String str) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (start > count)
            throw new StringIndexOutOfBoundsException("start > length()");
        if (start > end)
            throw new StringIndexOutOfBoundsException("start > end");

        if (end > count)
            end = count;
        int len = str.length();
        int newCount = count + len - (end - start);
        ensureCapacityInternal(newCount);

        System.arraycopy(value, end, value, start + len, count - end);
        str.getChars(value, start);
        count = newCount;
        return this;
    }

}



 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
	//都是synchronized,保证线程安全
    public synchronized void trimToSize() {
        super.trimToSize();
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    public synchronized void setLength(int newLength) {
        super.setLength(newLength);
    }

    /**
	...
}



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

	//没有synchronized,线程不安全
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

   
}


小结:

本文分析了一下String 对象的主要特性,但并没有对String的一些其他成员函数做一一介绍,比方说subString,indexOf 等等。一来时间有限,二来这些函数也并没有特别之处。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值