第五篇 Java常用类(一) 字符串相关类
一、String类
(注意:在Java中我们可以这样声明一个字符串String str = 'hello';但是,String并不是像int、char、double等的基本数据类型,它是Java中提供的类,可以使用字面量的方式声明,也可以使用实例化对象的方式:String str = new String('hello');)
1.String的特性
- 不可变性:String对象是不可变的,查看JDK文档就会发现,String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。
《Java编程思想(第4版)》

- 不可变对象的体现:对字符串重新赋值或字符串连接(包括调用repalce( )方法),需重写指定内存区域赋值,不能通过原有的value进行修改;
2.String的存储
(1)String实例化方式的对比

- 我们声明的字符串常量会存储在字符串常量池中,非常量(对象)存储在堆中。
- 通过字面量的声明并赋值字符串的方式(不同于new):直接指向存储在字符串常量池中对应的字符串地址。
- 通过new的方式:String对象指向堆中的地址,该地址存储字符串常量池中对应字符串的地址。
- 字符串常量池中不会存储相同内容的字符串(如:str1和str2均指向同一个字符串)
(2)String拼接方式的对比

- 结论1:常量与常量的拼接结果存储在字符串常量池中,且常量池中不会存在相同内容的常量。
- 结论2:变量和其他(变量/常量)拼接结果存储在在堆中。
- 结论3:如果拼接的字符串调用intern()方法,返回得到的字符串也存储在常量池中。
(和变量拼接就放在堆中,其他在字符串常量池中)
3.String的常用方法
| 方法 | 作用 |
|---|---|
| int length( ) | 返回字符串长度(value[ ]数组的长度value.length) |
| char charAt( int index ) | 返回索引值处的字符(即value[index]) |
| boolean isEmpty( ) | 判断是否为空字符串 |
| String toLowerCase( ) | 使用默认语言环境,将String中所有字符转换为小写字符 |
| String toUpperCase( ) | 使用默认语言环境,将String中所有字符转换为大写字符 |
| boolean equals( Object obj ) | 比较字符串内容是否一致 |
| boolean equalsIgnoreCase( String str ) | 比较字符串内容是否一致(忽略大小写) |
| String trim( ) | 返回忽略前后空白的字符串 |
| String concat( String str ) | 将指定字符串连接到此字符串结尾(相当于+) |
| int compareTo( String str ) | 比较两个字符串的大小,返回长度差值 |
| String substring( int beginIndex ) | 返回从指定索引开始截取到最后的子字符串 |
| String substring( int beginIndex,int endIndex ) | 返回从指定索引开始截取到指定索引结束(不包括)的子字符串 |
| boolean endsWith( String suffix ) | 判断字符串是否以指定后缀(字符串)结束 |
| boolean startsWith( String prefix ) | 判断字符串是否以指定前缀(字符串)结束 |
| boolean startsWith( String prefix,int toffset ) | 判断字符串从指定索引开始的子字符串是否以指定前缀开始 |
| boolean contains( CharSequence s ) | 判断字符串是否包含指定的char值序列 |
| int indexOf( String str ) | 返回字符串在指定字符串中第一次出现的索引 |
| int indexOf( String str,int fromIndex ) | 返回字符串在指定字符串的指定索引第一次出现的索引 |
| int lastIndexOf( String str ) | 返回字符串在指定字符串中从右开始出现的索引 |
| int lastIndexOf( String str,int fromIndex ) | 返回字符串在指定字符串从右开始到指定索引开始出现的索引 |
| String replace( char oldChar,char newChar ) | 返回通过newChar替换原来的oldChar得到的字符串 |
| String replace( CharSequence target,CharSequence replacement ) | 使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 |
| String replaceAll( String regex,String replace ) | 根据指定的字符串替换匹配的正则表达式的所有字符串 |
| String replaceFirst( String regex,String replace ) | 根据指定的字符串替换匹配的正则表达式的第一个子字符串 |
| boolean matches( String regex ) | 判断字符串是否匹配指定的正则表达式 |
| String[ ] split( String regex ) | 根据指定的正则表达式拆分字符串 |
| String[ ] split( String regex,int limit ) | 根据指定正则表达式拆分字符串,超出limit放置最后一个元素 |
4.String的转换
(1)字符串转基本数据类型、包装类
| 方法 | 作用 |
|---|---|
| Integer.parseInt( String str ) | 字符串转整型 |
| Integer.parseByte( String str ) | 字符串转字节型 |
| Integer.parseFloat( String str ) | 字符串转浮点型 |
| …… | …… |
(2)基本数据类型转字符串
| 方法 | 作用 |
|---|---|
| valueOf( int n ) | int型转字符串 |
| valueOf( byte b ) | 字节型转字符串 |
| valueOf( float f ) | 浮点型转字符串 |
| …… | …… |
(3)字符数组char[ ]转字符串
| String的构造器 | 作用 |
|---|---|
| String( char [ ] ) | 将字符数组转成字符串 |
| String( char [ ],int offset,int length ) | 将指定位置开始和长度的字符数组转成字符串 |
(4)字符串转字符数组char[ ]
| 方法 | 作用 |
|---|---|
| toCharArray( ) | 字符串转字符数组 |
| getChars( int srcBegin,int srcEnd,char[] dst,int dstBegin ) | 指定字符串转字符数组 |
(5)字节数组byte[ ]转字符串
| String的构造器 | 作用 |
|---|---|
| String( byte[ ] ) | 使用默认字符集转换byte数组为字符串 |
| String( byte[ ],int offset,int length ) | 使用默认字符集转换byte数组的指定索引、长度为字符串 |
(6)字符串转字节数组
| 方法 | 作用 |
|---|---|
| getBytes( ) | 使用默认字符集转换将字符串为字符数组 |
| getBytes( String charsetName ) | 使用指定字符集转换将字符串为字符数组 |
二、StringBuffer类和StringBuilder类
上面所讲的String类是一个不可变的对象(不可变序列的字符串),接下来的StringBuffer和StringBuilder类均是可变的
1.String、StringBuffer和StringBuilder的区别
String:不可变的字符序列
StringBuffer:可变的字符序列,线程安全(方法均为synchronized声明的同步方法),但效率较低
StringBuilder:可变的字符序列,线程不安全,效率高(JDK 5.0新增)
(String、StringBuffer和StringBuilder底层都是使用byte[ ]数组来存储,而StringBuffer和StringBuilder是可变的,所以底层数组没有用final声明,而String类是不可变的,所以底层存储的数组用final声明private final byte[ ] value。PS:老版本JDK是char型数组)
2.StringBuffer和StringBuilder的底层实现
在使用String类时,我们操作字符串(对字符串重新赋值或字符串连接,包括调用repalce( )方法),不能通过底层原有的byte[] value数组进行修改,而是需要在内存中重新创建一个String对象,所以才说String类是一个不可变的对象;StringBuffer类和StringBuilder类与其不同,都是可变的对象,那么这两个类的底层是如何实现其的"可变"的呢?
(1)数组byte[ ] value的容量



(2)代码验证(一)
【验证1-1】创建一个初始化为空字符的(空参)的StringBuffer对象,底层的byte[ ] value默认容量大小为16。
【验证1-2】创建一个初始化为字符串str(带参)的StringBuffer对象,底层的byte[ ] value默认容量大小为str.length( )+16
import org.testng.annotations.Test;
public class StringTest {
@Test
public void test3(){
StringBuffer strBuffer1 = new StringBuffer("");
/**
*【验证1-1】创建一个初始化为空字符的(空参)的StringBuffer对象,
* 底层数组默认容量大小为16
**/
System.out.println("初始化为空字符串的长度:"+strBuffer1.length());//""长度0
System.out.println("初始化为空字符串byte[] value的容量大小:"+strBuffer1.capacity());//16
/**
*【验证1-2】创建一个初始化为str字符串(带参)的StringBuffer对象,
* 底层数组默认容量大小为str.length()+16
**/
StringBuffer strBuffer2 = new StringBuffer("hello");
System.out.println("初始化为指定字符串的长度:"+strBuffer2.length());//"hello"长度5
System.out.println("初始化为指定字符串时byte[] value的容量大小:"+strBuffer2.capacity());//21
}
}

到了这里,如果调用strBuffer2.append('aaaaa')直到超出原初始化的长度21,数组即将越界,底层是如何实现其容量的扩容呢?
(3)数组byte[ ] value的扩容




(4)代码验证(二)
【验证2-1】默认情况下,StringBuffer底层value数组扩容后的容量为原来的2倍+2。
【验证2-2】当需要的容量超过原数组容量的2倍+2,则直接扩容为所需容量。
import org.testng.annotations.Test;
public class StringTest {
@Test
public void test3(){
/**
* 【验证2-1】默认情况下,StringBuffer底层value数组扩容后的容量为原来的2倍+2。
* */
StringBuffer stringBuffer = new StringBuffer();
System.out.println("当前字符串长度:"+stringBuffer.length());//0
System.out.println("当前value数组容量:"+stringBuffer.capacity());//16
stringBuffer.append("HelloWorldHelloWorld"); //length = 20
System.out.println("扩容字符串长度:"+stringBuffer.length());//20
System.out.println("扩容后value数组容量:"+stringBuffer.capacity());//34
}
@Test
public void test4(){
/**
* 【验证2-2】当需要的容量超过原数组容量的2倍+2,则直接扩容为所需容量。
* */
StringBuffer stringBuffer = new StringBuffer();
System.out.println("当前字符串长度:"+stringBuffer.length());//0
System.out.println("当前value数组容量:"+stringBuffer.capacity());//16
stringBuffer.append("HelloWorldHelloWorldHelloWorldHelloWorld"); //length = 40
System.out.println("扩容字符串长度:"+stringBuffer.length());//40
System.out.println("扩容后value数组容量:"+stringBuffer.capacity());//40
}
}

3.StringBuffer和StringBuilder的常用方法
| 方法 | 作用 |
|---|---|
| StringBuffer append(xxx) | 字符串拼接(增) |
| StringBuffer insert(int offset,xxx) | 在指定位置插入元素(增) |
| StringBuffer delete(int start,int end) | 删除字符串元素(删) |
| StringBuffer replace(int start,int end,String str) | 修改指定位置的元素(改) |
| setCharAt(int n,char ch) | 修改指定位置的元素(改) |
| char charAt(int n) | 返回指定位置的元素(查) |
| StringBuffer reverse( ) | 字符串逆序排列 |
| ……(其他与String类似) | …… |
-
遍历数组元素
for(int i=0;i<stringBuffer.length();i++){ System.out.print(stringBuffer.charAt(i)); }
4.String、StringBuffer和StringBuilder效率
- StringBuilder > StringBuffer > String
import org.testng.annotations.Test;
public class StringTest {
@Test
public void test6() {
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间: " + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间: " + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间: " + (endTime - startTime));
}
}


被折叠的 条评论
为什么被折叠?



