美文网首页
源码阅读篇-String类

源码阅读篇-String类

作者: noobBird | 来源:发表于2019-06-18 14:01 被阅读0次

    Java的String

    1.简介

    学习一下 java.lang.String类的方法和源码
    

    2.作用和特性

     只有一个父类就是Object 所有Java类都是继承于Object类
     实现了三个接口:Serizable 序列化接口,CharSequence(char的可读序列),Comparable<T>排序接口
    

    String 代表字符串,在创建后不可变。
    被final修饰 俗称的断子绝孙类 >_< 不能被继承

    本质是字符数组:

          String str="abc";
    

    相当于:

       char data[] = {'a', 'b', 'c'};
       String str = new String(data);   
    

    2.1 成员变量

    成员变量

    见图知意:注意都是final修饰的

    value[] :

         用来存储组成字符串的字符数组
         使用了private,也没有提供setter方法,所以在String类的外部不能修改value,
          同时value也使用了final进行修饰,那么在String类的内部也不能修改value
    

    hash:

     存储的是String的hash值 就是散列码 具体算法下面hashcode方法
    

    PS:

    final在修饰引用类型变量时,表示该引用在构造对象之后不能指向其他的对象
    但该引用指向的对象的状态可以改变
    关键是因为SUN公司的工程师,在后面所有String的方法里都很小心的没有去动字符数组里的元素。
    所以String类不可变的关键都在底层的实现,而不仅仅是一个final
    

    3.构造方法(弃用的构造方法不赘述了)

     1.String() 初始化新创建的 String对象,使其表示空字符序列。 
    
     2.String(byte[] bytes,Charset charset):根据指定的字节的数组解码charset创建一个新的String 。
    
     3.String(byte[] bytes, int offset, int length):和上面一样不过指定了字节数组的长度和起始位置
    
     4.String(byte[] bytes, int offset, int length, String charsetName) 
    
     5.String(byte[] bytes, String charsetName) :见参数知意
    
     6.String(char[] value):见参数知意 
    
     7.String(char[] value, int offset, int count) :见参数知意
    
     8.String(int[] codePoints, int offset, int count) :分配一个新的 String ,其中包含 Unicode code point数组参数的子阵列中的 字符 。 
    
     9.String(String original) :根据指定的字符串 初始化一个字符串对象 新的字符串的hash值和value字符数粗的值是一样的
    
     10.String(StringBuffer buffer) : 根据StringBuffer生成String
    
     11.String(StringBuilder builder)  : 根据StringBuilder 生成String 区别是StringBuffer的 length方法是线程安全的
    

    3.1 涉及到String的基本原理的方法:arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

     java.lang.System 类 (这个类不能被实例化就是说不能new 他的构造方式是private)下得到方法:
    
     arraycopy(Object src, int srcPos, Object dest, int destPos, int length) :
    
     将指定源数组中的数组从指定位置复制到目标数组的指定位置。
    
     参数:
        src:要被拷贝的数组对象  srcPos:要被拷贝数组的起始位置  dest:新的数组  destPos:新的数组存储的起始位置  length:要拷贝的数组长
    

    举了例子:

        public static void main(String[] args) {
            int[] a = new int[]{1, 4, 5, 6};
            int[] b = new int[4];
    
            System.out.println( Arrays.toString( a ) );
            System.out.println( Arrays.toString( b ) );
            /**
             * 从数组a的第2个元素开始拷贝到b 从b的第二个元素开始写入  长度为3
             * 注意:1 这是指 数组的下标索引 而数组都是从0开始的
             */
            System.arraycopy( a,1,b,1,3 );
            System.out.println( "——————————数组拷贝后输出————————————");
            System.out.println( Arrays.toString( a ) );
            System.out.println( Arrays.toString( b ) );
        }
    
    输出结果:
            
            [1, 4, 5, 6]
            [0, 0, 0, 0]
            ——————————数组拷贝后输出————————————
            [1, 4, 5, 6]
            [0, 4, 5, 6]
    

    4.常用方法

    4.1 charAt(int index):

       返回指定索引处的字符值 记住String本质是字符数组构建的 所以索引下标都是从0开始
    

    4.2 compareTo(String anotherString) :

          按照字典序与指定的字符串比较 返回结果:相等-0  参数在String之前返回>0 反之小于0
    
           public int compareTo(String anotherString) {
            int len1 = value.length;               //获取当前字符串的长度 
            int len2 = anotherString.value.length; //获取指定的字符串的长度
            int lim = Math.min(len1, len2);       //取二者之间比较小的值
            char v1[] = value;                    //获取当前字符串对应的字符数组
            char v2[] = anotherString.value;       //获取指定字符串对应的字符数组
    
            int k = 0;            
            while (k < lim) {                //从0开始 相同索引处的字符开始比较 如果都是相等的一直到到 最小长度
                char c1 = v1[k];
                char c2 = v2[k];
                if (c1 != c2) {
                    return c1 - c2;      //返回 比较结果
                }
                k++;
            }
            return len1 - len2;   //如果没个字符都相等 返回长度比较值 还是相等 那么就是相等
        }
    PS:这个方法大小写敏感  compareToIgnoreCase(String str) :这个方法忽略大小写比较
    

    4.3 concat(String str) :

        把指定的字符串 连接到字符串的末尾  字符串四大拼接方式之一
    

    源码:

          public String concat(String str) {
          int otherLen = str.length();  //获取指定字符串的长度属性
          if (otherLen == 0) {            //如果长度为0 返回当前这个字符串即可
              return this;
          }
          int len = value.length;          //获取当前字符串的长度属性
         //字符数组拷贝一个新的数组 注意这就是字符串不可变的根本原因 不是简单的final修饰一句话概括 
          char buf[] = Arrays.copyOf(value, len + otherLen);   
          str.getChars(buf, len);
          return new String(buf, true);   //构造一个新的字符串返回 
      }   
    

    4.4 length() :返回此字符串的长度。

    length方法源码

    很简单直白就是返回字符数组的长度

    4.4 equals(Object object) :比较对象是否相等。

    比较方法源码

    4.5 hashCode():、

    字符串hash值算法
    数学公式:val[0]31^(n-1) + val[1]31^(n-2) + ... + val[n-1] n是数组长度
    具体系数为什么选择31 粗浅的理解: 31是一个素数 31可以表示二进制位 11111-1 只占用5个字节 散列性比较好 而且不容易造成溢出 丢失精度 据说很多虚拟机都是31做了优化

    4.5substring(int beginIndex, int endIndex)

    原理:

    substring源码
    JDK6和JDK7之后这个方法的区别
      JDK6 是在堆空间创建新的String对象 但是不创建新的数组   
    
        数组还是采用原来的数组 和原来String对象区别是String的成员变量 offset字符数组开始下标和count数组长度改了 
    
        但是原来的数组还在  问题因为这个非常长的字符数组一直在被引用,所以无法被回收,就可能导致内存泄露 
    
        解决方法方法  substring(int beginIndex, int endIndex)+“” 这样就能创建一个新的数组
    
      JDK7  以后直接新建一个数组  让原来的数组不被引用 让垃圾回收清理掉
    

    4.6 replace(),replaceAll()、replaceFist()的原理和区别

    replace源码
     replaceFirst(): 正则匹配 只替换匹配到的第一个 用StringBuffer做的替换
     replaceAll()    正则匹配替换 全部 用StringBuffer做的替换
     replace()方法原理:![replace源码]
    

    4.7 String 对“+”的重载的两种情况:

             1.String s1 = "yves"; String s2 = s1 + "he";等于 String s1 = "yves";    
    
               String s2 = (new StringBuilder(String.valueOf(s1))).append("he").toString();
    
             2.String s1 = "yves" + "he";  这种JDK 会自动优化处理 “yveshe”
    
       字符串拼接的四种方式:
    
             plus 所谓"+"号 拼接:  效率低下 每次拼接都会创建一个 StringBuilder对象 多次拼接会创建大量对象
    
             concat拼接 :concat其实就是声明一个char类型的buf数组,将需要拼接的字符串都放在这个数组里,最后再转换 
    
                          成String对象 本质还是数组的拷贝  concat是要多少扩容多少 扩容效率低下
    
             StringBuilder/StringBuffer append方法: 调用父类AbstractStringBuilder的append方法,
    
                           而 StringBuffer是的append方法加了sychronized关键字,因此是线程安全的  
    
                           本质还是数组的拷贝 和 concat区别是数组的扩容 这两个是指数级扩容 
    
             效率:StringBuilder>StringBuffer >concat>plus  
    
                   StringBuilder线程不安全 没加锁 StringBuffer线程安全加了锁 concat扩容机制低下 plus需要创建对象
    
    StringBuilder扩容原理
    需要扩容的的时候直接扩大2倍+2 MAX_ARRAY_SIZE定义是 Integer.MAX_VALUE - 8 应该是21亿多(2^31 -1 -8)

    4.8 特殊的方法:String intern()

    这是一个原生的方法:

    image.png
    简单来说就是 如果字符串常量池中有新建的字符串 调用这个方法会直接去找一次字符常量池 如果有直接指向常量池的地址 没有的话就新建一个String对象
    字符串内存测试
    public static void main(String[] args) {
            String  str1="Java";
    
            String  str2 = new String( "Java" );
    
            System.out.println("Str1==Str2:"+(str1==str2));
    
            String  str3 = new String( "Java" ).intern();
    
            System.out.println("Str1==Str3:"+(str1==str3));
    
        }
    结果:
      Str1==Str2:false
      Str1==Str3:true
    

    相关文章

      网友评论

          本文标题:源码阅读篇-String类

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