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
|
目录 一、前置 二、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为参数构造类似,不过不是线程安全的。 (编辑:驾考网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐
