您的位置:首页 > 编程语言 > Java开发

java 基于JDK中的源码总结下String

2014-02-24 01:10 477 查看
 挤出了一点时间根据JDK总结下String。源码开头的注释说明:String代表字符串,java中的所有字符串序列都是这个类型的实例。String是固定不变的,它们的值在分配之后就不可以改变。鉴于这一点它们的值可以用来共享。java语言对字符串连接运算符“+”提供了特别的支持,控制台输出的时候就经常用到+这个特性。再增加说明一点,String类私有的维护着一个string pool,这块存储区存放着全局唯一的字符串。说了一大堆,有点晕了,但是几个关键字要特别注意:固定不变,共享,对“+”运算符的特殊支持,string
pool。这几个关键词基本可以解释刚学java时候感觉到的String特殊性。

        先说string pool,一般认为java中,基本数据类型和引用类型都存储在栈中,引用所指向的对象存储在堆中。String就很特殊,其在这两个区域之外还有一个私有的string pool。我们应该经常用到这一特性,但是很少有知道这一点的,sample:

[java] view
plaincopyprint?

String str = "hello world";  

String str1 = new String("hello world");  

String str2 = new String("the world");  

        第一句代码中str是引用类型,其指向string pool中的保存着"hello wolrd"的存储区。str1,str2也是引用类型,但是他们指向的类型在堆中,而不是string pool。str2更加特殊,String str2 = new String("the world"); 执行完之后实际上占用了两块内存,一块在堆中,一块在string pool中。事实上str1也有是一样的,但是变量str 前面已经在string pool中声明了一块区域,而string pool中的值是唯一的共享的,所以str1没有再一次分配内存。String
str2 = new String("……")这种声明每次会占用两块内存区域,在日常的编程中基本不用也就是由于这个原因。到这里string pool基本说完,可以告一段落。接着往下看说说字符串相等的比较,sample:

[java] view
plaincopyprint?

if(str == str1){  

    System.out.println("str == str1");  

}  

  

if(str.equals(str1)){  

    System.out.println("str.equals(str1)");  

}  

        上面代码执行结束之后只会输出"str.equals(str1)",因为 == 运算符是基于地址进行的,而str指向string pool,str1指向堆,对象地址肯定不一样。 String类重新实现equals(Object anObject)方法,其是基于内容进行比较的。String源码中的说明如下:将此字符串与指定的object进行比较,只有当object不是null,并且是一个代表着同样的字符串序列的String对象才为真。再补充一个例子,sample:

[java] view
plaincopyprint?

String sum = "+";  

if(sum.equals("+")){  

    System.out.println("sum.equals("+")");  

}  

  

if(sum == "+"){  

    System.out.println("sum == \"+\"");  

}  

这段代码会输出"sum.equals()" 和 "sum == "+""这两个字符串,原因前面已经说过,sum指向的是string pool中的内容,而sting pool中字符串唯一,所以地址和内容都相等。同样的原因,下面这段代码只会输出"sum1.equals()" 

[java] view
plaincopyprint?

String sum1 = new String("+");  

if(sum1.equals("+")){  

    System.out.println("sum.equals("+")");  

}  

  

if(sum1 == "+"){  

    System.out.println("sum == \"+\"");  

}  

       在上面的基础上接着说说intern()方法,这个也涉及到string pool,不过是没有新的知识。sample:

[java] view
plaincopyprint?

String str = "hello world";  

String str1 = new String("hello world");  

  

  

if(str.intern() == str1.intern()){  

    System.out.println("str.intern() == str1.intern()");  

}  

这段代码会输出"str.intern() == str1.intern()",看看源码中对intern()方法的说明:这个方法返回一个字符串的规范表示;String私有一个string pool 初始时为空;当这个方法被调用的时候,如果string pool中已经包含根据equals(Object)方法确定的和这个String想等的字符串,则直接返回这个字符串;否则这个字符串会被添加到string pool,并且返回它的引用;因此,对于任何两个字符串s和t,s.intern()== t.intern()当且仅当s.equals(t)为真的时候才为真。说到这里可以理解intern这个方法和equal基本等价。

        今天先总结到这里。以上内容为自己总结,如果有错误还请指出,甚感。

转自:http://blog.csdn.net/maybe_windleave/article/details/8738336

java 基于JDK中的源码总结下String二

 上一篇文章:http://blog.csdn.net/ts1122/article/details/8738336,介绍了String一些易错内容。这里接着大体介绍下String类提供的API。大体就是不对API做一一介绍,只是根据类型说个大概,再选择一些经典的方法详细说明下。说明下,这两篇文章都是基于JDK1.7.0_17版本进行说明的,本文章后面的内容会说到JDK1.7版本String类的实现和JDK1.6版本还是很大不同的。

        先说构造方法,String类里面构造方法很多。由于方法签名只与方法名称以及参数类型有关系,这么多构造方法的差异就只能是传递的参数类型和数目。不过有的构造方法基本不会被用到,比如下面两个:

[java] view
plaincopyprint?

public String() {  

   this.value = new char[0];  

}  

