jdk源码阅读笔记-String
本人自学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的更多相关文章
- jdk源码阅读笔记-LinkedHashMap
Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...
- jdk源码阅读笔记-ArrayList
一.ArrayList概述 首先我们来说一下ArrayList是什么?它解决了什么问题?ArrayList其实是一个数组,但是有区别于一般的数组,它是一个可以动态改变大小的动态数组.ArrayList ...
- jdk源码阅读笔记-HashSet
通过阅读源码发现,HashSet底层的实现源码其实就是调用HashMap的方法实现的,所以如果你阅读过HashMap或对HashMap比较熟悉的话,那么阅读HashSet就很轻松,也很容易理解了.我之 ...
- JDK源码学习笔记——String
1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() { ...
- jdk源码阅读笔记
1.环境搭建 http://www.komorebishao.com/2020/idea-java-jdk-funyard/ 2. 好的源码阅读资源 https://zhuanlan.zhihu.co ...
- jdk源码阅读笔记-Integer
public final class Integer extends Number implements Comparable<Integer> Integer 由final修饰了,所以该 ...
- jdk源码阅读笔记-HashMap
文章出处:[noblogs-it技术博客网站]的博客:jdk1.8源码分析 在Java语言中使用的最多的数据结构大概右两种,第一种是数组,比如Array,ArrayList,第二种链表,比如Array ...
- jdk源码阅读笔记-LinkedList
一.LinkedList概述 LinkedList的底层数据结构为双向链表结构,与ArrayList相同的是LinkedList也可以存储相同或null的元素.相对于ArrayList来说,Linke ...
- jdk源码阅读笔记-AbstractStringBuilder
AbstractStringBuilder 在java.lang 包中,是一个抽象类,实现 Appendable 接口和 CharSequence 接口,这个类的诞生是为了解决 String 类在创建 ...
随机推荐
- AJAX的get和post请求原生编写方法
var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ if(xhr.readyState===4){ if(xhr.stat ...
- Coursera-AndrewNg(吴恩达)机器学习笔记——第一周
一.初识机器学习 何为机器学习?A computer program is said to learn from experience E with respect to some task T an ...
- scrapy安装过程问题解决、新建项目、调试断点
一.安装问题 1. 下载速度太慢 使用国外源,下载速度很慢,可以考虑使用豆瓣的镜像下载 pip install -i https://pypi.douban.com/simple/ scrapy 2. ...
- tomcat启动报错:Address already in use: JVM_Bind
tomcat启动时出现Address already in use: JVM_Bind 的原因是因为端口被占用,有可能是因为多次启动tomcat或者启动了多个tomcat,或者是其他应用程序或者服务占 ...
- 创建servlet的三种方式
第一种方式,实现Servlet接口 package com.example.servlet; import java.io.IOException; import javax.servlet.Serv ...
- 网络营销行业十大看了就想吐的“滥词”
网络营销行业在国内的互联网界已"猖獗"数年之久,它是一个让企业爱让用户恨的行业.有互联网的地方,就有网络营销的机会,有了机会就有了相关产业的存在,只不过是业大业小的问题.但是随着互 ...
- MySql中innodb存储引擎事务日志详解
分析下MySql中innodb存储引擎是如何通过日志来实现事务的? Mysql会最大程度的使用缓存机制来提高数据库的访问效率,但是万一数据库发生断电,因为缓存的数据没有写入磁盘,导致缓存在内存中的数据 ...
- 消息中间件activemq的使用场景介绍(结合springboot的示例)
一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使 ...
- float之脱离文档流
所谓的文档流:指的是元素在排版过程中,元素自动从左到右,从上到下的顺序排列. 脱离文档流:也就是将元素从普通的布局排版中拿走,其他盒子在定位的时候,会当做脱离文档流的元素不存在而进行定位 只有绝对定位 ...
- [ Java面试题 ]WEB篇
1.AJAX有哪些有点和缺点? 优点: 1.最大的一点是页面无刷新,用户的体验非常好. 2.使用异步方式与服务器通信,具有更加迅速的响应能力. 3.可以把以前一些服务器负担的工作转嫁到客户端,利用客户 ...