String、StringBuilder、StringBuffer的理解及使用说明

一、从源码来理解String类

     public final class String
    	    implements java.io.Serializable, Comparable<String>, CharSequence {
    	    /** The value is used for character storage. */
    	    private final char value[];
    
    	    /** 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;
    	 }

1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。

      private final char value[];
    
        /** Cache the hash code for the string */
        private int hash; // Default to 0

源码中String类中的成员属性可以看出,String类其实是通过char数组来保存字符串的。

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

    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);
        }

从上面的两个方法可以看出,无论是 substring()还是 concat()操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。
  在这里要永远记住一点:
   “对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

连接表达式+(加号)

1、只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。
2、对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中

    String str1 = "str";
    	String str2 = "ing";
    	
    	String str3 = "str" + "ing";
    	String str4 = str1 + str2;
    	System.out.println(str3 == str4);//false
    	
    	String str5 = "string";
    	System.out.println(str3 == str5);//true   
1、 Sting s; //定义了一个变量s,没有创建对象;
2=    // 赋值,将某个对象的引用(句柄)赋给s ,没有创建对象;
3、 “abc”    //创建一个对象;
4new String(); // 创建一个对象。

FAQ

1. String str1 = “abc”; System.out.println(str1 == “abc”); 

步骤:
a> 栈中开辟一块空间存放引用str1;
b> String池中开辟一块空间,存放String常量”abc”;
c> 引用str1指向池中String常量”abc”;
d> str1所指代的地址即常量”abc”所在地址,输出为true;

String str2 = new String(“abc”); System.out.println(str2 == “abc”); 

步骤:
a> 栈中开辟一块空间存放引用str2;
b> 堆中开辟一块空间存放一个新建的String对象”abc”;
c> 引用str2指向堆中的新建的String对象”abc”;
d> str2所指代的对象地址为堆中地址,而常量”abc”地址在池中,输出为false;
注意:对于通过new产生的对象,会先去常量池检查有没有 “abc”,如果没有,先在常量池创建一个 “abc” 对象,然后在堆中创建一个常量池中此 “abc” 对象的拷贝对象;

String s2 = new String(“Hello”); 产生几个对象? 

首先,在jvm的工作过程中,会创建一片的内存空间专门存入string对象。我们把这片内存空间叫做string池;
String s2 = new String(“Hello”);jvm首先在string池内里面看找不找到字符串”Hello”,如果找到不做任何事情;否则创建新的string对象,放到string池里面。由于遇到了new,还会在内存Heap上(不是string池里面)创建string对象存储”Hello”,并将内存上的(不是string池内的)string对象返回给s2。
Re: 如果常量池中原来没有“Hello”, 则创建两个对象。如果原来的常量池中存在“Hello”时,就是一个对象;

二、深入理解String、StringBuffer、StringBuilder

 
1.String str="hello world"和String str=new String("hello world")的区别

String str=“hello world” 通过直接赋值的形式可能创建一个或者不创建对象,如果"hello world"在字符串池中不存在,会在java字符串池中创建一个String对象(“hello world”),常量池中的值不能有 重复的,所以当你通过这种方式创建对象的时候,java虚拟机会自动的在常量池中搜索有没有这个值,如果有的话就直接利用他的值,如果没有,他会自动创建一个对象,所以,str指向这个内存地址,无论以后用这种方式创建多少个值为”hello world”的字符串对象,始终只有一个内存地址被分配。

String str=new String(“hello world”)通过new 关键字至少会创建一个对象,也有可能创建两个。因为用到new关键字,肯定会在堆中创建一个String对象,如果字符池中已经存在"hello world",则不会在字符串池中创建一个String对象,如果不存在,则会在字符串常量池中也创建一个对象。他是放到堆内存中的,这里面可以有重复的,所以每一次创建都会new一个新的对象,所以他们的地址不同。

String 有一个intern() 方法,native,用来检测在String pool是否已经有这个String存在。

通过一段代码来理解一下

    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);
        }
    }

返回的结果是

    false
    true
    false
2.String、StringBuffer以及StringBuilder的区别
通过代码来理解他们之间的区别
一、利用String操作
    public class Main {
        public static void main(String[] args) {
            String string = "";
            for(int i=0;i<10000;i++){
                string += "hello";
            }
        }
    }

从上面String的源码中我们可以分析出:这句 string += “hello”;的过程相当于将原有的string变量指向的对象内容取出与"hello"作字符串相加操作再存进另一个新的String对象当中,再让string变量指向新生成的对象。也就是说这个循环执行完毕new出了10000个对象,试想一下,如果这些对象没有被回收,会造成多大的内存资源浪费

利用StringBulider操作
    public class Main {      
        public static void main(String[] args) {
            StringBuilder stringBuilder = new StringBuilder();
            for(int i=0;i<10000;i++){
                stringBuilder.append("hello");
            }
        }
    }

这断代码和前面的功能一样,但是这里的10000次循环new操作只进行了一次,也就是说只生成了一个对象,append操作是在原有对象的基础上进行的。因此在循环了10000次之后,这段代码所占的资源要比上面小得多。这就是String和stringBuilder的最最主要的区别。

那么有人会问既然有了StringBuilder类,为什么还需要StringBuffer类?查看源代码便一目了然,事实上,StringBuilder和StringBuffer类拥有的成员属性以及成员方法基本相同,区别是StringBuffer类的成员方法前面多了一个关键字:synchronized,不用多说,这个关键字是在多线程访问时起到安全保护作用的,也就是说StringBuffer是线程安全的。有关线程的问题,我们讲多线程的时候在详细讲解!

总结:这三个类是各有利弊,应当根据不同的情况来进行选择使用
1、循环外字符串拼接可以直接使用String的+操作,没有必要通过StringBuilder进行append.
2、有循环体的话,好的做法是在循环外声明StringBuilder对象,在循环内进行手动append。
不论循环多少层都只有一个StringBuilder对象。
3、当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值