Jave String StringBuffer StringBuilder杂想

来源:互联网 发布:python re 匹配域名 编辑:程序博客网 时间:2024/06/15 19:55

         记得大学毕业那会,笔试时有这道题,转眼多年过去,不曾在写java, 有点遗憾,现在一点一点的补起来,贵在坚持!

程序性能,除去框架等方面影响等其他影响,剩下就是我们代码写的不聪明,或者说我们没有尽职尽责每一个行代码的深层理解,如上使用,估计大家回使用,但是有没有彻底的深究或者准确使用,如下是自己汇总,便于记录,也便于查看:

        首先,查看源码可知,String 声明为: Public final     连接文章详细介绍fianl原理,http://www.importnew.com/7553.html;类似final类还有Interger及其他包装类, 此时不能被继承。同时一旦声明该对象,则不能进行修改,如果修改,则会在内存中重新申请内存来保存新的值,可见如果复杂的字符操作,性能会有差。

        为什么设计成为final, Java运行时保存了一个字符串池(String pool),这使得String成为了一个特别的类。这样带来的好处如下:

        只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String interning将不能实现(译者注:String interning是指对不同的字符串仅仅只保存一个,即不会保存多个相同的字符串。),因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。
        如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
         因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。
        因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。

   

     其次: StringBuffer和StringBuilder都集成了AbstractStringBuilder,而StringBuffer大部分方法都是synchronized,也就是线程安全的,而StringBuilder就没有,所以,我们查看API可以知道,StringBuilder可以操作StringBuffer,但是StringBuffer不可以操作StringBuilder,这也是线程的原因;

       可想而知,StringBuffer的效率肯定没有StringBuilder,因为StringBuffer要维持同步锁,这肯定要消耗部分资源,所以三者性能顺序为:StringBuilder>StringBuffer>String 

        
实际使用中主要如下:

首先,  String   “==” 和equals的区别, 其中 后者主要用于判断两个变量是否是对同一个对象的引用,即堆中的内容是否相同, 前者主要应用于如下:

1, 用于基本数据类型的比较; 2,判断引用是否指向堆内存的同一块地址。 

如下例句:

"例子一:对象不同,内容相同,"=="返回false,equals返回true


String  strData        = new String("data");
String  strDataEx   = new String("data");
System.out.println(strData ==strDataEx );                // 引用不同, 所以为false
System.out.println(strData .equals(strDataEx ));    // 内容相同, 所以为ture


String  strData = new String("data");
String strDataEx   = strData ;
System.out.println(strData==strDataEx);                //应用相同
System.out.println(strData.equals(strDataEx));    //内容相同


String  strData  = "strData ";
String  strDataEx   = "strData ";

System.out.println(strData==strDataEx);            //true
System.out.println(strData.equals(strDataEx));    //true


注意String 缓冲池:

String缓冲池内不存在与其指定值相同的String对象,那么此时虚拟机将为此创建新的String对象,并存放在String缓冲池内。
String缓冲池内存在与其指定值相同的String对象,那么此时虚拟机将不为此创建新的String对象,而直接返回已存在的String对象的引用。

同时需要明白JVM 常量池的用法, 详见连接http://www.cnblogs.com/xrq730/p/4827590.html;


其次, StringBuilder和StringBuffer使用注意事项, 两者字符串长度均可扩展, StringBuffer是线程安全, StringBuilder是非线程安全的, 其中其是如何动态扩展长度的呢?

如下源码:

public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > value.length) {
            expandCapacity(minimumCapacity);
        }
}
void expandCapacity(int minimumCapacity) {
        int newCapacity = (value.length + 1) * 2;
        if (newCapacity < 0) {                                          //// 此处如果新增的长度溢出,则小于0 ,足见写代码的严谨性
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
            newCapacity = minimumCapacity;
        }
        value = Arrays.copyOf(value, newCapacity);
}

可见其原理和STL vector 增长机制一样, 由此可见有些思想是大家共识的;


开发中利用StringBuffer和StringBuilder拼接字符串, 其中主要原因如下:

String  strTemp = "中国";     //常量池中,没有“中国”, 则创建并指向该值

strTemp += "是"     //常量池中,没有“中国是”, 则创建并指向该值

strTemp +="伟大的民族!" 常量池中,没有“中国是伟大的民族”, 则创建并指向该值


通过以上,则发现,其会产生大量的中间临时对象,消耗内存空间,如果复杂的操作中,会造成严重的性能问题, 同时

String  strTemp = "中国" +  "是"    + "伟大的民族!"  这样写虽无过程对象产生,但是代码可读性比较差 ,建议不要这样写!   



StringBuilder  sbTemp = new StringBuilder("中国");

sbTemp.append("是");

sbTemp.append("伟大的民族!");

此处逻辑为:

StringBuffer和StringBuilder原理一样,无非是在底层维护了一个char数组,每次append的时候就往char数组里面放字符而已,在最终sb.toString()的时候,用一个new String()方法把char数组里面的内容都转成String,这样,常量池中创建了一个最终产生的String,在需要对字符串进行拼接尤其是大量拼接的地方,大量地节省常量池的空间。













 








0 0
原创粉丝点击