dataflow论文阅读笔记 Polysh的安装使用 SnappyData排序函数比较 Squirrel-sql客户端连接SnappyData手册 在虚拟机里显示Hello World spark学习博客推荐 SnappyData学习博客和官网文章 Docker常用命令 MyBatis支持的OGNL语法 mysql性能优化 mysql性能优化-优化Sql语句 java各版本新特性 mac上命令行操作 explain输出格式 从文件中读取zk配置——ZooKeeper编程技能(1) git进阶经验-从项目中删除移除的目录 Mongodb 学习之shell命令操作(3) mysql命令 git进阶经验-从多模块项目中分理子模块 从零学hadoop-搭建基础(单点)的Hdfs环境 ZooKeeper集群操作脚本 Vue安装使用 2 初学JVM之问答式记住类加载机制 2 初学JVM之问答式记住虚拟机性能监控与故障处理工具 2 初学JVM之问答式记住垃圾收集器 log4j2 按天生成日志文件 1 初学JVM之问答式记住java自动内存管理 MapReduce学习心得之MapReduce初识 log4j2 日志发送到kafka配置实战 log4j2 日志配置实战 Mongodb 学习之shell命令操作(二) Mongodb 学习之linux版本安装(一) Dubbo的初级使用 ServiceLoader内部实现分析 ServiceLoader 初级应用 log4j日志发送邮件配置实战 红黑树笔记 IDEA首次使用之前的配置 java源码学习之Enum java源码学习之String 自定义Spring tag标签 编写一键发布脚本 记一次Spring Scheduler莫名不执行的一次堆栈分析 kafka的基本操作 nginx 5:Nginx内部变量 nginx 4:Nginx日志管理 提高hadoop配置效率的shell脚本 Hive编程指南之一 Hive的安装 Ambari服务器安装 Ambari服务器管理集群 HBase分布式安装 windows下Eclipse远程调试运行MR程序 基于MapReduce新的API的编程Demo-wordCount window下Eclipse远程只读HDFS上的文件 YARN上节点标签 编写第一个MapReduce的wordcount程序 NodeManager的重启 搭建JStorm集群 YARN上的web应用代理 YARN上的ResourceManager的高可用方案 配置vmware中的虚拟机使用宿主机的共享网络 YARN架构简述 HDFS 架构 Spring的统一异常处理机制 Tomcat 配置服务 HDFS的viewfs指南 HDFS的Federation之路 HDFS基于QJM的HA之路 nginx 3:Nginx反向代理 mybatis操作主体流程 1.正则表达式学习-基础篇 log4j日志配置详解 mysql的时间函数 nginx 2:Nginx模块配置理论及实战 HashMap相关解析和测试文章 工作一年后的面试 用私有构造器或枚举类型强化Singleton属性 java中比较重要的图 mybatis处理枚举类 mybatis集成进spring Spring比较重要的几个截图 21.hadoop-2.7.2官网文档翻译-使用NFS的HDFS高可用性 20.hadoop-2.7.2官网文档翻译-使用仲裁日志管理器的HDFS高可用性 markdown在jekyll中支持的一些操作 Spring项目中配置sl4j和log4j的日志配置 19.hadoop-2.7.2官网文档翻译-HDFS命令指南 Spring的profile机制介绍 mybatis-generator反向生成 18.hadoop-2.7.2官网文档翻译-HDFS用户指南 17.hadoop-2.7.2官网文档翻译-实现Hadoop中Dapper-like追踪 16.hadoop-2.7.2官网文档翻译-Hadoop的KMS(key 管理服务器)-文档集 15.hadoop-2.7.2官网文档翻译-Hadoop的http web认证 14.hadoop-2.7.2官网文档翻译-服务级别的授权指南 13.hadoop-2.7.2官网文档翻译-安全模式中的Hadoop 09.hadoop-2.7.2官网文档翻译-Hadoop命令行微型集群 12.hadoop-2.7.2官网文档翻译-机架感知 11.hadoop-2.7.2官网文档翻译-代理用户-超级用户对其他用户的代表 10.hadoop-2.7.2官网文档翻译-原生库指南 08.hadoop-2.7.2官网文档翻译-文件系统规范 07.hadoop-2.7.2官网文档翻译-Hadoop接口类别 (转)浅析 Decorator 模式,兼谈 CDI Decorator 注解 06.hadoop-2.7.2官网文档翻译-Hadoop的兼容性 05.hadoop-2.7.2官网文档翻译-文件系统命令 04.hadoop-2.7.2官网文档翻译-Hadoop命令指南 03.hadoop-2.7.2官网文档翻译-集群安装 02.hadoop-2.7.2官网文档翻译-单节点集群安装 01.hadoop-2.7.2官网文档翻译-概述 Http 协议相应状态码大全及常用状态码 IDEA快捷键 JDBC Type与Java Type redis 12:redis 操作集合 mybatis-generator错误集合 redis 11:redis 错误集合 nginx 1:nginx的安装 redis 10:redis cluster命令操作 redis 9:redis实例集群安装 java设计模式 hadoop集群学习笔记(1) Apache Shiro 简介 vim编辑神器的进阶命令 Eclipse配置 Eclipse快捷键 Linux 测试题 Linux脚本学习(1) Linux启动简要过程 Centos7上安装Mysql hadoop集群学习笔记(1) (转)分布式发布订阅消息系统 Kafka 架构设计 maven 命令 Kafka集群安装 Kafka初步使用 redis 8:redis server 和 scripting命令操作 redis 7:redis transaction 和 connection命令操作 redis 6:redis hash 命令操作 redis 5:redis sorted_set 命令操作 搭建本地Jekyll+Markdown+Github的开发环境 Spring源码阅读笔记(2) redis 4:redis set命令操作 Spring添加任务调度配置 redis 3:Redis list命令操作 redis 2:redis 一般命令操作 redis 1:redis单机安装笔记 redis 0:redis配置属性描述 Spring源码阅读笔记(1) spark 错误集锦 spark集群安装 Linux 基本命令操作 Hadoop错误信息处理 Hadoop代码拾忆 从零开始搭建spring-springmvc-mybatis-mysql和dubbo项目 java知识点札记 java排错 Google Java Style 中文版 git进阶经验 github使用经验 MongoDB用户角色授权与AUTH启用 MongoDB 命令 MongoDB 特定规范 Spring MVC实现跳转的几种方式 史上最全最强SpringMVC详细示例实战教程 Spring 零星笔记 js中(function(){…})()立即执行函数写法理解 如何解决跨域问题 创建ajax简单过程 前端定位 设置MYSQL允许通过IP访问 mybatis异常 :元素内容必须由格式正确的字符数据或标记组成 如何为 WordPress 绑定多个域名的方法s WordPress工作原理之程序文件执行顺序(传说中的架构源码分析) Spring源码导入Eclipse中 基于PHPnow搭建Eclipse开发环境 解决wordpress首页文章内容截断处理的几种方法 ZooKeeper理论知识 ZooKeeper集群安装配置 Git常用命令速查表 Linux 4:磁盘与文件系统管理 Linux 3:文件与目录管理 Linux 2:文件权限与目录配置 Markdown输入LaTeX数学公式
从零学hadoop-搭建基础(单点)的Hdfs环境 MapReduce学习心得之MapReduce初识 Ambari服务器安装 Ambari服务器管理集群 windows下Eclipse远程调试运行MR程序 基于MapReduce新的API的编程Demo-wordCount window下Eclipse远程只读HDFS上的文件 YARN上节点标签 编写第一个MapReduce的wordcount程序 NodeManager的重启 YARN上的web应用代理 YARN上的ResourceManager的高可用方案 YARN架构简述 HDFS 架构 HDFS的viewfs指南 HDFS的Federation之路 HDFS基于QJM的HA之路 21.hadoop-2.7.2官网文档翻译-使用NFS的HDFS高可用性 20.hadoop-2.7.2官网文档翻译-使用仲裁日志管理器的HDFS高可用性 19.hadoop-2.7.2官网文档翻译-HDFS命令指南 18.hadoop-2.7.2官网文档翻译-HDFS用户指南 17.hadoop-2.7.2官网文档翻译-实现Hadoop中Dapper-like追踪 16.hadoop-2.7.2官网文档翻译-Hadoop的KMS(key 管理服务器)-文档集 15.hadoop-2.7.2官网文档翻译-Hadoop的http web认证 14.hadoop-2.7.2官网文档翻译-服务级别的授权指南 13.hadoop-2.7.2官网文档翻译-安全模式中的Hadoop 09.hadoop-2.7.2官网文档翻译-Hadoop命令行微型集群 12.hadoop-2.7.2官网文档翻译-机架感知 11.hadoop-2.7.2官网文档翻译-代理用户-超级用户对其他用户的代表 10.hadoop-2.7.2官网文档翻译-原生库指南 08.hadoop-2.7.2官网文档翻译-文件系统规范 07.hadoop-2.7.2官网文档翻译-Hadoop接口类别 06.hadoop-2.7.2官网文档翻译-Hadoop的兼容性 05.hadoop-2.7.2官网文档翻译-文件系统命令 04.hadoop-2.7.2官网文档翻译-Hadoop命令指南 03.hadoop-2.7.2官网文档翻译-集群安装 02.hadoop-2.7.2官网文档翻译-单节点集群安装 01.hadoop-2.7.2官网文档翻译-概述 hadoop集群学习笔记(1) hadoop集群学习笔记(1) Hadoop错误信息处理 Hadoop代码拾忆

