本人自学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. .net core2.0添加json文件并转化成类注入控制器使用

    上一篇,我们介绍了如何读取自定义的json文件,数据是读取出来了,只是处理的时候太麻烦,需要一遍一遍写,很枯燥.那么有没有很好的办法呢?经过钻研,办法有了. 既然一个一个读取比较麻烦,那么可以把它放入 ...

  2. istio收集Metrics和日志信息

    1.切换到istio根目录 cd /data/istio/istio-0.7.1 2.安装prometheus kubectl apply -f install/kubernetes/addons/p ...

  3. 初探Margin负值(转)

    相对而言,margin 负值的使用机率在布局中似乎很少,但是我相信一旦你开始掌握就会着迷,接下来我们看看关于margin负值的一些资料: 它是一个有效的属性,至少w3c中明确描述如下:”Negativ ...

  4. HiJson(Json格式化工具)64位中文版下载 v2.1.2

    链接:https://pan.baidu.com/s/15gMvig15iUjpqSX7nUZ-5Q 密码:8086

  5. XShell上传文件到Linux服务器上

    在学习Linux过程中,我们常常需要将本地文件上传到Linux主机上,这里简单记录下使用Xsheel工具进行文件传输 1:首先连接上一台Linux主机 2:输入rz命令,看是否已经安装了lrzsz,如 ...

  6. Docker容器发布spring boot项目

    一.安装Docker环境 yum install docker 安装完成后,使用下面的命令来启动 docker 服务,并将其设置为开机启动: systemctl start docker.servic ...

  7. “史上更难就业季”暴露出啥隐情?

      如果说,2013年中国高校毕业生达到699万,被称为"史上最难就业季".那么2014年将成为去年之后的"更难就业季".据最新资料显示,2014年应届大学毕业 ...

  8. spring中@Resource和@Autowired理解

    一.@Resource的理解 @Resource在bean注入的时候使用,@Resource所属包其实不是spring,而是javax.annotation.Resource,只不过spring支持该 ...

  9. HTML学习之制作导航网页

    前言 今天用HTML写了一个网址导航,源代码如下: <html> <head> <title>网址导航</title> </head> &l ...

  10. 关于新建Eclipse新建一个WEB项目后创建一个jsp文件头部报错问题?

    点击项目右键→Build Path→Libraries→AddLibrary→Server Runtime→Apache Tomcat v7.0→Finsh  操作步骤如上! http://jingy ...