String(JDK1.8) 源码阅读记录

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40254498/article/details/82464207 String在 Java 中字符串属于对象。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40254498/article/details/82464207

String

  • 在 Java 中字符串属于对象。
  • Java 提供了 String 类来创建和操作字符串。

定义

使用了final ,说明该类不能被继承。同时还实现了:

  • java.io.Serializable
  • Comparable
  • CharSequence
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence { }

属性


 /** The value is used for character storage. 
  * String就是用char[]实现的。保存的  
  */
 private final char value[];

 /** Cache the hash code for the string 
 * hash 值
 */
 private int hash; // Default to 0

 /** use serialVersionUID from JDK 1.0.2 for interoperability 
 * Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。
 */
 private static final long serialVersionUID = -6849794470754667710L;

 /**
  * Class String is special cased within the Serialization Stream Protocol.
  * 类字符串在序列化流协议中是特殊的。
  * A String instance is written into an ObjectOutputStream according to
  * 将字符串实例写入ObjectOutputStream中,根据 a标签
  * <a href="{@docRoot}/../platform/serialization/spec/output.html">
  * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
  */
 private static final ObjectStreamField[] serialPersistentFields =
     new ObjectStreamField[0];

构造方法

String 的构造方法大概有十几种,其中最常用的如下:

/**
 * 根据字符串创建字符串对象
 * Initializes a newly created {@code String} object so that it represents
 * the same sequence of characters as the argument; in other words, the
 * newly created string is a copy of the argument string. Unless an
 * explicit copy of {@code original} is needed, use of this constructor is
 * unnecessary since Strings are immutable.
 *
 * @param  original
 *         A {@code String}
 */
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

/**
 * 根据byte数组创建字符串对象
 * byte[] to String 是根据系统的编码来的,但是也可以自己指定编码
 * Constructs a new {@code String} by decoding the specified array of bytes
 * using the platform's default charset.  The length of the new {@code
 * String} is a function of the charset, and hence may not be equal to the
 * length of the byte array.
 *
 * <p> The behavior of this constructor when the given bytes are not valid
 * in the default charset is unspecified.  The {@link
 * java.nio.charset.CharsetDecoder} class should be used when more control
 * over the decoding process is required.
 *
 * @param  bytes The bytes to be decoded into characters
 * @since  JDK1.1
 */
public String(byte bytes[]) {
    this(bytes, 0, bytes.length);
}

/**
 * 在Java中,String实例中保存有一个char[]字符数组,char[]字符数组是以unicode码来存储的,
 * String 和 char 为内存形式,byte是网络传输或存储的序列化形式。 
 * 所以在很多传输和存储的过程中需要将byte[]数组和String进行相互转化。 
 * 所以,String提供了一系列重载的构造方法来将一个字符数组转化成String, 
 * 提到byte[]和String之间的相互转换就不得不关注编码问题。  
 * 例如:
 * public String(byte bytes[], int offset, int length, Charset charset) {} 
 * String(byte bytes[], String charsetName)
 * String(byte bytes[], int offset, int length, String charsetName) 
 * and so on
 * String(byte[] bytes, Charset charset)是指通过charset来解码指定的byte数组, 
 * 将其解码成unicode的char[]数组,够造成新的String。 
 * 
 * 下面这个构造方法可以指定字节数组的编码  
 * Constructs a new {@code String} by decoding the specified array of
 * bytes using the specified {@linkplain java.nio.charset.Charset charset}.
 * The length of the new {@code String} is a function of the charset, and
 * hence may not be equal to the length of the byte array.
 *
 * <p> This method always replaces malformed-input and unmappable-character
 * sequences with this charset's default replacement string.  The {@link
 * java.nio.charset.CharsetDecoder} class should be used when more control
 * over the decoding process is required.
 *
 * @param  bytes
 *         The bytes to be decoded into characters
 *
 * @param  charset
 *         The {@linkplain java.nio.charset.Charset charset} to be used to
 *         decode the {@code bytes}
 *
 * @since  1.6
 */
 public String(byte bytes[], Charset charset) {
     this(bytes, 0, bytes.length, charset);
 }