java源码学习之String

2017年03月21日
摘要:阅读源码分析String类的实现

String类实现

String类实现了SerializableComparableCharSequence接口。

String类实现特性

  1. String为常量,在String对象被创建后值就不能再变化;
  2. Java程序中所有的字面量值都被实现为String类的实例;
  3. String的缓冲区是支持多变的,但String对象是不可变的;
  4. String类包含了检查单个字符序列、比较字符串、搜索字符串、提取子串、创建字符串所有字符的全大写和全小写副本的方法;
  5. 大小写映射基于Character类中指定的Unicode标准版本;
  6. Java语言提供了特殊的支持string字符串连接的操作(+),并将其他对象转为字符串。同时String的连接是通过StringBuilder或者StringBuffer及其方法实现的;而转换操作是通过toString方法,在Object中定义,并且被所有的类继承
  7. String表示UTF-16格式化的字符串(A String represents a string in the UTF-16 format in which supplementary characters are represented by surrogate pairs),索引值参照字符码单元,因此补充的字符在String中使用了两个位置;
  8. String提供了处理Unicode代码点的方法,另外处理Unicode的码单元。

String实现原理

存储:每个String对象的value存储在final修饰的char数组中。

实例化:无参构造方法会创建空字符串""的对象。因为String是不可变的,因此可以不必使用String的构造方法创建对象,可以使用如String str = "abc"方式。而且对于String的所有构造方法,虽然代码中可能没有的源值进行非空检查,但编译器在编译时会对其检查,如果判断为null,会直接抛出NullPointerException异常。

