码迷,mamicode.com
首页 > 编程语言 > 详细

004 Java字符串的几个特性

时间:2017-02-25 14:52:00      阅读:241      评论:0      收藏:0      [点我收藏+]

标签:png   内存布局   避免   缺点   substr   空格   改变   发布   技术分享   

在本系列教材的上一篇(003 Java字符串)中,对Java语言中String类的一些基本情况和整体架构进行了讲解和分析,相信大家已经很好地掌握了。本篇教程主要是补充一些String类的重要特性,帮助大家避免掉使用String过程中的一些陷阱。

        首先,补充一个在JDK中使用非常频繁的概念:不可变类。所谓的不可变类是指该类的对象在生成以后就不会被改变了,关于不可变类的优点、缺点,特别是在Java并发编程时的优势,此处暂时略过不讲。那么如何定义一个不可变类呢?如果你有仔细观察String类的源码,你肯定能发现所有String类的所有可能修改字符串对象的方法都新建了一个字符串对象,而不是在原有的字符串对象上做修改。所以String类就是一个非常标准的不可变类。

        如果曾经做过Java的面试题,那么你对下面的题目不会陌生:

String a = "test";

String b = "te" + "st";

 

System.out.println(a == b);

System.out.println(a.equals(b));

强烈建议大家先自己思考一下,得出答案后再接着往下看。正确答案是true和true,恭喜你答对了。答案虽然单纯,背后的机制却并不简单,导致此种结果的主要原因有三:一是通过双引号定义的字符串是字符串常量,存储在JVM内存模型中的方法区(常量区);二是String是一个不可变类,JVM对字符串常量进行了特殊处理:缓存处理,也就是说一个字符串常量在整个JVM中只存在一份;三是Java编译器对编译时就能确定的字符串常量进行了一定的优化,也就是自动进行了合并操作。我绘制了一个字符串在简化版的JVM内存模型中的布局情况简图:

技术分享

从此图中可以看出,对于字符串常量,JVM将其存储于方法区。

    ?    ?接下来,再看看如下的代码:

String a = "test";

String b = new String(a);

String c = new String(a);

 

System.out.println(a == b);

System.out.println(b == c);

同样建议大家先自己思考得出答案。如果的答案不是false和false,那么接下来的内容你需要边看边思考了。我们发现此处使用new运算法来生成两个对象,它们就不再符合上面说的字符串常量的情况,它们在JVM简化内存模型中的布局如下图所示:

技术分享

从此图可以得出,如果使用new运算符来生成String对象,那么该String对象就一定是作为一个新生成的对象,存储于堆空间内。

    ?    ?关于String对象的内存布局情况,我们再来看一下String类中的intern函数:

public native String intern();

如果你的英文水平不差,建议仔细看一遍该方法的注释,从中可以获得该方法的大部分特性。看完之后并思考之后,可以通过下面这个代码来测试一下是否完全理解,代码如下:

String a = "test";

String b = new String(a);

 

System.out.println(a == a.intern());

System.out.println(b == b.intern());

System.out.println(a == b);

System.out.println(a.intern() == b.intern());

如果你给出的答案是true、false、false和true,那么恭喜你,以后关于String内存布局的笔试面试题都难不住你了。如果答案没有完全匹配,请仔细查看我给出JVM内存布局模型简图并思考:

技术分享

intern这个本地方法,对于存储于堆内存和方法区中的字符串对象,该方法都是获得字符串在JVM中的实际存储位置。一个Java应用中使用到的所有字符串值,实际上都只在JVM方法区的常量区中存储,堆内存中的String对象实际上并没有真正存储字符串值。如果理解了这个,上述代码的答案就显而易见了。

    ?    ?最后,大家需要注意一下String类中trim()方法,源码如下:

 public String trim() {

        int len = value.length;

        int st = 0;

        char[] val = value;    /* avoid getfield opcode */

 

        while ((st < len) && (val[st] <= ‘ ‘)) {

            st++;

        }

        while ((st < len) && (val[len - 1] <= ‘ ‘)) {

            len--;

        }

        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;

    }

我相信大部分Java程序员学的第一门编程语言都是C\C++,那么自然就以为String类的trim()方法也是用来去除字符串的前后空白字符的。当仔细看一下trim()方法的源码,你可能会发现Java版本trim()方法去除的不仅仅是空白字符(空格、换行、制表符),而是所有Unicode码小于等于32(空格的Unicode码)的字符。

    ?    ?通过本篇和上一篇教程对String类的讲解,相信你对JDK1.7中String类的构造结构和基本特性已经有了比较深入的理解。总结起来,String类中最关键的两个难点是字符编码和内存布局,相信各位读者都已经很好地掌握了。如果还有疑问和建议,欢迎给我留言。

  本系列文档会在本人的微信公众号发布,欢迎大家扫码关注。

                技术分享

004 Java字符串的几个特性

标签:png   内存布局   避免   缺点   substr   空格   改变   发布   技术分享   

原文地址:http://www.cnblogs.com/TwoWaterLee/p/6441764.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!