如何理解java String源代码

时间:2015-03-25 20:13:04

标签: java string

它是如何工作的?我无法理解它是如何达到这样的,即每当你改变原始字符串中的某个字符串时就会创建一个新字符串。什么是抵消,价值和数量?

private final char value[];  
   private final int offset;  
   private final int count;  

public String() {  
       this.offset = 0;  
       this.count = 0;  
       this.value = new char[0];  
   }  

public String(String original) {  
       int size = original.count;  
       char[] originalValue = original.value;  
       char[] v;  
       if (originalValue.length > size) {  
           // The array representing the String is bigger than the new  
           // String itself.  Perhaps this constructor is being called  
           // in order to trim the baggage, so make a copy of the array.  
           int off = original.offset;  
           v = Arrays.copyOfRange(originalValue, off, off+size);  
       } else {  
           // The array representing the String is the same  
           // size as the String, so no point in making a copy.  
           v = originalValue;  
       }  
       this.offset = 0;  
       this.count = size;  
       this.value = v;  
   }  

public String(char value[]) {  
       int size = value.length;  
       this.offset = 0;  
       this.count = size;  
       this.value = Arrays.copyOf(value, size);  
   }  

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

public String substring(int beginIndex, int endIndex) {  
       if (beginIndex < 0) {  
           throw new StringIndexOutOfBoundsException(beginIndex);  
       }  
       if (endIndex > count) {  
           throw new StringIndexOutOfBoundsException(endIndex);  
       }  
       if (beginIndex > endIndex) {  
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);  
       }  
       return ((beginIndex == 0) && (endIndex == count)) ? this :  
           new String(offset + beginIndex, endIndex - beginIndex, value);         
   }  

public String concat(String str) {  
       int otherLen = str.length();  
       if (otherLen == 0) {  
           return this;  
       }  
       char buf[] = new char[count + otherLen];  
       getChars(0, count, buf, 0);  
       str.getChars(0, otherLen, buf, count);  
       return new String(0, count + otherLen, buf);       
   }  

public static String valueOf(char data[]) {  
       return new String(data);       
   }  

public static String valueOf(char c) {  
       char data[] = {c};  
       return new String(0, 1, data);       
   }  

2 个答案:

答案 0 :(得分:2)

该值是字符串的基础字符数组。偏移量是字符串开始的位置,计数是它的长度。数组{'a','b','c','d','e'}上的字符串可以是计数3和偏移量1,它是"bcd"。这样就不会为每个子字符串操作复制数组。

答案 1 :(得分:2)

  

...每次更改原始字符串中的某个字符串后,每次创建一个新字符串。

我假设你在谈论String的不变性。它不复杂或聪明。相反,String上的每个操作都会更改原始操作。它只是将结果复制到新的String,或者保留对旧的结果的未更改的引用。

字符串基于字符数组,各种字符串操作访问该字符数组。当要创建与旧字符串不同的新字符串时,将创建一个新字符数组,并从旧字符串复制数据,并进行更改。然后从该字符数组中生成一个新的String对象。

例如,concat方法准备一个新的字符数组,复制来自两个String的数据(当前的一个和作为参数传递的数据),然后创建一个新的{ {1}}由此新字符串支持的对象。两个旧的String对象不会更改。

但是你带来的版本来自Java 6.在Java 7之前,Java的作者希望通过允许子串操作指向原始字符数组来分配更少的内存。这里的想法是,由于原始的长字符数组永远不会被更改(因为没有任何方法可以更改该数组),所有的子字符串实际上都可以由相同字符数组支持,如果您通过三个项目定义字符串:

  1. 支持哪个字符数组。
  2. 该字符数组中的当前字符串开始(String)。
  3. 该字符数组中的多少个字符被视为当前字符串(offset)的一部分。
  4. 因此,count之类的字符串可以表示为:

    1. char数组:"ABC",偏移量:{ 'A', 'B', 'C' },计数:0
    2. char数组:3,偏移量:{ 'A', 'B', 'C', 'D', 'E' },计数:0
    3. char数组:3,偏移量:{ 'F', 'O', 'O', 'A', 'B', 'C' },计数:3
    4. 所有这些都是Java(最高版本6)认为是3的有效实现。

      这个技巧允许他们在执行"ABC"操作时避免复制数组。它没有导致字符串是不可变的。 substring是不可改变的事实,这是基于的伎俩。

      但是,在Java 7中,这个技巧已被放弃,现在Java中String的唯一有效表示是#1。原因是这个技巧实际上导致了内存泄漏。你可以创建一个巨大的字符串,取一些它的小子串,然后去除对这个巨大字符串的所有引用......但是,它仍然不会被垃圾收集。为什么?因为微小的子串仍然指的是里面的内部巨大的字符数组。

      总结一下:

      • 通过确保没有更改后备字符数组的方法来实现字符串不变性。所有操作都是读操作。您可以看到返回String值的方法会调用"ABC"来返回。并且各种构造函数不会将传递给它们的原始字符串中的任何内容更改为参数。
      • new String(...)offset技巧现已过时,依赖于不变性来保存复制操作。它没有引起不变性,只是依靠它。