相关方法内部实现说明

  • public String(char value[])

    内部通过`Arrays.copyof()`方法将char数组的值复制给新的String对象,对原来char数组值的修改不会影响该新String对象。
    
  • public String(char value[], int offset, int count)

    先检查offset和count,对于这两个值小于0的会抛出`StringIndexOutOfBoundsException`异常;
    在offset不小于0,count为0时,会为新对象赋`""`值;
    在`offset+count>value.length`时也会抛出`StringIndexOutOfBoundsException`异常;
    在以上都通过后,会调用`Arrays.copyOfRange()`从value中复制值并赋给新对象的value。
    
  • public String(byte bytes[], int offset, int length, String charsetName)

    先检查获取范围是否合法,然后通过调用"StringCoding.decode()"将自定位置范围的字节码转为对应的ASCII的字符,并设置为指定的编码。
    
  • public char charAt(int index)

    参数为char数组的下标索引,返回的为数组指定索引位置的某个字符(包括中文字符)。
    
  • public int codePointAt(int index)

    返回索引所在位置的字符对应的Unicode码值(十进制)
    
  • public int codePointBefore(int index)

    返回索引所在前一个位置的字符的Unicode码值(十进制)
    
  • public int codePointCount(int beginIndex, int endIndex)

  • public int compareTo(String anotherString)

    先判断有没有空字符串,没有空字串符则比较每个字符,否则比较两个串长度。
    
  • public int compareToIgnoreCase(String str)

    会调用String内部静态final类`CASE_INSENSITIVE_ORDER`的`compare(s1,s2)`方法。该方法会循环比较s1和s2对应字符A和B,A和B不相等时,都转为大写比较,还不相等则都转为小写比较,如果还不相等,直接返回A和B的ASCII值的差;如果所有的字符都相等则比较字符串s1和s2长度。
    
  • public String concat(String str)

    连接字符串。字符串长度为0,直接返回当前字符串s1;否则用`Arrays.copyOf()`对s1进行扩容,然后将str的字符全都复制到中间变量buf字符数组中,然后使用buf创建新的字符串并返回;
    
  • public boolean contains(CharSequence s)

    内部是对`public int indexOf(String str)`是否大于-1的判断,如果大于-1则判定为存在该子字符串。具体实现在`public int indexOf(String str)`中阐述。
    
  • public boolean contentEquals(CharSequence cs)

    对于AbstractStringBuilder的类型,根据cs是否为`StringBuffer`判断是否同步调用`private boolean nonSyncContentEquals(AbstractStringBuilder sb)`;
    如果是String类型,调用String的`equal()`方法,否则通过比对两个字符序列的长度和每个字符是否相等判断两个字符序列是否相等。
    
  • public default IntStream chars()

    JDK1.8中新添加的接口默认方法,返回字符的IntStream。该IntStream支持Lambda表达式,而且该`chars()`方法是在`CharSequence`中添加的,因此所有实现了该接口的类(包括`String`、`StringBuilder`、`StringBuffer`)都可以使用jdk8的函数式编程特性了,通过该方法就可以应用JAVA8的特性了。
    