/**
 * 根据char数组
 * Allocates a new {@code String} so that it represents the sequence of
 * characters currently contained in the character array argument. The
 * contents of the character array are copied; subsequent modification of
 * the character array does not affect the newly created string.
 *
 * @param  value
 *         The initial value of the string
 */
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);

/**
 * 根据 StringBuffer 创建 String对象
 * Allocates a new string that contains the sequence of characters
 * currently contained in the string buffer argument. The contents of the
 * string buffer are copied; subsequent modification of the string buffer
 * does not affect the newly created string.
 *
 * @param  buffer
 *         A {@code StringBuffer}
 */
public String(StringBuffer buffer) {
    synchronized(buffer) {
        this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
    }
}

/**
 * 根据 StringBuilder 创建 String对象
 * Allocates a new string that contains the sequence of characters
 * currently contained in the string builder argument. The contents of the
 * string builder are copied; subsequent modification of the string builder
 * does not affect the newly created string.
 *
 * <p> This constructor is provided to ease migration to {@code
 * StringBuilder}. Obtaining a string from a string builder via the {@code
 * toString} method is likely to run faster and is generally preferred.
 *
 * @param   builder
 *          A {@code StringBuilder}
 *
 * @since  1.5
 */
public String(StringBuilder builder) {
    this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

 /*
  * 这是一个受保护构造方法,因为不能继承,所以内部使用
  * 第二个属性基本没有用,只能是true
  * 从代码中可以看出来是直接引用,而不是新建一个,为了提高性能,节省内存等。
  * 保护的原因也是为了保证字符串不可修改。
  * Package private constructor which shares value array for speed.
  * this constructor is always expected to be called with share==true.
  * a separate constructor is needed because we already have a public
  * String(char[]) constructor that makes a copy of the given char[].
  */
  String(char[] value, boolean share) {
      // assert share : "unshared not supported";
      this.value = value;
  }

常用的方法

getByte

/**
 * 将字符串转成可用的 byte数组
 * 在通信的比较多,例如  网络中传输、8583报文、socket通信 
 * 要想不乱码,就得搞清楚通信双方所使用的字节编码!!!
 * Encodes this {@code String} into a sequence of bytes using the named
 * charset, storing the result into a new byte array.
 *
 * <p> The behavior of this method when this string cannot be encoded in
 * the given charset is unspecified.  The {@link
 * java.nio.charset.CharsetEncoder} class should be used when more control
 * over the encoding process is required.
 *
 * @param  charsetName
 *         The name of a supported {@linkplain java.nio.charset.Charset
 *         charset}
 *
 * @return  The resultant byte array
 *
 * @throws  UnsupportedEncodingException
 *          If the named charset is not supported
 *
 * @since  JDK1.1
 */
public byte[] getBytes(String charsetName)
        throws UnsupportedEncodingException {
    if (charsetName == null) throw new NullPointerException();
    return StringCoding.encode(charsetName, value, 0, value.length);
}

/**
 * 同上
 * Encodes this {@code String} into a sequence of bytes using the given
 * {@linkplain java.nio.charset.Charset charset}, storing the result into a
 * new byte array.
 *
 * <p> This method always replaces malformed-input and unmappable-character
 * sequences with this charset's default replacement byte array.  The
 * {@link java.nio.charset.CharsetEncoder} class should be used when more
 * control over the encoding process is required.
 *
 * @param  charset
 *         The {@linkplain java.nio.charset.Charset} to be used to encode
 *         the {@code String}
 *
 * @return  The resultant byte array
 *
 * @since  1.6
 */
public byte[] getBytes(Charset charset) {
    if (charset == null) throw new NullPointerException();
    return StringCoding.encode(charset, value, 0, value.length);
}
/**
 * 将使用系统默认编码。
 * 要注意的,部署的时候容易出错的地方就是这里,
 * windows 环境和linux环境字节编码不一样.所以建议指定编码方法
 * Encodes this {@code String} into a sequence of bytes using the
 * platform's default charset, storing the result into a new byte array.
 *
 * <p> The behavior of this method when this string cannot be encoded in
 * the default charset is unspecified.  The {@link
 * java.nio.charset.CharsetEncoder} class should be used when more control
 * over the encoding process is required.
 *
 * @return  The resultant byte array
 *
 * @since      JDK1.1
 */
public byte[] getBytes() {
    return StringCoding.encode(value, 0, value.length);
}

hashCode

/**
 * hash算法
 * hashCode可以保证相同的字符串的hash值肯定相同,
 * 但是,hash值相同并不一定是value值就相同。 
 * 所以要保证两个字符串相等还得用上 equals
 * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 */
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

equals

/**
  * 
  * 在hashmap中
  * 一定要重写 equals 和 hachcode 
  * 才能保证是同一个字符串
  * 正因为String 重写了我们才能愉快的使用字符串作为key
  */
 public boolean equals(Object anObject) {
     /** 首先判断是不是自己!*/
     if (this == anObject) {
         return true;
     }
     /** 在判断是不是String类型 */
     if (anObject instanceof String) {
         String anotherString = (String)anObject;
         int n = value.length;
         /** 判断长度 */
         if (n == anotherString.value.length) {
             char v1[] = value;
             char v2[] = anotherString.value;
             int i = 0;
             /** 判断字节 */
             while (n-- != 0) {
                 if (v1[i] != v2[i])
                     return false;
                 i++;
             }
             return true;
         }
     }
     return false;
 }

substring

这个方法在JDK1.6(含1.6)以前和JDK1.7之后(含1.7)有了不一样的变化

JDK1.6 substring

/** 
 * 仍然创建新的字符串但是 旧字符串还在 只是新的引用了旧的一部分
 * 但旧字符串很大的时候,因为新的引用一小部分而无法回收会导致内存泄漏
 * 一般使用加上一个空的字符串来生成新的解决这个问题
 * str = str.substring(x, y) + ""
 */
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

public String substring(int beginIndex, int endIndex) {
    /** 校验数组溢出 */
    return  new String(offset + beginIndex, endIndex - beginIndex, value);
}
  • 内存泄露:在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。 内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

JDK1.8 substring

jdk1.7之后直接新建了一个字符串 。虽然增加了内存,但是解决了内存泄漏问题。

public String substring(int beginIndex, int endIndex) {

    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) {
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);

public String(char value[], int offset, int count) {
   if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count <= 0) {
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        if (offset <= value.length) {
            this.value = "".value;
            return;
        }
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

valueOf

/** 调用对象自己的toString方法 */
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}
public static String valueOf(char data[]) {
    return new String(data);
}
public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}

