源码阅读篇-String类

2019-06-18  本文已影响0人  noobBird

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
上一篇下一篇

猜你喜欢

热点阅读