A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

探秘JavaStringStringBuilder以及StringBuffer
  相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下StringStringBuilderStringBuffer这几个类,分析它们的异同点以及了解各个类适用的场景。下面是本文的目录大纲:
  一.你了解String类吗?
  二.深入理解StringStringBufferStringBuilder
  三.不同场景下三个类的性能测试
  四.常见的关于StringStringBuffer的面试题(辟谣网上流传的一些曲解String类的说法)
  若有不正之处,请多多谅解和指正,不胜感激
  想要了解一个类,最好的办法就是看这个类的实现源代码,String类的实现在
  \jdk1.6.0_14\src\java\lang\String.java  文件中。
  打开这个类文件就会发现String类是被final修饰的:
  
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
  
public final class String
  
    implements java.io.Serializable,  Comparable<String>, CharSequence
  
{
  
    /** 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;
  
  
    /** 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;
  
  
    ......
  
  
}
  
  从上面可以看出几点:
  1String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final
  2)上面列举出了String类中所有的成员属性,从上面可以看出String类其实是通过char数组来保存字符串的。
  下面再继续看String类的一些方法实现:
  
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
22
  
23
  
24
  
25
  
26
  
27
  
28
  
29
  
30
  
31
  
32
  
33
  
34
  
35
  
36
  
37
  
38
  
39
  
40
  
41
  
42
  
43
  
44
  
45
  
46
  
47
  
48
  
49
  
50
  
51
  
  
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 String  replace(char oldChar, char newChar) {
  
    if (oldChar != newChar) {
  
        int len  = count;
  
        int i  = -1;
  
        char[]  val = value; /* avoid getfield opcode */
  
        int off  = offset;   /* avoid getfield opcode */
  
  
        while (++i  < len) {
  
        if (val[off  + i] == oldChar) {
  
            break;
  
        }
  
        }
  
        if (i  < len) {
  
        char buf[]  = new char[len];
  
        for (int j  = 0 ; j < i ; j++) {
  
            buf[j]  = val[off+j];
  
        }
  
        while (i  < len) {
  
            char c  = val[off + i];
  
            buf  = (c == oldChar) ? newChar : c;
  
            i++;
  
        }
  
        return new String(0,  len, buf);
  
        }
  
    }
  
    return this;
  
  从上面的三个方法可以看出,无论是sub操、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。
  在这里要永远记住一点:
  String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象
  在了解了于String类基础的知识后,下面来看一些在平常使用中容易忽略和混淆的地方。
二.深入理解String、StringBuffer、StringBuilder
1.String str="hello world"String str=new String("helloworld")的区别
  想必大家对上面2个语句都不陌生,在平时写代码的过程中也经常遇到,那么它们到底有什么区别和联系呢?下面先看几个例子:
  
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
  
public class Main {
  
         
  
    public static void main(String[]  args) {
  
        String  str1 = "hello world";
  
        String  str2 = new String("hello world");
  
        String  str3 = "hello world";
  
        String  str4 = new String("hello world");
  
         
  
        System.out.println(str1==str2);
  
        System.out.println(str1==str3);
  
        System.out.println(str2==str4);
  
    }
  
}
  
  这段代码的输出结果为
  从这段反编译出的字节码文件可以很清楚地看出:从第8行开始到第35行是整个循环的执行过程,并且每次循环会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象。也就是说这个循环执行完毕new出了10000个对象,试想一下,如果这些对象没有被回收,会造成多大的内存资源浪费。从上面还可以看出:string+="hello"的操作事实上会自动被JVM优化成:
  StringBuilder str = newStringBuilder(string);
  str.append("hello");
  str.toString();
  再看下面这段代码:
  
1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
  
public class Main {
  
         
  
    public static void main(String[]  args) {
  
        StringBuilder  stringBuilder = new StringBuilder();
  
        for(int i=0;i<10000;i++){
  
            stringBuilder.append("hello");
  
        }
  
    }
  
}
  
  反编译字节码文件得到:
  file:///C:/Users/wu/AppData/Local/Temp/msohtmlclip1/01/clip_image019.jpg
  可以看出,在1)中只进行了一次append操作,而在2)中进行了两次append操作。


24 个回复

正序浏览
加油,同学
回复 使用道具 举报
不错不错
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
回复 使用道具 举报
不错不错
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:33:23
14#
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:33:20
13#
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:33:17
12#
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:33:14
11#
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:33:09
10#
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:33:03
9#
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:32:59
8#
回复 使用道具 举报
gzgd 高级黑马 2018-3-21 10:32:53
7#
回复 使用道具 举报
12下一页
您需要登录后才可以回帖 登录 | 加入黑马