String + 号重载

 String str = "abc";
 String str1= str + "def";

 /** 反编译之后 */
 String str = "abc";
 String str1= (new StringBuilder(String.valueOf(str))).append("def").toString();

spilt

按照字符regex将字符串分成limit份。

 public String[] split(String regex, int limit) {
     /* fastpath if the regex is a
      (1)one-char String and this character is not one of the
         RegEx's meta characters ".$|()[{^?*+\\", or
      (2)two-char String and the first char is the backslash and
         the second is not the ascii digit or ascii letter.
      */
     char ch = 0;
     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 {    // last one
                 //assert (list.size() == limit - 1);
                 list.add(substring(off, value.length));
                 off = value.length;
                 break;
             }
         }
         // If no match was found, return this
         if (off == 0)
             return new String[]{this};

         // Add remaining segment
         if (!limited || list.size() < limit)
             list.add(substring(off, value.length));

         // Construct result
         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);
 }

按照字符regex将字符串分割

 /** 直接调用 split(String regex, int limit) limit 为 零 */
 public String[] split(String regex) {
    return split(regex, 0);
  }

equalsIgnoreCase

public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
    }

三目运算符加 && 代替 多个if

replaceFirst、replaceAll、replace

String replaceFirst(String regex, String replacement)
String replaceAll(String regex, String replacement)
String replace(CharSequence target, CharSequence replacement)
  • replace的参数是char和CharSequence,即可以支持字符的替换,也支持字符串的替换
  • replaceAll和replaceFirst的参数是regex,即基于规则表达式的替换,replace只要有符合就替换
  • replaceFirst(),只替换第一次出现的字符串;