String str="123456";
IntStream s=str.chars();
s.forEach(ch -> {
      System.out.println(Character.toChars(ch));
});

//亦可
str.chars().mapToObj(ch -> Character.toChars(ch)).forEach(System.out::println);

遍历出来的为每个字符的Unicode代理码位置,但可以通过Character.toChars(ch)方法将Unicode位置码转为对应的字符。

  • public default IntStream codePoints()

    也是JDK1.8 中新加默认方法,用法与`chars()`类似。
    
  • public boolean equals(Object s2)

    这应该是String类型最常见的方法了。该方法的判断逻辑如下:如果传入的s2==this,认为是同一个对象直接返回true;如果是String类型,先比较长度然后比较每个字符,只有在长度和字符都相等时返回true,其他情况返回false。
    
  • public boolean endsWith(String suffix)

    很奇怪吧,内部调用的`public boolean startsWith(String prefix, int toffset)`,通过设置起始匹配位置实现匹配后缀的操作。
    
  • public boolean equalsIgnoreCase(String s2)

    先判断是否为当前对象,然后判断对象s2长度,并通过`public boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len)`方法判断在忽略大小写情况下两个对象是否相等。
    
  • public byte[] getBytes()

    内部调用`StringCoding.encode(value, 0, value.length)`方法实现对字符串编码。
    
  • public byte[] getBytes(Charset charset)

    内部调用`StringCoding.encode(charset, value, 0, value.length)`实现
    
  • public byte[] getBytes(String charsetName)

    内部调用`StringCoding.encode(charsetName, value, 0, value.length)`实现,声明了`UnsupportedEncodingException`异常
    
  • public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)

    内部校验检查后调用`System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin)`将当前字符串的内容复制到指定的字符数组中。
    
  • public int hashCode()

    计算公式为:`s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]`,s为字符数组,n为字符数组长度
    
  • public int indexOf(int ch, int fromIndex)

    从指定formIndex向后查找第一个ch字符出现的位置;
    判断fromIndex小于0,将formIndex设置为0;
    formIndex大于字符串长度,返回-1;
    判断字符code point如果小于最小代理code point,然后从字符串中查找第一个匹配的位置并返回;
    否则调用`indexOfSupplementary(ch, fromIndex)`内部方法通过对高代理和低代理计算进行判断;
    
    高代理和低代理的概念是因为JDK在早期使用UTF-16对字符编码,但因字符数后来超过UTF-16能容纳范围,又兼容16位内存效率高的优点,Unicode引入新的设计方法!具体参考<http://www.ibm.com/developerworks/cn/java/j-unicode/>
    
  • public int indexOf(String str, int fromIndex)

    自定字符串在当前子串的开始位置;内部调用`indexOf(char[] source, int sourceOffset, int sourceCount,char[] target, int targetOffset, int targetCount, int fromIndex) `;主要实现逻辑如下:for循环遍历中先查找target的第一个元素,如果没有找到则一直循环直到找到或结束,当第一个元素在范围内,则for循环匹配其后的元素是否完全匹配,如果完全匹配直接返回位置(index-sourceOffset);否则继续循环找下一个匹配第一个目标元素的位置重复上面的操作。
    
  • public native String intern()

    调用原生方法;返回String对象的标准表示法,会初始化一个空的被String类内部维持的String池,也就是String支持字面量的原因。调用该方法会使得对象去池中查找是否存在与当前对象相等(通过`equal()`)的string,如果存在则返回该string,否则将当前的String对象添加到String对象池中,并返回其引用.
    
    **注意**如果s.equal(t)相等,那么s.intern()和t.intern()也一定相等,因为返回的是String对象池中同一个对象的引用。
    
  • public boolean isEmpty()

    判断内部存储char数组的长度是否为0。
    
  • public static String join(CharSequence delimiter, CharSequence... elements)

    JDK1.8中新添加方法,使用指定分隔符delimiter连接多个字符序列元素。
    内部使用了`StringJoiner`循环调用`add()`方法调用。
    
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
String commaSeparatedNumbers = numbers.stream()
   .map(i -> i.toString())
   .collect(Collectors.joining(", "));
  • public int length()

    内部存储char数组的长度;
    
  • public int lastIndexOf(int ch, int fromIndex)

    如果ch小于最小的增补码位置,则从后向前遍历,直到找到第一个匹配的字符,并返回所在位置。
    否则,调用`lastIndexOfSupplementary(ch, fromIndex)`方法。
    
  • public int lastIndexOf(String str, int fromIndex)

    内部调用`static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount,int fromIndex)`,
    如果fromIndex小于0,直接返回-1;如果fromIndex大于(rightIndex=sourceCount-targetCount),fromIndexw设置为rightIndex;如果targetCount等于0,直接返回fromIndex;
    
    核心:
    预置条件:最后一个字符strLastChar,向前查找的字符限制位置min,开始匹配的字符位置(i=min+fromIndex),
    获取到最后一个字符strLastChar,然后从后向前匹配当前字符是否与strLastChar相等,不相等则一直向前遍历;否则通过内部while循环遍历其他的字符是否匹配,如果不匹配则使用goto语句跳转到外部while从新的位置再次执行查找与strLastChar匹配的字符。重复上面的操作,直到完全匹配返回第一个字符的位置或者没有匹配到返回-1。
    
