皮皮网

皮皮网

【模板库源码】【潜龙伏虎源码】【图片解析网站源码】getchars源码

时间:2024-12-28 19:27:55 分类:探索

1.Java中字符串为什么不以\0结尾
2.StringBuilder为什么线程不安全?

getchars源码

Java中字符串为什么不以\0结尾

       å…¶å®žè¿™ä¸ªé—®é¢˜æ²¡æœ‰ä»€ä¹ˆå¥½è¯´çš„,Java里面一切都是对象,是对象的话,字符串肯定就有长度,即然有长度,编译器就可以确定要输出的字符个数,当然也就没有必要去浪费那1字节的空间用以标明字符串的结束了。

       å­¦è¿‡C/C++的人都有一种固定的思维模式,凡是字符串都是以\0结束,其实C++中未必,不信你查查string这个泛型类,为了与C语言兼容性,它里面有一个方法c_str()就是返回以\0结束的字符串。

       ä¸‹é¢æ˜¯C++/C代码

       char

       str[]=”test”;//这个占5个字节

       ä¸ºä»€ä¹ˆæ˜¯5个字节,因为C语言的标准输出函数printf对字符串进行输出时,会判断当前字符是不是\0,源码模板库源码如果是\0的话,就停止输出。不信,你可以看看Linux下的Vsprintf.c这个文件

       Java中:

       char

       []str=”test”;//编译都通不过

       char

       []str=new

       char[];//这样才行,也就是直接指定了其大小

       åœ¨java中数组其实就是一个对象,学习java时,我们都接触的一句话就是everythingis

       object,因此数组也不例外,数组对象里还有一个属性叫做length,就是数组的长度,因此对于输出函数来说,有直接的大小可以判断字符串的边界,编译器就没必要再去浪费一个空间标识字符串的结束。

       Java中的Stringç±»

       å¦‚下String

       str=”test”;为什么不能/0作为结束呢,其实上面也说了,String类里面本身就有方法length()可以确定字符串的长度,因些输出时完全可以根据这个长度来输出,当我们调用

       System.out.println(str),看源码的话,真正调用的是如下这个函数:

       public

       void

       write(String

       str,

       int

       off,

       int

       len)

       throws

       IOException

       {

       synchronized

       (lock)

       {

       char

       cbuf[]

       //如果长度小于1K的话;

       if

       (len

       <=

       writeBufferSize)

       {

       if

       (writeBuffer

       ==

       null)

       { //如果writeBuffer属于第一次使用,还没申请缓存空间时

       writeBuffer

       =

       new

       char[writeBufferSize];

       }

       cbuf

       =

       writeBuffer;

       }//大于1K的话

       else

       {

       //

       Don't

       permanently

       allocate

       very

       large

       buffers.

       cbuf

       =

       new

       char[len];

       }//将str存入缓存,这里看到了没,这个的len就是字符串的长度

       str.getChars(off,

       (off

       +

       len),

       cbuf,

       0);

       write(cbuf,

       0,

       len);//向控制台写

       }

       }

StringBuilder为什么线程不安全?

       1、为什么输出值跟预期值不一样

       æˆ‘们先看一下StringBuilder的两个成员变量(这两个成员变量实际上是定义在AbstractStringBuilder里面的,StringBuilder和StringBuffer都继承了AbstractStringBuilder)

       //存储字符串的具体内容char[] value;//已经使用的字符数组的数量int count;

       å†çœ‹StringBuilder的append()方法:

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

       StringBuilder的append()方法调用的父类AbstractStringBuilder的append()方法

       public AbstractStringBuilder append(String str) {     if (str == null)        return appendNull();    int len = str.length();    ensureCapacityInternal(count + len);    str.getChars(0, len, value, count);    count += len;    return this;}

       æˆ‘们先不管代码的第五行和第六行干了什么,直接看第七行,count += len不是一个原子操作。假设这个时候count值为,len值为1,两个线程同时执行到了第七行,拿到的count值都是,执行完加法运算后将结果赋值给count,所以两个线程执行完后count值为,而不是。这就是为什么测试代码输出的值要比小的原因。

       2、为什么会抛出ArrayIndexOutOfBoundsException异常。

       æˆ‘们看回AbstractStringBuilder的append()方法源码的第五行,ensureCapacityInternal()方法是检查StringBuilder对象的原char数组的容量能不能盛下新的字符串,如果盛不下就调用expandCapacity()方法对char数组进行扩容。

       private void ensureCapacityInternal(int minimumCapacity) {         // overflow-conscious code    if (minimumCapacity - value.length > 0)        expandCapacity(minimumCapacity);}

       æ‰©å®¹çš„逻辑就是new一个新的char数组,新的char数组的容量是原来char数组的两倍再加2,再通过System.arryCopy()函数将原数组的内容复制到新数组,最后将指针指向新的char数组。

       void expandCapacity(int minimumCapacity) {     //计算新的容量    int newCapacity = value.length * 2 + 2;    //中间省略了一些检查逻辑    ...    value = Arrays.copyOf(value, newCapacity);}

       Arrys.copyOf()方法

       public static char[] copyOf(char[] original, int newLength) {     char[] copy = new char[newLength];    //拷贝数组    System.arraycopy(original, 0, copy, 0,                         Math.min(original.length, newLength));    return copy;}

       AbstractStringBuilder的append()方法源码的第六行,是将String对象里面char数组里面的内容拷贝到StringBuilder对象的char数组里面,代码如下:

       str.getChars(0, len, value, count);

       getChars()方法

       public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {     //中间省略了一些检查    ...       System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);    }

       æ‹·è´æµç¨‹è§ä¸‹å›¾

       å‡è®¾çŽ°åœ¨æœ‰ä¸¤ä¸ªçº¿ç¨‹åŒæ—¶æ‰§è¡Œäº†StringBuilder的append()方法,两个线程都执行完了第五行的ensureCapacityInternal()方法,此刻count=5。

       è¿™ä¸ªæ—¶å€™çº¿ç¨‹1的cpu时间片用完了,线程2继续执行。线程2执行完整个append()方法后count变成6了

       çº¿ç¨‹1继续执行第六行的str.getChars()方法的时候拿到的count值就是6了,执行char数组拷贝的时候就会抛出ArrayIndexOutOfBoundsException异常。

       è‡³æ­¤ï¼ŒStringBuilder为什么不安全已经分析完了。如果我们将测试代码的StringBuilder对象换成StringBuffer对象会输出什么呢?

       å½“然是输出啦!

       é‚£ä¹ˆStringBuffer用什么手段保证线程安全的?这个问题你点进StringBuffer的append()方法里面就知道了。