文章目录
- String类的定义
- 成员变量
- 构造方法
- String()
- String(String original)
- String(char value[])
- String(char[] value,boolean share)
- String(char value[], int offset, int count)
- String(int[] codePoints, int offset, int count)
- String(byte bytes[])
- String(byte[] bytes, int offset, int length)
- String(byte[] bytes, int offset, int length, Charset charset)
- String(byte bytes[], int offset, int length, String charsetName)
- String(byte bytes[], String charsetName)
- 方法
- int compareTo(String anotherString)
- int compareToIgnoreCase(String str)
- int compare(String s1, String s2)
- String concat(String str)
- void checkBounds(byte[] bytes, int offset, int length) 【私有】
- String intern()
- char charAt(int index)
- void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
- byte[] getBytes()
- byte[] getBytes(Charset charset)
- boolean equals(Object anObject)
- boolean equalsIgnoreCase(String anotherString)
- boolean startsWith(String prefix)
- boolean matches(String regex)
- boolean regionMatches(int toffset,String other, int ooffset, int len)
- boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len)
- String replace(char oldChar, char newChar)
- String replaceAll(String regex, String replacement)
- String replaceFirst(String regex,String replacement)
- String[] split(String regex)
- String[] split(String regex, int limit)
- String.join()
- String trim()
- String toUpperCase()&& String toLowerCase()
- String toString()
- public char[] toCharArray()
- valueOf -- 常见数据类型转String
- format
- CharSequence subSequence(int beginIndex, int endIndex)
- String substring(int beginIndex)
- String substring(int beginIndex, int endIndex)
- indexof
String不是基本数据类型,而是一个对象。因为对象的默认值是null,所以String的默认值也是null
String类的定义
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
final — 不可继承&&不可变
- String是由final修饰的,表示String是不可以继承的。
为什么String要设计成为不可变的???
Serializable
这个序列化接口没有任何方法和域,仅用于标识序列化的语意。
String实现了序列化的接口Serializable,也就是说String是支持序列化和反序列化的。
那什么是Java对象的序列化呢? 深入分析Java的序列化与反序列化说:
Java平台允许我们在内存中创建可复用的Java对象,但是一般情况下,只有当jvm处于运行时,这些对象才可能存在,也就是说,这些对象的生命周期不会比JVM的生命周期长。但是在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象的序列化就能够帮助我们实现该功能
使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。
必须注意,对象序列化保存的是对象的"状态",也就是它的成员变量。由此可知,对象序列化不会关注类中的静态变量。
除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或者这网络中传递对象时,都会用到对象序列化
Java序列化API为处理对象序列化提供了一个标准机制,该API简单易用
在String源码中,我们也可以看到支持序列化的类成员定义:
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
Comparable
这个接口只有一个compareTo(T 0)接口,用于对两个实例化对象比较大小。
CharSequence
这个接口是一个只读的字符序列。包括length(), charAt(int index), subSequence(int start, int end)这几个API接口,值得一提的是,StringBuffer和StringBuild也是实现了改接口。
成员变量
/** The value is used for character storage. */
private final char value[];
这是一个final修饰的字符数组,用于存储字符串内容,从final可以知道,String的内容一旦被初始化了就不能被更改了
/** Cache the hash code for the string */
private int hash; // Default to 0
- hash是String实例化时hashCode的一个缓存。因为String经常用于比较,比如在HashMap中,如果每次进行比较都重新计算hashcode的值的话,很麻烦,而保存一个hashCode的缓存无疑能够优化这样的操作
- String设置使用了享元模式【可以简单的理解位缓存,这是设计中的一种优化策略】,引入了"常量池"
- 常量池指的是在编译期间确定,并且保存在已编译的.class文件中的一些数据。它包括了类、方法、接口等中的常量,以及字符串常量
在String源码中,我们也可以看到支持序列化的类成员定义
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
Java的序列化机制是通过在运行时判断类的serialVersion来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID
与本地相应实体类(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。
serialPersistentFields
[不懂]这个定义则比上一个少见许多,大致猜到是与序列化时的类成员有关系: 默认序列化自定义包括关键字transient
和静态字段名serialPersistenFields
,transient
用于定义哪个字段不被默认序列化,serialPersistenFields
用于指定哪些字段需要被默认序列化。如果两者同时定义了就忽略transient
构造方法
String()
public String() {
this.value = "".value;
}
先看下下面这段代码
Class clazz = "".getClass();
System.out.println(""+"的class是:"+clazz);
Object o = clazz.newInstance();
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
Object o2 = declaredConstructor.newInstance("123123");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field: declaredFields){
System.out.println("我是由"+""+"声明的对象我有字段:"+field);
}
Field value = clazz.getDeclaredField("value");
value.setAccessible(true);
Object o1 = value.get(o2);
System.out.println(o1 instanceof char[]);
char[] o11 = (char[]) o1;
System.out.println((o11));
value.setAccessible(true);
以上代码其实就是java的反射机制,后来才知道,这个位置其实通过反射机制实现的,可以理解为:
“”.getClass.newInstance() ---->Field.get(“value”) 来拿到field="value"的值。
String(String original)
将字符串常量转为字符串:
String s6 = new String("java");
源码:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
- 在堆中创建一个String类型的结构,这个结构的value和hash和原始的orginal一模一样。也就是是orginal的副本
- 因为是副本,所以不用担心改变源String会影响目标String的值
- 用new String()创建的字符串不是常量,不能再编译器就确定。所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。
除非需要参数字符串的显式拷贝,否则不需要使用这个构造函数。
String(char value[])
将字符数组转为字符串
例子:
char[] arr3 = {'a','b','c'};
String s4 = new String(arr3);
System.out.println(s4);//abc
源码:
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
- 参数是一个char数组,【传递的是地址】
- 原本堆空间的String结构的value指向的是null,现在会重新创建一个char[],将原来的字符串数组中的内容一一复制到char[]中,然后令String.value = char[]
String(char[] value,boolean share)
String(char[] value,boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
和String(char[] value)
方法对比:
- 该方法多了一个参数
boolean share
,这个参数在方法体中并没有使用。那为什么还要这个参数呢?加上这个参数只是为了区分String(char[] value)
方法,只有参数不同才能进行重载 - 具体实现方法不同:
String(char[] value)
方法在创建String的时候是使用Arrays.copy
方法将value的内容全部复制到String中,而这个String(char[] value,boolean share)
方法是直接将value的引用赋值给String中的value。也就是说,这个方法构造出来的String和参数中的char[] value共享同一个数组。
为什么Java会提供这样一个方法呢?
- 性能好: 一个是直接赋值,一个一一拷贝,赋值更快
- **节约内存:**该方法之所以设置为 protected,是因为一旦该方法设置为公有,在外面可以访问的话,如果构造方法没有对 arr 进行拷贝,那么其他人就可以在字符串外部修改该数组,由于它们引用的是同一个数组,因此对 arr 的修改就相当于修改了字符串,那就破坏了字符串的不可变性。
- 安全的:对于调用他的方法来说,由于无论是原字符串还是新字符串,其 value 数组本身都是 String 对象的私有属性,从外部是无法访问的,因此对两个字符串来说都很安全。
String(char value[], int offset, int count)
将字符数组的一部分转为字符串
怎么使用
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
char[] a={'a','d','f','r','d','e'};
String s = new String(a,2,4);
System.out.println(s); // frde
}
参数:
- value:作为字符源的数组
- offset:char[]数组要复制的起始坐标
- count:要从char[]数组复制多少个字符到目标数组中
分析:
- 必须先检查参数是否正确。
- char[] 为null,或者offset起始偏移量小于0,返回并抛出异常
- 如果要复制的长度count小于0,抛出异常
- 如果偏移量offset 大于等于value.length,抛出超出索引长度异常
- 如果偏移量offset + count 大于等于 value.length,抛出超出索引长度异常
- 如果count等于0,那么只要偏移量不超过数组索引长度,就直接让
this.value = "".value
就可以了
- 如果参数正确,那么调用数组的赋值长度就可以了
// value[]:作为字符源的数组,offset:偏移量、下标从0开始并且包括offset,count:从数组中取到的元素的个数。
public String(char value[], int offset, int count) {
// 如果偏移量小于0抛出IndexOutOfBoundsException异常
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
// 判断要取的元素的个数是否小于等于0
if (count <= 0) {
// 要取的元素的个数小于0,抛出IndexOutOfBoundsException异常
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// 在要取的元素的个数等于0的情况下,判断偏移量是否小于等于数组的长度
if (offset <= value.length) {
// 偏移量小于等于数组的长度,返回一个空字符串数组的形式
this.value = "".value;
return;
}
}
// 如果偏移量的值大于数组的长度减去取元素的个数抛出IndexOutOfBoundsException异常
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
// 复制元素
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
以上介绍的构建String对象的方式中,基本都是属于操作它内部的字符数组来实现的
总结:当我们使用字符数组char []
创建String的时候,会用到Arrays.copyOf
方法或者Arrays.copyOfRange
方法,这两个方法是将原有字符数组中的内容一一复制到String中的字符数组中,会创建一个新的字符串对象,随后修改的字符数组不会影响新创建的字符串
String类和编码转换
String(int[] codePoints, int offset, int count)
分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。
例子1
int[] ints = {91,81,97,99,98};
String str = new String(ints,2,3);
System.out.println(str);
返回:
acb
例子2
int[] a = {100, 2312, 12313, 54545, 23432, 22, 65, 78, 99};
String b = new String(a, 0, a.length);
System.out.println(b);
dई〙픑守ANc
String(byte bytes[])
前面我们已经知道,String底层是一个char[]字符数组,char[]字符数组是以Unicode码来存储的。
byte是网络传输或者存储的序列化形式,所以在很多传输和存储的过程中需将将byte[]数组和String进行相互转换,而byte[]数组和String相互转换就要小心编码问题。
我们在使用 byte[] 构造 String 的时候,如果没有指明解码使用的字符集的话,那么 StringCoding 的 decode 方法首先调用系统的默认编码格式,如果没有指定编码格式则默认使用 ISO-8859-1 编码格式进行编码操作。
作用:
- 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
public String(byte bytes[]) {
this(bytes, 0, bytes.length);
}
实际调用的是String(byte[] bytes, int offset, int length)
String(byte[] bytes, int offset, int length)
作用:
- 通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。
例子
byte[] arr2 = {97,98,99,100,101};
String s3 = new String(arr2,2,3);
System.out.println(s3);//cde
源码:
public String(byte bytes[], int offset, int length) {
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(bytes, offset, length);
}
String(byte[] bytes, int offset, int length, Charset charset)
作用:
- 通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。
String(byte bytes[], int offset, int length, String charsetName)
https://www.cnblogs.com/xuejianbest/p/10285298.html
从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到value
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);
}
String(byte bytes[], String charsetName)
调用public String(byte bytes[], int offset, int length, String charsetName)构造函数
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
this(bytes, 0, bytes.length, charsetName);
}
方法
int compareTo(String anotherString)
int compareToIgnoreCase(String str)
作用:
- 按字典顺序比较两个字符串。
示例:
String str1 = "Strings";
String str2 = "Strings";
String str3 = "Strings123";
int result = str1.compareToIgnoreCase( str2 );
System.out.println(result);
result = str2.compareTo( str3 );
System.out.println(result);
result = str3.compareTo( str1 );
System.out.println(result);
源码:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
int compare(String s1, String s2)
public int compare(String s1, String s2) {
// 获取s1的长度
int n1 = s1.length();
//获取s2的长度
int n2 = s2.length();
// 得到较少的长度
int min = Math.min(n1, n2);
// 从0开始读取循环比较,比较范围 [0, min)
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
// 如果相等就进入下一轮循环,如果不相等
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
String concat(String str)
拼接字符串,基本上不用了的
public String concat(String str) {
// 获取带拼接的字符串长度
int otherLen = str.length();
// 如果带拼接的字符串长度为0就直接返回当前字符串
if (otherLen == 0) {
return this;
}
// 获取当前字符串的长度
int len = value.length;
// 构建一个新的长度为len+otherlen的字符数组,并将当前字符串拷贝到这个字符中
char buf[] = Arrays.copyOf(value, len + otherLen);
// str.getChars底层是System.arraycopy,也就是将str拷贝到buf【偏移量为当前字符串长度】
str.getChars(buf, len);
// 构建一个新的String:buf的引用给新String
return new String(buf, true);
}
示例:
"aaaa".concat("w3xue")
void checkBounds(byte[] bytes, int offset, int length) 【私有】
一个简单的边界检查方法
private static void checkBounds(byte[] bytes, int offset, int length) {
if (length < 0)
throw new StringIndexOutOfBoundsException(length);
if (offset < 0)
throw new StringIndexOutOfBoundsException(offset);
if (offset > bytes.length - length)
throw new StringIndexOutOfBoundsException(offset + length);
}
String intern()
public native String intern();
存在于.class文件中的常量池,在运行期间被JVM装载,并且可以扩充。String中的intern方法就是扩充常量池的一个方法。当一个String实例str调用intern方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有就返回其引用,如果没有,则在常量池中增加一个Unicode等于Str的字符串并返回它的引用
navtive关键字说明修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言比如C/Cpp之类实现的文件中。
Java语言本身不能对OS底层进行访问好操作,但是可以通过JNI接口调用其他语言来实现对底层的访问
JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。
public static void main(String[] args) {
String a0 = "Lering string";
String a1 = new String("Lering string");
String a2 = new String("Lering string");
System.out.println(a0 == a1); // false
a1.intern(); // 这里没有作用,因为它的返回值没有赋给a1
a2 = a2.intern();
System.out.println(a0 == a1); //false
System.out.println(a0 == a1.intern()); //true
System.out.println(a0 == a2); //true
}
char charAt(int index)
charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。
参数:
- index— 字符的索引
返回值:
- 返回指定索引处的字符
示例:
public static void main(String[] args) {
String s = new String("www.w3xue.com");
char result = s.charAt(2);
System.out.println(result);
}
结果:
w
源码:
public char charAt(int index) {
// 先检查索引是否越界
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
//然后直接使用索引访问char[]数组
return value[index];
}
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
将字符串中的字符复制到目标字符数组中。索引包含srcBegin,不包含srcEnd。
参数:
- srcBegin:字符串中要复制的第一个字符的索引
- srcEnd: 字符串中要复制的最后一个字符之后的索引
- dst:目标数组
dstBegin – 目标数组中的起始偏移量。
返回值
- 没有返回值,但会抛出 IndexOutOfBoundsException 异常。
示例:
String Str1 = new String("www.w3xue.com");
char[] Str2 = new char[6];
try {
Str1.getChars(4, 9, Str2, 0);
System.out.print("拷贝的字符串为:" );
System.out.println(Str2 );
} catch( Exception ex) {
System.out.println("触发异常...");
}
以上程序执行结果为:
拷贝的字符串为:W3xue
源码:
- 先检查参数是否正确
- 然后使用 System.arraycopy复制数组一部分到目标数组中
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
// 起始索引小于0抛出异常
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);
}
ps:
* @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. 复制的长度
public static native void arraycopy(Object src,int srcPos,Object dest, int destPos,int length);
byte[] getBytes()
使用平台默认的字符集将字符串编码为byte序列,并将结果存储到一个新的byte数组中
返回值:
- 返回byte数组
例子:
public static void main(String[] args) {
String s = new String("abcdefg");
byte[] str = s.getBytes();
System.out.println(str + ":");
for (int i = 0 ; i < str.length; i++){
System.out.print(str[i] + "\t");
}
}
程序结果:
[B@1540e19d:
97 98 99 100 101 102 103
源码:
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
byte[] getBytes(Charset charset)
根据指定字符集将String编码为byte序列,并用byte数组存储起来
参数:
- charsetName – 支持的字符集名称。
返回值:
- 返回 byte 数组。
示例:
public static void main(String[] args) {
String s = new String("abcdefg");
try {
byte[] str = s.getBytes("UTF-8" );
System.out.print(str + ":");
for (int i = 0 ; i < str.length; i++){
System.out.print(str[i] + "\t");
}
System.out.println();
byte[] str1 = s.getBytes("ISO-8859-1");
System.out.print(str1 + ":");
for (int i = 0 ; i < str1.length; i++){
System.out.print(str[i] + "\t");
}
} catch (UnsupportedEncodingException e) {
System.out.println("不支持的字符集");
}
}
源码:
public byte[] getBytes(Charset charset) {
if (charset == null) throw new NullPointerException();
return StringCoding.encode(charset, value, 0, value.length);
}
boolean equals(Object anObject)
将字符串与指定的对象比较,只要两者的内容相等会可以了
这个方法重写了Object中的equals方法。方法中的将此字符串与指定对象进行比较
参数:
- anObject – 与字符串进行比较的对象。
返回值:
- bool
示例:
public static void main(String args[]) {
String Str1 = new String("W3xue");
String Str2 = Str1;
String Str3 = new String("W3xue");
String Str4 = "W3xue";
boolean retVal;
retVal = Str1.equals( Str2 );
System.out.println("返回值 = " + retVal );
retVal = Str1.equals( Str3 );
System.out.println("返回值 = " + retVal );
retVal = Str1.equals( Str4 );
System.out.println("返回值 = " + retVal );
}
返回值:
返回值 = true
返回值 = true
返回值 = true
源码:
public boolean equals(Object anObject) {
// 如果引用的是同一个对象,返回true
if (this == anObject) { //比较地址
return true;
}
// 判断给定的对象是否是String类型的
if (anObject instanceof String) {
// 将anObject转为String类型anotherString ,如果anotherString的底层数组长度不相等,返回false
String anotherString = (String)anObject;
// 获取当前字符串的长度
int n = value.length;
// 判断给定字符串的长度是否等于当前字符串的长度
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;
}
boolean equalsIgnoreCase(String anotherString)
public boolean equalsIgnoreCase(String anotherString) {
// 引用相同返回true。引用不相同进行长度、各个位置上的char是否相同
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
boolean startsWith(String prefix)
作用:
- 字符串是否以指定的前缀开始
参数:
- prefix: 前缀
- toffset:字符串中开始查找的位置
返回值:
- 如果字符串以指定的前缀开始,则返回 true;否则返回 false
例子:
public void test_start(){
String Str1 = new String("www.w3xue.com");
System.out.print("返回值 :" );
System.out.println(Str1.startsWith("www")); //true
System.out.print("返回值 :" );
System.out.println(Str1.startsWith("w3xue", 4)); //true
System.out.print("返回值 :" );
System.out.println(Str1.startsWith("w3xue")); //false
}
源码:
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
public boolean endsWith(String suffix) {
// 偏移量: 当前字符串长度 - 后缀字符串长度
return startsWith(suffix, value.length - suffix.value.length);
}
public boolean startsWith(String prefix, int toffset) {
// 同一个引用
char ta[] = value;
// 当前字符偏移量
int to = toffset;
// 与前缀字符串同一个内存
char pa[] = prefix.value;
// 0起始
int po = 0;
// 前缀字符串长度
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
// 如果 当前字符偏移量小于0【因为数组索引不可能小于0】 或者当前字符偏移量+前缀字符串长度大于当前字符长度,那么就超过索引范围了,因此返回false
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
// 循环整个前缀字符串长度
while (--pc >= 0) {
// 判断当前字符【从偏移量开始】是否和前缀字符串相等
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
boolean matches(String regex)
作用:
- matches() 方法用于检测字符串是否匹配给定的正则表达式。
调用此方法的 str.matches(regex) 形式与以下表达式产生的结果完全相同:
Pattern.matches(regex, str)
参数:
- regex – 匹配字符串的正则表达式。
返回值:
- 在字符串匹配给定的正则表达式时,返回 true。
boolean regionMatches(int toffset,String other, int ooffset, int len)
boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len)
作用:
- 用于检测两个字符串在一个区域内是否相等
参数:
- ignoreCase – 如果为 true,则比较字符时忽略大小写。
- toffset:此字符串中子区域的起始偏移量
- other:字符串参数
- ooffset – 字符串参数中子区域的起始偏移量。
- len – 要比较的字符数。
返回值:
- 如果字符串的指定子区域匹配字符串参数的指定子区域,则返回 true;否则返回 false。是否完全匹配或考虑大小写取决于 ignoreCase 参数。
示例:
public void test_start(){
String Str1 = new String("www.w3xue.com");
String Str2 = new String("W3xue");
String Str3 = new String("w3xue");
System.out.print("返回值 :" );
System.out.println(Str1.regionMatches(4, Str2, 0, 5)); //false
System.out.print("返回值 :" );
System.out.println(Str1.regionMatches(4, Str3, 0, 5)); //true
System.out.print("返回值 :" );
System.out.println(Str1.regionMatches(true, 4, Str3, 0, 5)); //true
}
- 源码:
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
// 和value共享一块内存
char ta[] = value;
int to = toffset;
// 和要比较的str共享一颗内存
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
// 如果偏移量小于0或者偏移量+要比较的长度小于字符串长度,就直接返回false
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
// 不短读取两个字符串
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
// 如果相等就进入下次循环
if (c1 == c2) {
continue;
}
// 如果不相等,如果不忽略大小就直接返回false,否则:
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
// 分别江这个两个字符转换为大小和小写两种形式比较
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
String replace(char oldChar, char newChar)
作用:
- 用newChar字符替换字符串中出现过的所有oldChar字符,并返回提缓缓后的新字符
参数:
- oldChar:原则来的字符
- newChar:新字符
返回值:
- 替换之后生成的新字符串
示例:
public void test_start(){
String Str = "HHaabbc";
System.out.print("返回值 :" );
System.out.println(Str.replace('H', 'T'));
System.out.print("原始字符串 :" );
System.out.println(Str);
}
String replaceAll(String regex, String replacement)
String replaceFirst(String regex,String replacement)
- 使用给定的参数 replacement 替换字符串第一个匹配给定的正则表达式的子字符串
参数:
- regex – 匹配此字符串的正则表达式。
- replacement – 用来替换第一个匹配项的字符串。
返回值:
- 成功则返回替换的字符串,失败则返回原始字符串。
String Str1 = new String("ww w3 xue w3 3");
System.out.print("返回值 :" );
System.out.println(Str1.replaceFirst("w3", "abab")); //:ww abab xue w3 3
System.out.print("原始值 :" );
System.out.println(Str1); //ww w3 xue w3 3
System.out.print("返回值 :" );
System.out.println(Str1.replaceAll("w3", "abab")); //ww abab xue abab 3
System.out.print("原始值 :" );
System.out.println(Str1); //ww w3 xue w3 3
String[] split(String regex)
String[] split(String regex, int limit)
作用:
- 根据匹配给定的正则表达式来拆分字符串
参数:
- regex: 正则表达式分隔符
- limit: 分割的份数
返回值:
- 成功返回替换的字符串,失败返回原始字符串
示例:
public void test_start(){
String Str = "www-to-aa-cc-vfvvd.com";
System.out.println("返回值:");
for (String s : Str.split("-", 2)){
System.out.println(s);
}
System.out.println("");
System.out.println("返回值 :" );
for (String retval: Str.split("-", 3)){
System.out.println(retval);
}
System.out.println("");
System.out.println("返回值 :" );
for (String retval: Str.split("-", 0)){
System.out.println(retval);
}
System.out.println("");
System.out.println("返回值 :" );
for (String retval: Str.split("-")){
System.out.println(retval);
}
}
public String[] split(String regex) {
return split(regex, 0);
}
public String[] split(String regex, int limit) {
*****
}
返回值:
返回值:
www
to-aa-cc-vfvvd.com
返回值 :
www
to
aa-cc-vfvvd.com
返回值 :
www
to
aa
cc
vfvvd.com
返回值 :
www
to
aa
cc
vfvvd.com
String.join()
作用:拼接字符串
例子:
- joiner.add
public void test_start(){
StringJoiner joiner = new StringJoiner(",");
joiner.add("foo");
joiner.add("bar");
joiner.add("baz");
String joinered = joiner.toString();
System.out.println(joinered); //foo,bar,baz
}
String joinered = new StringJoiner("-").add("fff").add("aaa").add("ccc").toString();
System.out.println(joinered); //foo,bar,baz
String j1 = String.join("/", "2020", "10", "28");
System.out.println(j1);
List<String> list = Arrays.asList("foo", "bar", "baz");
joinered = String.join(";", list); //2020/10/28
System.out.println(joinered); //foo;bar;baz
List<Person> list = Arrays.asList(
new Person("John", "Smith"),
new Person("Anna", "Martinez"),
new Person("Paul", "Watson ")
);
String joinedFirstNames = list.stream()
.map(Person::getFirstName)
.collect(Collectors.joining(", ")); // "John, Anna, Paul"
源码:
# 数组方式
public static String join(CharSequence delimiter, CharSequence... elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
// Number of elements not likely worth Arrays.stream overhead.
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
// 集合方式:
public static String join(CharSequence delimiter,
Iterable<? extends CharSequence> elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
// 拼接方法
public StringJoiner add(CharSequence newElement) {
// prepareBuilder()方法首次调用会创建StringBuilder对象,后面再调用会执行拼接分隔符
prepareBuilder().append(newElement);
return this;
}
// 未进行拼接创建StringBuilder对象,已经拼接以后value != null执行拼接分隔符
private StringBuilder prepareBuilder() {
// 判断拼接的value是否为空
if (value != null) {
// 不为空执行拼接分隔符
value.append(delimiter);
} else {
// 最开始使用拼接的时候,调用这个方法创建一个空的StringBuilder对象,只调一次
value = new StringBuilder().append(prefix);
}
return value;
}
// 上面是调用的这个拼接元素方法
@Override
public StringBuilder append(CharSequence s) {
// 这里啥都没处理,调用的是父类的append方法,设计模式为建造者模式
super.append(s);
return this;
}
// 上面的prepareBuilder方法是拼接分隔符,这个方法是将分隔符和给定的元素拼接的方法
@Override
public AbstractStringBuilder append(CharSequence s) {
// 以下3个判断根据类型和是否为空进行区别拼接
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
// 拼接
return this.append(s, 0, s.length());
}
String trim()
作用:
-
删除字符串的头尾空白符
-
参数:
-
无
返回值:
-
删除头尾空白符的字符串
-
例子:
public static void main(String args[]) {
String Str = new String(" acdsa ");
System.out.print("删除头尾空白 :" );
System.out.println( Str.trim() );
System.out.print("原始值 :" );
System.out.println( Str );
}
源码:
public String trim() {
// 获取当前底层数组长度
int len = value.length;
int st = 0;
// char[]val是原来底层数组的引用,他们指向同一个内存
char[] val = value; /* avoid getfield opcode */
//找到字符串前段没有空格的位置
while ((st < len) && (val[st] <= ' ')) {
st++;
}
// 找到字符串末尾没有空格的位置
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
// 如果前后都没有出现空格,返回字符串本身,否则调用substring
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
String toUpperCase()&& String toLowerCase()
这个源码没有看懂
public void test_start(){
String Str = new String(" acdsa ");
System.out.print("返回值 :" );
System.out.println( Str.toUpperCase() );
System.out.print("原始值 :" );
System.out.println( Str );
}
返回值 : ACDSA
原始值 : acdsa
String toString()
返回当前对象
public String toString() {
return this;
}
例子:
public void test_start(){
String Str = new String(" acdsa ");
System.out.print("返回值 :" );
System.out.println( Str.toString() );
System.out.print("原始值 :" );
System.out.println( Str );
System.out.println( Str == Str.toString() );
}
返回值 : acdsa
原始值 : acdsa
true
public char[] toCharArray()
将字符串转换为字符数组
参数:
- 无
返回值:
- 字符数组
示例:
public static void main(String args[]) {
String Str = new String("www.aaaa.com");
System.out.print("返回值 :" ); // 返回值 :www.aaaa.com
System.out.println( Str.toCharArray() );
}
源码:
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
// 构造一个value.length长度的字符数组
char result[] = new char[value.length];
// 苑
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
valueOf – 常见数据类型转String
valueOf(boolean b): 返回 boolean 参数的字符串表示形式。.
public static String valueOf(boolean b) {
return b ? "true" : "false";
}
valueOf(char c): 返回 char 参数的字符串表示形式。
public static String valueOf(char c) {
char data[] = {c};
return new String(data, true);
}
valueOf(char[] data): 返回 char 数组参数的字符串表示形式。
copyValueOf(char data[]):返回 char 数组参数的字符串表示形式。
public static String valueOf(char data[]) {
return new String(data);
}
public static String copyValueOf(char data[]) {
return new String(data);
}
valueOf(char[] data, int offset, int count): 返回 char 数组参数的特定子数组的字符串表示形式。
- data – 这是字符数组。
- offset – 这是最初的子数组的偏移量。
- count – 这是子数组的长度
public static String valueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
public static String copyValueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
public static void main(String[] args) {
// character array
char[] charArr = { 'C', 'O', 'M', 'P', 'I', 'L', 'E', ' ',
'O', 'N', 'L', 'I', 'N', 'E' };
String str = String.copyValueOf(charArr, 8, 6 );
System.out.println(str);
}/
valueOf(double d): 返回 double 参数的字符串表示形式。
public static String valueOf(double d) {
return Double.toString(d);
}
valueOf(float f): 返回 float 参数的字符串表示形式。
public static String valueOf(float f) {
return Float.toString(f);
}
valueOf(int i): 返回 int 参数的字符串表示形式。
public static String valueOf(int i) {
return Integer.toString(i);
}
valueOf(long l): 返回 long 参数的字符串表示形式。
public static String valueOf(long l) {
return Long.toString(l);
}
valueOf(Object obj): 返回 Object 参数的字符串表示形式。
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
参数:
- 指定参数类型
返回值:
- 删除提问空白符的字符串
例子:
public static void main(String args[]) {
double d = 1100.00;
boolean b = true;
long l = 1234567890;
char[] arr = {'w', '3', 'x', 'u', 'e'};
System.out.println("返回值 : " + String.valueOf(d) );
System.out.println("返回值 : " + String.valueOf(b) );
System.out.println("返回值 : " + String.valueOf(l) );
System.out.println("返回值 : " + String.valueOf(arr) );
}
format
格式化字符串
源码:
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
public static String format(Locale l, String format, Object... args) {
return new Formatter(l).format(format, args).toString();
}
CharSequence subSequence(int beginIndex, int endIndex)
作用:
- 返回一个新的字符序列,它是此序列的一个子序列。
参数:
- beginIndex – 起始索引(包括)。
- endIndex – 结束索引(不包括)。
返回值:
- 返回一个新的字符序列,它是此序列的一个子序列。
例子:
String Str1 = new String("abcdefg");
System.out.print("返回值 :" );
System.out.println(Str1.subSequence(0, 2)); //:ab
System.out.print("原始值 :" );
System.out.println(Str1); //abcdefg
源码:
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
String substring(int beginIndex)
String substring(int beginIndex, int endIndex)
作用:
- 返回字符串的子字符串
参数:
- beginIndex – 起始索引(包括)。
- endIndex – 结束索引(不包括)。
返回值:
- 子字符串
例子:
String Str1 = new String("abcdefg");
System.out.print("返回值 :" );
System.out.println(Str1.substring(1)); //:bcdefg
System.out.print("原始值 :" );
System.out.println(Str1); //abcdefg
System.out.print("返回值 :" );
System.out.println(Str1.substring(1, 7)); //:bcdefg
System.out.print("原始值 :" );
System.out.println(Str1); //abcdefg
源码:
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
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);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
indexof
int indexOf(String str, int fromIndex)
int indexOf(String str)
作用:
- 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
int indexOf(int ch)
作用:
- 返回指定字符在字符串中第一次出现的索引。如果没有就返回-1
int indexOf(int ch, int fromIndex)
作用:
- 返回指定字符在字符串中第一次出现的索引【从formIndex开始查找】。如果没有就返回-1
参数:
- ch:字符
- fromIndex : 开始搜索的索引位置
- str – 要搜索的子字符串。
返回值:
- 指定子字符串在字符串中第一次出现处的索引,从指定的索引开始。
String Str1 = new String("abcdefgww");
System.out.print("查找字符 w 第一次出现的位置 :" );
System.out.println(Str1.indexOf('w')); //7
System.out.print("从第3个位置开始查找字符 w 第一次出现的位置 :" );
System.out.println(Str1.indexOf('w', 3)); // 7
System.out.print("字符串 ef 第一次出现的位置 :" );
System.out.println(Str1.indexOf("ef")); // 4
System.out.print("从第7个位置开始查找字符串 ef 第一次出现的位置 :" );
System.out.println(Str1.indexOf("ef")); //4
查询
Java 在线速查手册
深度分析:面试腾讯,阿里面试官都喜欢问的String源码,看完你学会了吗?
String()分析
Java源码分析,String的设计
JDK源码分析(7)String
https://www.cnblogs.com/yangming1996/p/6850441.html
深入理解Java常用类----String
https://blog.csdn.net/yulungggg/article/details/81039655