其他方法

String 类中还有很多方法。例如:

  • public int length(){}
    返回字符串长度
  • public boolean isEmpty() { }
    返回字符串是否为空
  • public char charAt(int index) {}
    返回字符串中第(index+1)个字符
  • public char[] toCharArray() {}
    转化成字符数组
  • public String trim(){}
    去掉两端空格
  • public String toUpperCase(){}
    转化为大写
  • public String toLowerCase(){}
    转化为小写
  • public String concat(String str) {}
    拼接字符串
  • public boolean matches(String regex){}
    判断字符串是否匹配给定的regex正则表达式
  • public boolean contains(CharSequence s)
    判断字符串是否包含字符序列s
目录
相关文章
|
4月前
|
缓存 Dubbo Java
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
|
7月前
|
Java 容器
阿里内部流传的JDK源码剖析手册!GitHub已获上千万的访问量
相信现在已经有很多小伙伴知道了“微软”要对JDK下手了! JDK是什么? jdk是Java语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。jdk是整个java开发的核心,它包含了JAVA的运行环境和JAVA工具。相对而言,没有jdk的话,无法编译Java程序(指java源码.java文件),如果想只运行Java程序(指class或jar或其它归档文件),要确保已安装相应的JRE。
196 0
|
3月前
|
缓存 Java Spring
Spring 源码阅读 66:基于 JDK 的 AOP 代理如何获取拦截器链(4)- 将 Advice 封装为拦截器
【1月更文挑战第1天】本文分析了 Advice 被封装成 MethodInterceptor 的过程,Spring AOP 用到的五种 Advice 中,有些本身就是 MethodInterceptor 的实现类,而有些需要通过适配器的封装。
41 0
|
7月前
|
设计模式 Java 程序员
太爆了!阿里最新出品2023版JDK源码学习指南,Github三天已万赞
最近后台收到很多粉丝私信,说的是程序员究竟要不要去读源码?当下行情,面试什么样的薪资/岗位才会被问到源码? 对此,我的回答是:一定要去读,并且要提到日程上来! 据不完全统计,现在市面上不管是初级,中级,还是高级岗,面试的时候都有可能会问到源码中的问题,它已经成为程序员常规必备的一个技术点。如果你当下想通过一个面试,或者想把中级薪资要到相对于比较高的话,源码这块就必须要会。
94 0
|
1月前
|
算法 Java 索引
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
31 0
|
2月前
|
设计模式 Java
根据JDK源码Calendar来看工厂模式和建造者模式
根据JDK源码Calendar来看工厂模式和建造者模式
33 3
|
3月前
|
Java Linux iOS开发
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
23 0
|
3月前
|
XML Java 数据格式
Spring 源码阅读 70:基于 JDK 的 AOP 代理拦截器链执行(4)- 容易被忽略的 ExposeInvocationInterceptor
【1月更文挑战第5天】本文分析了 Spring AOP 拦截器链中的一个特殊拦截器 ExposeInvocationInterceptor 的注册的时机以及它的作用。至此,基于 JDK 的 AOP 代理拦截器链执行的逻辑就分析完了。
382 0
|
3月前
|
Java 索引 Spring
Spring 源码阅读 69:基于 JDK 的 AOP 代理拦截器链执行(3)- MethodInterceptor 分析
【1月更文挑战第4天】本文详细分析了 Spring AOP 中五种增强类型对应的拦截器中增强方法的执行逻辑,结合上一篇中分析的 ReflectiveMethodInvocation 中proceed方法的执行逻辑,就组成了完整的拦截器链递归调用的逻辑。
34 0
|
3月前
|
Java 索引 Spring
Spring 源码阅读 68:基于 JDK 的 AOP 代理拦截器链执行(2)- ReflectiveMethodInvocation 分析
【1月更文挑战第3天】本文分析了 ReflectiveMethodInvocation 类中的proceed方法,通过对这个方法的分析,了解了连接器链中的增强逻辑是如何逐层执行的,以及目标方法是什么时候被调用的。
30 0