JAVA源码解析-String源码

JAVA源码解析 专栏收录该内容
3 篇文章 0 订阅

  开学已经一周了,刚开学也是忙的不亦乐乎,总想开个好头嘛。眼看这就大三下学期了,表示压力山大啊,感觉再过上个不到半年我就成个社会人了,想着都怕。今天在知乎上闲逛,发现有初学者问关于String的问题,看了些解答之后突然很想看看String的源码,毕竟String类在平时的开发中用的相当相当多,所以就开始写这一专题《JAVA源码解析》,在String之后还会和大家一起阅读JAVA集合类的源码。当然这并不表示我的《JAVA基础进阶》就不写了,以后还会继续更新。←(开篇得有一段废话这是惯例)


一、String类

  String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例来实现的。首先String类是被final所修饰的,所以不允许被继承和修改,String类实现了Serializable、Comparable、CharSequence这三个接口,Serializable接口使得String可序列化;Comparable为String提供了比较器,使其可进行排序;CharSequence接口有length(),charAt(int index),subSequence(int start,int end)方法。

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

二、String属性

  String声明了4个变量如下图:
这里写图片描述
  String类中包含一个不可变的char数组用来存放字符串(在Java中String类其实就是对字符数组的封装),一个int型的变量hash用来存放计算后的哈希值。serialVersionUID变量提供序列化的ID,serialPersistentFields变量声明了一个可序列化的字段。

    private final char value[];
    //一个不可变的char数组用来存放字符串
    private final char value[];
    //一个int型的变量hash用来存放计算后的哈希值
    private int hash; // Default to 0
    //提供序列化的ID
    private static final long serialVersionUID = -6849794470754667710L;
    //声明了一个可序列化的字段
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

三、String构造方法

  String类型共有五个常用的构造器,其代码如下。

    //不带参数的构造函数
    public String() {
        this.value = "".value;
    }

    //参数为String类型的构造函数
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    //参数为字符数组的构造函数
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

    //参数为字符数组的构造函数,offset为复制的其实位置,count为要复制的字符个数
    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;
            }
        }
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

    /*整形数组作为参数的构造函数,分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。offset 参数是该子数组第一个代码点的索引,count 参数指定子数组的长度。将该子数组的内容转换为 char;后续对 int 数组的修改不会影响新创建的字符串。*/
    public String(int[] codePoints, int offset, 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;
            }
        }
        if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count;


        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));
        }


        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;
    }

四、String常用方法

  String类提供了许多对字符串进行操作的方法,很多方法都在平时的开发中经常使用,我们一起来看看一些常用的方法是如何实现的。

  getChars方法是将一个String字符串,按照给定的参数复制到目标字符数组的方法。其中传入4个参数:int类型的srcBegin为字符串中要复制的第一个字符的索引;int类型的srcEnd为字符串中要复制的最后一个字符之后的索引(要复制的最后一个字符位于索引 srcEnd-1 处);char类型的数组dst[]为目标数组;int类型的desBegin为目标数组中的起始偏移量。

    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            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);
    }

  equals方法用来将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true。源代码如下:

    public boolean equals(Object anObject) {
        //如果引用的是同一个对象,返回真
        if (this == anObject) {
            return true;
        }
        //如果不是String类型的数据,返回假
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            //如果char数组长度不相等,返回假
            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;
    }

  这段代码写的很好,说不出来的好,有一种美感。(JAVA的所有源代码其实都是(∩_∩))

  startsWith方法用来测试此字符串从指定索引开始的子字符串是否以指定前缀开始,该方法有两个参数,分别是String类型的prefix表示前缀,int类型的toffset表示在当前字符串中开始查找的位置。以下是源代码:

    public boolean startsWith(String prefix, int toffset) {
        char ta[] = value;
        int to = toffset;
        char pa[] = prefix.value;
        int po = 0;
        int pc = prefix.value.length;
        /*如果开始查找的位置小于0或大于当前字符串长度与指定前缀长度的差值,则返回false*/
        if ((toffset < 0) || (toffset > value.length - pc)) {
            return false;
        }
        //从此字符串的指定索引开始比较是否与指定前缀相等
        while (--pc >= 0) {
            if (ta[to++] != pa[po++]) {
                //不相等返回false
                return false;
            }
        }
        //相等返回true
        return true;
    }

  concat方法用来将指定字符串连接到此字符串的结尾,如果参数字符串的长度为 0,则返回此 String 对象。否则,创建一个新的 String 对象,用来表示由此 String 对象表示的字符序列和参数字符串表示的字符序列连接而成的字符序列。以下是源代码:

    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

  trim方法返回字符串的副本,忽略前导空白和尾部空白。这在开发中也是很常用的方法,源代码如下:

public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */

    //找到字符串前段没有空格的位置
    while ((st < len) && (val[st] <= ' ')) {
        st++;
    }
    //找到字符串末尾没有空格的位置
    while ((st < len) && (val[len - 1] <= ' ')) {
        len--;
    }
    //如果前后都没有出现空格,返回字符串本身
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

  subString方法返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。该方法包含两个int类型的参数,分别是: beginIndex - 起始索引(包括),endIndex - 结束索引(不包括)。源码如下:

    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);
        }
        /*如果起始索引为0并且结束索引为此字符串的长度则返回此字符串,否则创建新的字符串*/
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

注:我挑了一些常用的方法展现出来,有些方法没有一一列举大家可以自行查看jdk1.7的源码即可。


笔者水平有限,若有错漏,欢迎指正,如果转载以及CV操作,请务必注明出处,谢谢!

版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张三,你好,我是李四”   产生张三的密钥对(keyPairZhang)   张三生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置 简单 Java图片加水印,支持旋转和透明度设置 摘要:Java源码,文件操作,图片水印   util实现Java图片水印添加功能,有添加图片水印和文字水印,可以设置水印位置,透明度、设
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值