美文网首页
String源码学习分析

String源码学习分析

作者: 雨夏_ | 来源:发表于2019-01-07 17:42 被阅读40次

    1.String简介

    String是java.lang包下的一个类,它不属于基本数据类型,是我们使用频率非常高的一个类。它是一个被final修饰的类,不能够被继承。


    String类图

    从上面的类图可以知道:
    1.String实现了Serializable接口,支持序列化
    2.String实现了Comparable接口,可以进行比较。
    3.String实现了CharSequence接口,实现了length()、charAt()、subSequence()等方法。

    2.String的成员变量

    String的成员变量

    1.String类型其实就是一个字符数组,这个字符数组用final修饰了,不能修改。所以API中进行修改字符串的方法都是返回一个新的字符串,需要重新引用才行。
    2.String缓存了hash值,不用每次都计算hash值,提高了效率。

    3.String类的一些方法

    StringAPI中方法太多了,我就不一一介绍了,挑几个有价值的方法的分析一下。

    1.Java中char类型是Unicode编码的,我们用的字符串可能是"ISO-8859-1"等其它字符集编码,如果直接使用很有可能会出现乱码的问题。

    String的一个构造方法,这个构造方法可以传递一个编码名称,让String可以根据编码名称进行解码,以解决乱码的问题。


    String的一个构造方法

    通过源码分析,我们可以知道,它是通过StringCoding工具类进行转码操作的。StringCoding这个工具类会通过Charset去判断字符集是否支持,不支持就抛出异常UnsupportedEncodingException,最终会通过相应的Decoder进行解码。

    2.String中split()方法,进行字符串的切割。
    public String[] split(String regex, int limit) {
        char ch = 0;
        //特殊情况判断
        // 单字符情况下regex不等于正则表达式的元字符(meta character):.$|()[{^?*+\\
        //双字符情况下regex第一个字符是反斜杠,第二个字符不是Unicode编码中的数字或字母
        if (((regex.value.length == 1 &&
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
             (regex.length() == 2 &&
              regex.charAt(0) == '\\' &&
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
              ((ch-'a')|('z'-ch)) < 0 &&
              ((ch-'A')|('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))
        {
            int off = 0;
            int next = 0;
            boolean limited = limit > 0;
            ArrayList<String> list = new ArrayList<>();
            while ((next = indexOf(ch, off)) != -1) {
                if (!limited || list.size() < limit - 1) {
                    list.add(substring(off, next));
                    off = next + 1;
                } else {
                    list.add(substring(off, value.length));
                    off = value.length;
                    break;
                }
            }
            //没有匹配到,将原字符串返回
            if (off == 0)
                return new String[]{this};
            //如果没有限制或者小于限制,将剩余的字符串添加进去
            if (!limited || list.size() < limit)
                list.add(substring(off, value.length));
            int resultSize = list.size();
            if (limit == 0) {//将结果集最后为""的字符串一个一个删除
                while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }
    

    从源码中分析可以得知,split()有两个参数,在limit等于0的情况下,从最后一个子字符串往前,所有的空字符串""都会被清除,所以有可能得到的String数组会小于期望值,如果limit>0,会最多切割limit-1次,没有切割的部分直接添加到结果集中。

    注意:参数regex是一个 regular-expression的匹配模式,所以特殊字符需要转义,例如"|"这个字符,需要写成"\|"。

    3.String类中有一个本地方法intern()
    intern()方法
    这个方法的作用是:在JDK1.6中,是将该字符串添加到常量池中并且返回指向该常量的引用,如果常量池中已经存在,则直接返回指向该常量的引用,在JDK1.7以后(包括1.7),常量池从方法区的PermGenSpace移动到了堆中,首先判断这个常量是否存在于常量池,如果存在,判断存在内容是引用还是常量,如果是引用,返回引用地址指向堆空间对象,如果是常量,直接返回常量池常量如果不存在,将当前对象引用复制到常量池,并且返回的是当前对象的引用。
    详细分析可以参考一下:美团技术团队《深入解析String#intern》

    4.String相关的一些问题

    为什么经常用String作为HashMap的Key呢?

    String缓存了hash code,提高了效率。其次是,String是一个不可变类,如果HashMap使用可以改变的类,会存在数据找不到的问题。例如用一个用一个可变类Student,Student stu = new Student();map.put(stu,value);如果stu的值发生了改变,map中的存储的key也会发生改变。下次通过stu访问map的时候,stu的hash值发生了改变,就找不到stu原来所在的位置了。

    遇到的一个BUG,有网友能给出答案吗?

    美团技术团队《深入解析String#intern》这篇文章,我可以很清楚的intern的用法了,在JDK1.6和JDK1.7以后的区别,但是,我在测试的时候,遇到了下面一个难以解释的问题~!
    测试环境:JDK1.8,Junit4.10

    测试代码
    测试结果
    在同样的环境下进行测试,但是两段类似的代码,执行的结果竟然不一样。
    但是将test1()中的代码放到main方法中执行,输出的结果又是true。
    测试代码
    测试结果
    根据intern在1.8中的作用,s1.intern()应该是将字符串"1"在内存中的引用放置常量池中,s2="11",s2得到的应该是s1在常量池中的引用,所以s1==s2应该结果为true
    为什么在Junit测试中,结果为false?而且就单独"1"会出现这个问题,其他字符都不会。

    相关文章

      网友评论

          本文标题:String源码学习分析

          本文链接:https://www.haomeiwen.com/subject/infprqtx.html