//核心代码
int strLastIndex = targetOffset + targetCount - 1;
char strLastChar = target[strLastIndex];
int min = sourceOffset + targetCount - 1;
int i = min + fromIndex;

startSearchForLastChar:
      while (true) {
            while (i >= min && source[i] != strLastChar) {
                  i--;
            }
            if (i < min) {
                  return -1;
            }
            int j = i - 1;
            int start = j - (targetCount - 1);
            int k = strLastIndex - 1;

            while (j > start) {
                  if (source[j--] != target[k--]) {
                        i--;
                        continue startSearchForLastChar;
                  }
            }
      return start - sourceOffset + 1;
}
  • public boolean matches(String regex)

    内部调用`Pattern.matches(regex, this)`查找字符串中是否有符合正则表达式的内容。
    
  • public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)

    while循环匹配每个字符,先对原字符进行相等判断,不相等时都转为大写进行判断,还不相等时都转为小写判断,如果还不相等直接返回false,否则遍历other字符串的其他字符。如果所有字符都匹配成功则返回true。
    
  • public String[] split(String regex, int limit)

    内部实现使用的正则表达式。
    
  • public boolean startsWith(String prefix, int toffset)

    从指定位置开始匹配每个字符,只要发现有字符不匹配立即返回false。
    
  • public String substring(int beginIndex, int endIndex)

    如果beginIndex=0,endIndex=value.length,直接返回this,因为字符串不需要截取;否则调用String构造方法创建新的对象。
    
  • public CharSequence subSequence(int beginIndex, int endIndex)

    调用`substring(int beginIndex, int endIndex)`方法。
    
  • public String toLowerCase()

    内部调用`public String toLowerCase(Locale locale)`,通过unicode的code point方式转换,并最终生成新的String对象返回。
    
  • public String toString()

    返回this。
    
  • public String toUpperCase()

    内部调用`public String toUpperCase(Locale locale)`,通过unicode的code point方式转换,并最终生成新的String对象返回。
    
  • public char[] toCharArray()

    调用`System.arraycopy()`生成新的char数组。
    
  • public String trim()

    检查字符串前后各部位空字符的位置,以该两个位置为区间,调用`subString()`创建新的String对象;或者前后没有空字符,直接返回this。
    
  • private int lastIndexOfSupplementary(int ch, int fromIndex)

特别声明

  • 如果通过构造方法或者其他方法通过参数null创建String对象将会报NullPointerException异常。
String str=new String((String)null);

参考

JDk1.8中String类

dd