JDK1.8源码学习-String
JDK1.8源码学习-String
目录
一、String简介
String类是Java中最常用的类之一,所有字符串的字面量都是String类的实例,字符串是常量,在定义之后不能被改变。
二、定义
- public final class String implements java.io.Serializable, Comparable<String>, CharSequence{}
1.String类是由final修饰的,表明String类不能被继承,并且String类中的成员方法都默认是final方法。
2.String类是由final修饰的,表明String类一旦创建,就无法被改变,对String对象的任何操作都不会影响到原对象,任何的change操作都会产生新的String对象。
3.java.io.Servializable,标识序列化,Comparable<String>中只有一个compareTo(T o)方法,用于两个实例化对象比较大小,CharSequence表示一个char值的可读序列,CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写序列,而String的值是只读序列。
三、属性
- private final char value[];
- private int hash; // Default to 0
- private static final long serialVersionUID = -6849794470754667710L;
- private static final ObjectStreamField[] serialPersistentFields =
- new ObjectStreamField[0];
1.String的内容本质上是使用不可变的char类型的数组来存储的。
2.hash是String实例化对象的hashcode的一个缓存值,这是因为String对象经常被用来进行比较,如果每次比较都重新计算hashcode值的话,是比较麻烦的,保存一个缓存值能够进行优化。
3.serialVersionUID为序列化ID.
4.serialPersistentFields属性用于指定哪些字段需要被默认序列化,具体用法为:
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("name", String.class),
new ObjectStreamField("age", Integer.Type)
}
transient用于指定哪些字段不会被默认序列化,两者同时使用时,transient会被忽略。
四、构造方法
4.1、无参构造函数
- public String() {
- this.value = "".value;
- }
空字符串" "
4.2、参数为String类型
- public String(String original) {
- this.value = original.value;
- this.hash = original.hash;
- }
其实就是一个克隆的过程,但是String是不可变的,所以没有太多必要。
4.3、参数为char数组
- public String(char value[]) {
- this.value = Arrays.copyOf(value, value.length);
- }
传入一个字符数组,将该数组拷贝一份给value。
4.4、参数为char数组
- 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);
- }
从char数组中的offset位置开始,截取count个字符,拷贝到value。
4.5、参数为bytes数组
- public String(byte bytes[], int offset, int length, String charsetName)
- throws UnsupportedEncodingException {
- if (charsetName == null)
- throw new NullPointerException("charsetName");
- checkBounds(bytes, offset, length);
- this.value = StringCoding.decode(charsetName, bytes, offset, length);
- }
从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到value
五、创建String对象
1.直接使用" ",也就是使用"字面量"赋值
String name = "张三";
2.使用连接符"+"来赋值
String name = "张" + "三";
3.使用关键字new来创建对象
String name = new String("张三");
六、常用方法
6.1、equals方法
- 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;
- }
这里重写了Object中的equals方法,用来判断两个对象实际意义上是否相等
6.2、compareTo方法
- public int compareTo(String anotherString) {
- //自身对象字符串长度len1
- int len1 = value.length;
- //被比较对象字符串长度len2
- int len2 = anotherString.value.length;
- //取两个字符串长度的最小值lim
- int lim = Math.min(len1, len2);
- char v1[] = value;
- char v2[] = anotherString.value;
- int k = 0;
- //从value的第一个字符开始到最小长度lim处为止,如果字符不相等,返回自身(对象不相等处字符-被比较对象不相等字符)
- while (k < lim) {
- char c1 = v1[k];
- char c2 = v2[k];
- if (c1 != c2) {
- return c1 - c2;
- }
- k++;
- }
- //如果前面都相等,则返回(自身长度-被比较对象长度)
- return len1 - len2;
- }
用于比较两个字符串的大小,如果两个字符串长度相等则返回0,如果长度不相等,则返回当前字符串的长度减去被比较的字符串的长度。
6.3、hashCode方法
- public int hashCode() {
- int h = hash;
- //如果hash没有被计算过,并且字符串不为空,则进行hashCode计算
- if (h == 0 && value.length > 0) {
- char val[] = value;
- //计算过程
- //s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
- for (int i = 0; i < value.length; i++) {
- h = 31 * h + val[i];
- }
- //hash赋值
- hash = h;
- }
- return h;
- }
这里重写了hashCode方法,采用多项式进行计算,可以通过不同的字符串得到相同的hash,所以两个String对象的hashCode相同,并不代表两个String是相同的。
算法:假设n = 3
- 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;
- // Note: toffset might be near -1>>>1.
- //如果起始地址小于0或者(起始地址+所比较对象长度)大于自身对象长度,返回假
- if ((toffset < 0) || (toffset > value.length - pc)) {
- return false;
- }
- //从所比较对象的末尾开始比较
- while (--pc >= 0) {
- if (ta[to++] != pa[po++]) {
- return false;
- }
- }
- return true;
- }
- public boolean startsWith(String prefix) {
- return startsWith(prefix, 0);
- }
- public boolean endsWith(String suffix) {
- return startsWith(suffix, value.length - suffix.value.length);
- }
startsWith和endWith方法也是比较常用的方法,常用来判断字符串以特定的字符开始或结尾。
6.5、concat方法
- 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);
- }
concat方法用于将指定的字符串参数连接到字符串上。
6.6、replace方法
- public String replace(char oldChar, char newChar) {
- //新旧值先对比
- if (oldChar != newChar) {
- int len = value.length;
- int i = -1;
- char[] val = value;
- //找到旧值最开始出现的位置
- while (++i < len) {
- if (val[i] == oldChar) {
- break;
- }
- }
- //从那个位置开始,直到末尾,用新值代替出现的旧值
- if (i < len) {
- char buf[] = new char[len];
- for (int j = 0; j < i; j++) {
- buf[j] = val[j];
- }
- while (i < len) {
- char c = val[i];
- buf[i] = (c == oldChar) ? newChar : c;
- i++;
- }
- return new String(buf, true);
- }
- }
- return this;
- }
replace的参数是char和charSequence,即可以支持字符的替换,也支持字符串的替换(charSequence即字符串序列的意思)
replaceAll的参数是regex,即基于规则表达式的替换,比如可以通过replaceAll("\\d","*")把一个字符串所有的数字字符都替换成星号;
相同点:都是全部替换,即把源字符串中的某一字符或者字符串全部替换成指定的字符或者字符串。
不同点:replaceAll支持正则表达式,因此会对参数进行解析(两个参数均是),如replaceAll("\\d","*"),而replace则不会,replace("\\d","*")就是替换"\\d"的字符串,而不会解析为正则。
6.7、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;
- }
trim用于删除字符串的头尾的空格。
JDK1.8源码学习-String的更多相关文章
- JDK1.8源码学习-Object
JDK1.8源码学习-Object 目录 一.方法简介 1.一个本地方法,主要作用是将本地方法注册到虚拟机中. private static native void registerNatives() ...
- JDK1.8源码学习-HashMap
JDK1.8源码学习-HashMap 目录 一.HashMap简介 HashMap 主要用来存放键值对,它是基于哈希表的Map接口实现的,是常用的Java集合之一. 我们都知道在JDK1.8 之前 的 ...
- JDK源码学习--String篇(二) 关于String采用final修饰的思考
JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...
- JDK1.8源码学习-LinkedList
JDK1.8源码学习-LinkedList 目录 一.LinkedList简介 LinkedList是一个继承于AbstractSequentialList的双向链表,是可以在任意位置进行插入和移除操 ...
- JDK1.8源码学习-ArrayList
JDK1.8源码学习-ArrayList 目录 一.ArrayList简介 为了弥补普通数组无法自动扩容的不足,Java提供了集合类,其中ArrayList对数组进行了封装,使其可以自动的扩容或缩小长 ...
- Java JDK1.8源码学习之路 2 String
写在最前 String 作为我们最常使用的一个Java类,注意,它是一个引用类型,不是基本类型,并且是一个不可变对象,一旦定义 不再改变 经常会定义一段代码: String temp = " ...
- 源码学习-String类
最近在扫描CodeDex时报了一个不能使用String.intern()的字符串来做锁对象的告警,对这个问题有疑问查了些资料,顺便学习一下String类的源码. 1.类定义 String 被final ...
- Java JDK1.8源码学习之路 1 Object
写在最前 对于一个合格的后端程序员来说,现行的流行框架早已经能胜任基本的企业开发,Springboot 任何的框架都把重复的工作更佳简单/优化的解决掉,但是完全陷入在这样的温水里面, 好比温水煮青蛙, ...
- JDK源码学习--String篇(四) 终结篇
StringBuilder和StringBuffer 前面讲到String是不可变的,如果需要可变的字符串将如何使用和操作呢?JAVA提供了连个操作可变字符串的类,StringBuilder和Stri ...
随机推荐
- .Net Core 项目开发中的Errors,Exceptions
这个错误是在连接数据库的时候,没有找到对应的表, namespace TodoApi.Models { public class TodoContext : DbContext { public To ...
- websocket推送进度条百分比给前台
说明:后台springboot项目 前台vue+element-UI 直接放代码: //别忘了开启springboot的websocket <dependency> <groupId ...
- js获取json对象的属性值
// var responseData = response.data.result; for (var r in responseData) { ...
- PHP入门之流程控制
前言 上一篇文章对PHP的一些类型和运算符进行了简单的讲解.PHP入门之类型与运算符 这篇简单讲解一下流程控制.结尾有实例,实例内容是用switch分支和for循环分别做一个计算器和金字塔. 分支控制 ...
- 【IDEA】HTML通过servlet3.0注解名提交表单到servlet类找不到页面的问题
IDEA一时爽,摸不着头的BUG火葬场 这个问题困扰我整整一天一夜,先是代码检查路径设置找不出问题,后面换tomcat版不行,抱着侥幸心理换IDEA版本意料之中还是没解决问题. 都快想秃了最后终于完美 ...
- webserver 返回json 如何去掉 <string xmlns="http://tempuri.org/">
[WebMethod] public void GetJson(String sJson) { Context.Response.Charset = "UTF-8"; / ...
- ELK5.6.4+Redis+Filebeat+Nginx(CentOS7.4)
下载地址: Elasticsearhc: https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.4.tar.gz ...
- logrotate nginx日志切割
1.安装 centos: yum -y install logrotate ubuntu: apt-get install -y logrotate 2. 配置文件 /etc/logrotate.co ...
- Python编程语言简介
Python诞生于20世纪90年代初,由荷兰人吉多·范罗苏姆发明.那么,Python这一门编程语言是如何发明的呢?这之中又有怎么的故事呢?请看下面. 故事发生在1989年的圣诞节上,吉多先生为了打发无 ...
- Pintech品致全新多功能MDO 704E系列示波器全新推出
2020年 7月,Pintech品致全新推出推出首款具有多个模拟通道和多个数字通道的示波器.每个模拟通道带宽为200 MHz,每个模拟通道采样率同时达1 GSa/s,在一台仪器中,实现精确.可重复的. ...