加入收藏 | 设为首页 | 会员中心 | 我要投稿 驾考网 (https://www.jiakaowang.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 教程 > 正文

Java String类源码阅读笔记

发布时间:2023-03-13 13:58:35 所属栏目:教程 来源:
导读:目录
一、前置
二、String类源码解析
1、String类继承关系
2、成员变量
3、构造方法
4、长度/判空
5、取字符
6、比较
7、包含
8、hashCode
9、查询索引
10、获取子串
11、拼接
12、替换
13、切割
14
目录
一、前置
二、String类源码解析
1、String类继承关系
2、成员变量
3、构造方法
4、长度/判空
5、取字符
6、比较
7、包含
8、hashCode
9、查询索引
10、获取子串
11、拼接
12、替换
13、切割
14、大小写转换
15、去空格
16、字符/字符串转换
17、格式化
18、intern()
三、常量池
四、其它扩展
1、String 真的不可变吗?
2、String类为什么要设计为不可变的?
3、String对“+”的重载

String类可谓是我们开发中使用最多的一个类了。对于它的了解,仅仅限于API的了解是不够的,必须对它的源码进行一定的学习。

一、前置
String类是Java中非常特别的一个类,虽然不是基本数据类型,但是通过一些处理,又在引用数据类型里比较特别,在学习之前,先了解一些JVM的知识。

Method Area:方法区,当虚拟机装载一个class文件时,它会从这个class文件包含的二进制数据中解析类型信息,然后把这些类型信息(包括类信息、常量、静态变量等)放到方法区中,该内存区域被所有线程共享,本地方法区存在一块特殊的内存区域,叫常量池(Constant Pool)。

Heap:堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,Java中的。

Stack:栈,又叫堆栈或者虚拟机栈。JVM为每个新创建的线程都分配一个栈。也就是说,对于一个Java程序来说,它的运行就是通过对栈的操作来完成的。栈以帧为单位保存线程的状态。JVM对栈只进行两种操作:以帧为单位的压栈和出栈操作。我们知道,某个线程正在执行的方法称为此线程的当前方法。

Program Count Register:程序计数器,又叫程序寄存器。JVM支持多个线程同时运行,当每一个新线程被创建时,它都将得到它自己的PC寄存器(程序计数器)。如果线程正在执行的是一个Java方法(非native),那么PC寄存器的值将总是指向下一条将被执行的指令,如果方法是 native的,程序计数器寄存器的值不会被定义。 JVM的程序计数器寄存器的宽度足够保证可以持有一个返回地址或者native的指针。

Native Stack:本地方法栈,存储本地方方法的调用状态。
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。Java把内存分为堆内存跟栈内存,前者主要用来存放对象,后者用于存放基本类型变量以及对象的引用。

二、String类源码解析
1、String类继承关系
public final class String
    implements java.io.Serializable,Comparable<String>,CharSequence {
看看String类的定义:

String是一个final类,既不能被继承的类
String类实现了java.io.Serializable接口,可以实现序列化
String类实现了Comparable<String>,可以用于比较大小(按顺序比较单个字符的ASCII码)
String类实现了 CharSequence 接口,表示是一个有序字符的序列,因为String的本质是一个char类型数组

2、成员变量
首先看看String类有哪些成员变量:

//用于字符存储的不可变字符数组
private final char value[];
// 缓存字符串的哈希码
private int hash;   //默认为0
//实现序列化的标识
private static final long serialVersionUID = -

private final char value[] 这是String字符串的本质,是一个字符集合,而且是final的,是不可变的。

3、构造方法
无参构造方法
   /**
   * 初始化String对象,将""空字符串的value赋值给实例对象的value,也是空字符,因为字符串是不可变的,所以不需要用这个方法
   * 
   */
    public String() {
        this.value = "".value;
    }
示例:

String str = new String()
str = "hello";
1.先创建了一个空的String对象
2.接着又在常量池中创建了一个"hello",并赋值给第二个String
3.将第二个String的引用传递给第一个String
这种方式实际上创建了两个对象

参数为String的有参构造方法
  /**
  *  参数为一个String对象
  * 将形参的value和hash赋值给实例对象作为初始化
  * 相当于深拷贝了一个形参String对象
  */
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
示例:

String str=new String("hello")
创建了一个对象。

参数为字符数组的有参构造方法
   /**
    * 参数为一个char字符数组
    * 将数组值拷贝赋给不可变字符数组
    * 这里为什么不直接赋值呢?
    * 因为参数char value[]是可变的,如果直接赋值,当参数数组发生变化时,就会影响到新生成的String对象,着就破坏的String的“不可变性”。 
   */
    public String(char value[]) {
        this.value = Arrays.copyOf(value,value.length);
    }

参数为字符数组(指定起止位置)的有参构造方法
   /**
   *  参数为char字符数组,offset(起始位置,偏移量),count(个数)
   * 在char数组的基础上,从offset位置开始计数count个,构成一个新的String的字符串
   **/
    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);
    }

参数为int数组(指定起止位置)的有参构造方法
      /** 
    * 参数为int字符数组,count(个数)
    * int数组的元素则是字符对应的ASCII整数值
    */
    public String(int[] codePoints,int count) {
        if (offset < 0) {
            throw new Stringindexoutofboundsexception(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new Stringindexoutofboundsexception(count);
            }
            if (offset <= codePoints.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) {
            throw new Stringindexoutofboundsexception(offset + count);
        }
        final int end = offset + count;
        // Pass 1: Compute precise size of char[]
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }
        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];
        for (int i = offset,j = 0; i < end; i++,j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c,v,j++);
        }
        this.value = v;
    }

有一些标为过时我们就不再关注了。

参数为byte数组(指定起止位置、字符编码)的有参构造方法
      /** 
    * 参数为byte数组,长度,和字符编码格式
    * 传入一个byte数组,从offset开始截取length个长度,其字符编码格式为charsetName,如UTF-8
    */
    public String(byte bytes[],int length,String charsetName)
            throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        //判断byte数组是否越界    
        checkBounds(bytes,length);
        this.value = StringCoding.decode(charsetName,bytes,length);
    }
StringCoding类的decode方法:

    static char[] decode(String charsetName,byte[] ba,int off,int len)
        throws UnsupportedEncodingException
    {
        StringDecoder sd = deref(decoder);
        String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
        if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
                              || csn.equals(sd.charsetName()))) {
            sd = null;
            try {
                Charset cs = lookupCharset(csn);
                if (cs != null)
                    sd = new StringDecoder(cs,csn);
            } catch (IllegalCharsetNameException x) {}
            if (sd == null)
                throw new UnsupportedEncodingException(csn);
            set(decoder,sd);
        }
        return sd.decode(ba,off,len);
    }
其余的从byte数组构造String的构造方法都是调用这个方法,这里就不再赘述。

参数为StringBuffer的有参构造方法
   /**
   *  参数类型为StringBuffer,将StringBuffer值数组拷贝给String的值数组
   * 线程安全的
   **/
    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(),buffer.length());
        }
    }
使用StringBuilder为参数构造类似,不过不是线程安全的。

(编辑:驾考网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章