public String(String original) {  

   this.value = original.value;  

   this.hash = original.hash;  

}  

第一个构造方法之所以不用,说明文档里面写的很清楚:由于String是不可变的,当前方法的基本没用。这个方法给出了一个指向空字符串的引用,之后指向的内容又不能改变,所以没有什么用处。Java里面有些类叫做不可变类,其定义为:类中的每个方法都不能改变其对象。一般这种类中的数据域都有加final修饰,String类就是一个不可变类,其数据域就加了final字符修饰。上面的第二个方法之所以不用是因为会多分配一块内存,JVM会在当前线程的stringpool中分配一块内存,之后在堆中再分配一块内存。构造一个String,我们一般只要用String
= “abc”这种方式就足够了,而且简单高效,这种用法也是java推荐的。:-D

        下面是一组和String类数据域有关的方法:

[java] view
plaincopyprint?

public int length()  

public boolean isEmpty()  

public char charAt(int index)  

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)  

这三个方法都是基于String的数据域char value[]实现的,比如返回value.length。这些方法经常有用到,得记住。最后一个方法是将String中数据域内容拷贝到char数组方法,用System.arraycopy的本地方法实现,效率应该很高。

        接下来是一组String内容比较方法:

[java] view
plaincopyprint?

public boolean equals(Object anObject)  

public boolean equalsIgnoreCase(String anotherString)  

public boolean contentEquals(CharSequence cs)  

public int compareTo(String anotherString)  

第一个方法比较两个字符串对象内容是否完全相等,如果是就返回true。第二个方法从方法名称就可以理解,不考虑大小写进行比较。看了下第二个方法的内部实现,先把字符统一转成大写进行比较,如果不行再转成小写进行比较,看说明文档对于Georgian alphabet就有这种大写不成要用小写的特殊情况。:-(   第三个方法和前面两个方法基本一样,只不过参数类型不同。最后一个方法和C语言中的一样:取两个字符串较短的长度,从第一个字符开始对两个字符串进行比较,如果出现第一个不同的字符则返回两个字符之间的差值。结果返回是0的话就说明两个字符串内容相等。但是这个方法在java中很少用到。

        一组新的字符串内容比较方法:

[java] view
plaincopyprint?

public int indexOf(int ch)  

public int indexOf(int ch, int fromIndex)   

public int lastIndexOf(int ch)  

public boolean startsWith(String prefix)   

public boolean startsWith(String prefix, int toffset)  

public boolean endsWith(String suffix)  

public int indexOf(String str)  

public int indexOf(String str, int fromIndex)  

public int lastIndexOf(String str)  

前面三个方法都是返回一个字符在当前字符串中的位置。后面一组方法是寻找一组字符串在当前字符串中的位置,和前面一组方法很相似。

        一组易被忽视会产生新的字符串对象的方法:

[java] view
plaincopyprint?

public String substring(int beginIndex)  

public String substring(int beginIndex, int endIndex)  

public String replace(char oldChar, char newChar)  

public String toLowerCase()  

public String toUpperCase()  

public String trim()  

这些都是对当前字符串内容进行截取,替换,大小写变更等操作的方法。由于String类是不可变类,这些方法不能改变对象内容,只能新分配内存保存操作结果,如果滥用这些方法会造成内存泄露的。

        这里根据public String substring(int beginIndex)说一下JDK1.6和JDK1.7String类的一些不同。JDK1.6上String类的数据域如下:

[java] view
plaincopyprint?

/** The value is used for character storage. */  

private final char value[];  

  

/** The offset is the first index of the storage that is used. */  

private final int offset;  

  

/** The count is the number of characters in the String. */  

private final int count;  

即其包含一个类似高中几何上向量的东西,给出内容同时还给出了一个起始点和长度的游标。JDK1.6中在实现substring最终会用到下面的方法:

[java] view
plaincopyprint?

// Package private constructor which shares value array for speed.  

String(int offset, int count, char value[]) {  

   this.value = value;  

   this.offset = offset;  

   this.count = count;  

}  

这就是说截取一段某一个字符串中的一段,实际上并没有分配新的内存,只是提供了一个新的向量,返回的引用仍然指向原来的字符串。如果原来的字符串不再使用,仅使用新的截取的字符串,个人认为就造成内存浪费。如果每次都是从一个很长很长字符串中截取一小段内容,就造成了内存的极大浪费,这也是JDK1.6使用过程中,关于字符串的一个优化点。再看JDK1.7中String类就只有char value[]一个数据域,方法substring就采取了新的做法,如下:  

[java] view
plaincopyprint?

this.value = Arrays.copyOfRange(value, offset, offset+count);  

这句代码完成这个方法实现主体,分配了新的内存,将需要的内容拷贝到新的内存中。看JDK文档中说明,1.6的这种做法是为了速度。:-D

       接下来就是valueOf方法,都是像    public static String valueOf(Object obj) 这样的类方法,这些方法的实现其实就是参数本身的toString方法的实现。最后就是split方法,这个涉及到了正则表达式,这里暂不介绍。:-D
转自:http://blog.csdn.net/maybe_windleave/article/details/8765417
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: