本人自学java两年,有幸初入这个行业,所以功力尚浅,本着学习与交流的态度写一些学习随笔,什么错误的地方,热烈地希望园友们提出来,我们共同进步!这是我入园写的第一篇文章,写得可能会很乱。

一、什么是String

  String是java中的一个类,位于java.lang包中,也就是我们平常所说的字符串。字符串是我们在编程的过程中用到最多的类,同时在面试中也经常会出现相关的面试题,所以掌握String字符串的特性是很有必要,不仅可以提高对java的理解,同时还能有助于提高编写程序的质量。

  从源码中我们可以看到,其实String内部维护的是一个char数组,对于字符串的操作,其实就是对内部数组的操作。值得注意的是,char数组是用final关键字修饰,也就是说每一次创建字符串的时候,都会创建一个对象(String不变性)

 /** The value is used for character storage. */
private final char value[];

二、String对象的创建

  最常用的字符串创建的方式有两种,第一种是直接用字面量的引用来创建,第二种是用new关键字来创建。两种创建方式存在很大的区别,上面有提到,字符串是不可变的,所以每次创建时都会创建一个新的对象,如果这样一直创建下去的话,势必会浪费很多的内存,java为了解决这个问题提高程序的性能,用第一种方式创建字符串的时候,首先,会先判断创建的字符串在常量池中是否已经存在,如果存在,则将引用指向它,否则创建新的字符串并在常量池中缓存起来,这样一来创建相同的字符串时只需要将引用指向常量池中已存在的字符串即可,无需再创建,从而节约内存;第二种用new关键字创建,这种方式无论常量池中是否已经创建都会创建一个新的对象,所以本人推荐在平常创建字符串的时候使用第一种方式。如下代码可验证:

     String a = "aaa";
        String b = "aaa";
        String c = new String("aaa");
        String d = new String("aaa");
        System.out.println(a == b);//发回true
        System.out.println(a == c);//返回false
        System.out.println(c == d);//返回false

三、String对象的使用

  String中有非常多的api,下面我将介绍一种常用的api:

  1、public char charAt(int index)

  获取指定位置的字符,index指定位置,源码如下:

    public char charAt(int index) {
if ((index < ) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}

这个方法非常简单,通过传入的数字来获取到数组中对应的下标位置的值,然后直接返回该值即可;

应用场景:获取字符串中指定位置的字符,但是一次只能获取一个字符。

  2、public boolean startsWith(String prefix, int toffset)

  这个方法可以用来判断字符串是否以指定的字符或字符串开始,如果是返回true,否则返回false。有两个重载的方法:

  (1)startsWith(String prefix, int toffset):prefix是指需要与原字符串匹配的字符串,toffset是指开始匹配的位置:

 public boolean startsWith(String prefix, int toffset) {
char ta[] = value;//原字符串
int to = toffset;
char pa[] = prefix.value;//匹配字符串
int po = ;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
if ((toffset < ) || (toffset > value.length - pc)) {
return false;
}
    //这里是主要的判断逻辑
while (--pc >= ) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}

  (2)startsWith(String prefix):默认从第一个字符串开始匹配

 public boolean startsWith(String prefix) {
return startsWith(prefix, );
}

  3、public String substring(int beginIndex, int endIndex)

  这个方法是用来截取指定字符串长度方法,返回一个新的字符串。

    public String substring(int beginIndex, int endIndex) {
if (beginIndex < ) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
     //截取字符串长度
int subLen = endIndex - beginIndex;
if (subLen < ) {
throw new StringIndexOutOfBoundsException(subLen);
}
    //调用构造方法
return ((beginIndex == ) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}

构造方法:

 public String(char value[], int offset, int count) {
if (offset < ) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= ) {
if (count < ) {
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);
}

这个构造方法主要看Arrays.copyOfRange复制数组的方法,其中value是原数组,offset是开始复制的位置,offset+count是需要复制的长度,该方法返回一个新的数组存放在String维护的数组中,完成字符串的截取。

  4、public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)

  该方法是将当前字符串从 srcBegin 位置到 srcEnd 结束为长度截取字符串放入到 dst 中,从 dstBegin 位置开始放

 public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < ) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

从源码中可以看到,该方法调用了数组复制的方法 System.arraycopy ,其实看了String源码都知道 该类中大量使用了arraycopy这个方法,原因是String内部维护的是一个不可变的数组,所以在对数组进行操作的时候都是要创建新的数组(新的字符串)进行操作。所以非常有必要说一下System.arraycopy这个方法,源码如下:

     /*
   * @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
* @exception IndexOutOfBoundsException if copying would cause
* access of data outside array bounds.
* @exception ArrayStoreException if an element in the <code>src</code>
* array could not be stored into the <code>dest</code> array
* because of a type mismatch.
* @exception NullPointerException if either <code>src</code> or
* <code>dest</code> is <code>null</code>.
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);

这是一个本地静态方法,没有返回值

  src:原数组

  srcPos:原数组开始被复制的位置

  dest:目标数组

  destPos:开始放复制字符的地方

  length:复制的长度

  5、public String concat(String str)

  连接两个字符串,这个方法比较少用到,在写代码时基本没有用过:

public String concat(String str) {
int otherLen = str.length();
if (otherLen == ) {
return this;
}
     //当前字符串长度
int len = value.length;
      //复制当前字符串维护的数组到 buf中,长度为当前字符串长度加拼接字符串长度
char buf[] = Arrays.copyOf(value, len + otherLen);
     //将 str 的字符放到 buf 中
str.getChars(buf, len);
return new String(buf, true);
}

步骤:创建一个两个拼接字符串长度之和大小的数组,然后将两个字符串放入改数组中即可

  6、public static String valueOf(char c)

  这个方法是将指定数据类型转换成字符串类型,有非常多重载的方法,8个基本数据类型基本都有,比较简单,就不用多说了。

  7、public String replace(char oldChar, char newChar)

  该方法将字符串中出现的字符 oldChar 全部替换成 newChar

public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -;
char[] val = value; /* avoid getfield opcode */
       //获取到需要替换字符第一次出现的地方
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
         //临时字符数组
char buf[] = new char[len];
         //将第一次出现替换字符之前的所有字符放到临时字符数组中
for (int j = ; j < i; j++) {
buf[j] = val[j];
}
         //替换字符
while (i < len) {
char c = val[i];
            //将oldChar替换成newChar
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}

  8、public String replaceAll(String regex, String replacement)

  将当前字符串中出现的regex替换成replacement

  9、public String trim()

  去掉字符串前后的空格(不能去除中间部分)

public String trim() {
int len = value.length;
int st = ;
char[] val = value; /* avoid getfield opcode */ while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - ] <= ' ')) {
len--;
}
return ((st > ) || (len < value.length)) ? substring(st, len) : this;
}

  9、public String[] split(String regex, int limit)

  将字符串按照regex规则分割成若干字符串数组,并返回。limit:如果大于0,最多返回limit大小的数组,如果为0,则返回全部可能存在的字符串数组,该方法还有个重载方法,就是不用传入limit参数,与limit等于0的情况是一样的。

  结语:

  1、第一次写东西,所以写得很乱

     2、可能因为自己功力不行,所以感觉整片文章都是粘贴源码的,没有太多实质性的东西

     3、喜欢java的同学,欢迎一起讨论,我们共同进步

jdk源码阅读笔记-String的更多相关文章

  1. jdk源码阅读笔记-LinkedHashMap

    Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...

  2. jdk源码阅读笔记-ArrayList

    一.ArrayList概述 首先我们来说一下ArrayList是什么?它解决了什么问题?ArrayList其实是一个数组,但是有区别于一般的数组,它是一个可以动态改变大小的动态数组.ArrayList ...

  3. jdk源码阅读笔记-HashSet

    通过阅读源码发现,HashSet底层的实现源码其实就是调用HashMap的方法实现的,所以如果你阅读过HashMap或对HashMap比较熟悉的话,那么阅读HashSet就很轻松,也很容易理解了.我之 ...

  4. JDK源码学习笔记——String

    1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() { ...

  5. jdk源码阅读笔记

    1.环境搭建 http://www.komorebishao.com/2020/idea-java-jdk-funyard/ 2. 好的源码阅读资源 https://zhuanlan.zhihu.co ...

  6. jdk源码阅读笔记-Integer

    public final class Integer extends Number implements Comparable<Integer> Integer 由final修饰了,所以该 ...

  7. jdk源码阅读笔记-HashMap

    文章出处:[noblogs-it技术博客网站]的博客:jdk1.8源码分析 在Java语言中使用的最多的数据结构大概右两种,第一种是数组,比如Array,ArrayList,第二种链表,比如Array ...

  8. jdk源码阅读笔记-LinkedList

    一.LinkedList概述 LinkedList的底层数据结构为双向链表结构,与ArrayList相同的是LinkedList也可以存储相同或null的元素.相对于ArrayList来说,Linke ...

  9. jdk源码阅读笔记-AbstractStringBuilder

    AbstractStringBuilder 在java.lang 包中,是一个抽象类,实现 Appendable 接口和 CharSequence 接口,这个类的诞生是为了解决 String 类在创建 ...

随机推荐

  1. ccf 目录格式转换

    任务背景: 在网络上获取的ccf目录的格式是PDF,但是要进行数据分析时,PDF格式的数据是不符合要求的,因此需要将pdf格式转化为excel格式 任务目的: 将pdf格式的CCF目录转化为excel ...

  2. 架构之微服务设计(Nginx + Upsync)

    Upsync,微博开源基于Nginx容器动态流量管理方案 . Nginx 以其超高的性能与稳定性,在业界获得了广泛的使用,微博的七层就大量使用了 Nginx .结合 Nginx 的健康检查模块,以及动 ...

  3. 1 Numpy-科学计算

    在Python中,使用list可以保存一组值,可以作为数组使用,但是比较浪费内存和时间.类似的array模块,不支持多维,也没有各种函数运算,因此也极其不方便. 为解决这一问题,Python提供了Nu ...

  4. PyCharm下载及安装教程

    pycharm官网地址 https://www.jetbrains.com/pycharm/download/#section=windows 下载社区版 选择安装路径E:\Python\pychar ...

  5. JS/jquery实现鼠标控制页面元素显隐

    最近网站要上一个活动广告横幅,当用户鼠标划过时显隐二维码.像这种鼠标事件控制页面元素显隐的情况,码农们会经常遇到,可以通过javascript或jquery代码实现,下面就几种常见需求一起归纳一下. ...

  6. SSM-MyBatis-10:Mybatis中SqlSession的getMapper()和简单的工具类MyBatisUtils

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- getMapper的作用,获取到接口,直接通过点的方式调用方法,以免直接手打的方式写错方法名,(强类型的方式) ...

  7. 【手记】小心在where中使用NEWID()的大坑

    这个表达式: --把GUID弄成正整数,然后取模 是随机返回0.1.2这三个数,不可能返回其它东西,但是如果把它用在where里面,就会发生很神奇的事情,比如这个查询: --创建一个只有1列3行的表, ...

  8. Using INSERT IGNORE with MySQL to prevent duplicate key errors

    An error will occur when inserting a new record in MySQL if the primary key specified in the insert ...

  9. thinter中lable标签控件(二)

    lable控件 对于tkinter来说,学起来很简单,只要设置好相应的参数即可出结果,所以不用刻意去记住这些参数.学习一遍后理解每个参数的作用是什么即可. 当下次用到的时候来笔记上看一下就行. 内容很 ...

  10. CSS布局-圣杯布局

    圣杯布局 圣杯布局很完美(兼容所有浏览器,包括IE6),但是使用了相对定位,布局有局限性,宽度控制要改的地方也多. 第一种方法公用部分: .lgyz, .lzrg, .lrzcg, .lcgrz, . ...