-
String字符串
-
不可被继承;实现了Comparable、Serializable
-
String的不可变性
-
String的实例化方式
- 方式一:使用字面量方式。 String s = “hello”
- 方式二:String s = new String(“hello”);
- 【面试题】String s = new String(“hello”)在内存中创建了几个对象? 两个!
- 内存解析:
- jdk6时,字符串常量池是存放在永久代
- jdk7时,字符串常量池改为存放在堆空间
- jdk8时,取消了永久代,取而代之的是元空间(使用直接内存)
-
连接操作:+
-
String的常用方法
-
String与其他相关结构的转换:包装类、字符数组、字节数组
-
String的常用算法题目
-
二、String、StringBuffer、StringBuilder
2.1 String 与其他类之间的转换
- String与包装类、基本数据类型之间的转化
/*
* String与包装类、基本数据类型之间的转化
*/
@Test
public void test1(){
//String-->包装类、基本数据类型:调用包装类Xxx的parseXxx()
String str = "123";
int num = Integer.parseInt(str);
System.out.println(num);
// 包装类、基本数据类型--> String:调用String的valueOf()
String str1 = String.valueOf(num);
System.out.println(str1);
String str2 = num + "";//底层需要创建SringBuilder,并调用append()导致效率第一valueOf()
System.out.println(str2);
}
- String与char[]之间的转化
/*
* String与char[]之间的转化
*/
@Test
public void test2(){
String str1 = "hello";
//String--->char[]:调动String的toCharArray()
char[] arr1 = str1.toCharArray();
for(int i = 0;i < arr1.length;i++){
System.out.println(arr1[i]);
}
//char[] ---> String:调用String的构造器
String str2 = new String(arr1, 0, arr1.length);//new String(arr1);
System.out.println(str2);
}
- String与byte[]之间的转化
/*
* String与byte[]之间的转化
*
*
* 1. 关联:
* 内存层面:
* char:用2个字节存储
*
* 存储层面:
* 一个char,应该用几个字节存储呢?跟字符集有关系,具体问题具体分析
*
* ASCII:给26个英文大小写字母,0-9等都分配了对应的一个字节数值。比如:a --> 97 A--> 65
* GBK:兼容了ASCII(如果出现英文字母,0-9时,仍然使用1个字节存储),一个汉字使用2个字节存储
* UTF-8:兼容了ASCII(如果出现英文字母,0-9时,仍然使用1个字节存储),一个汉字使用3个字节存储
*
* 2.
* 编码: 字符、字符串--->字节、字节数组
* > 从看得懂的,转换为看不懂的。
*
* 解码:字节、字节数组 --> 字符、字符串
* > 从看不懂的,转换为看得懂的。
*
* 结论:编码集要与解码集一致。否则会出现乱码!
*/
@Test
public void test3() throws Exception{
String str1 = "hello中国";
//编码:String--->byte[]:getBytes()
byte[] arr1 = str1.getBytes();//使用默认的字符集:UTF-8
System.out.println(Arrays.toString(arr1));
byte[] arr2 = str1.getBytes("gbk");//显式指定字符集。
System.out.println(Arrays.toString(arr2));
//解码:byte[] ---> String:使用构造器
String str2 = new String(arr1);//使用默认的字符集:UTF-8
System.out.println(str2);
String str3 = new String(arr2);
System.out.println(str3);//因为编码集和现在的解码集不一致,导致出现乱码!
String str4 = new String(arr2,"gbk");
System.out.println(str4);//因为编码集和现在的解码集都是使用gbk,没有乱码
}
- String、StringBuffer、StringBuilder
//String --> StringBuffer、StringBuilder
String str = "hello";
StringBuffer s1 = new StringBuffer(str);
StringBuilder s2 = new StringBuilder(str);
//StringBuffer、StringBuilder --> String
String str1 = s1.toString();
String str2 = s2.toString();
String str3 = new String(s1);
String str3 = new String(s2);
2.3 String、StringBuffer、StringBuilder三者对比
/*
* 一、三个类的对比
* String:不可变的字符序列; 底层使用char[]数组存储
* StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]数组存储
* StringBuilder:可变的字符序列; jdk1.5引入,线程不安全的,效率高;底层使用char[]数组存储
*
* 注意:在jdk8以后,String\StringBuffer\StringBuilder底层改成byte[]+字符集存储,节省内存空间。
*
*
* 1. StringBuffer与StringBuilder的主要区别就是StringBuffer中的方法声明为同步的了。
* 2. String s0 = "";//new char[0];
* String s1 = "abc";// new char[]{'a','b','c'};
*
* s1 += "de";//new char[]{'a','b','c','d','e'};
*
* StringBuilder s2 = new StringBuilder();//char arr = new char[16];
* StringBuilder s3 = new StringBuilder("abc");//char arr = new char["abc".length() + 16];
* s2.append("abc");//arr[0] = 'a',arr[1] = 'b',arr[2] = 'c';
*
* ...
* 有可能底层arr的char[]存储不下了,此时需要扩容!默认扩容为原来容量的2倍+2
*
* 启示:
* 1. 开发中如果频繁的对字符串进行修改操作,建议使用StringBuffer/StringBuilder,替换String
* 2.开发中如果不涉及线程安全问题或主动加锁,建议使用StringBuilder,替换StringBuffer
* 3.开发中添加的字符串数据较多,建议使用StringBuilder(int capacity),替换StringBuilder()
*/
@Test
public void test1() {
String str1 = "hello";
str1 += "world";// 新建一个内存空间保存helloworld
StringBuffer str2 = new StringBuffer("hello");
str2.append("world");// 与创建对象中的char[]数组是同一个
}
-
面试题:String、StringBuffer、StringBuilder的异同
-
对比三者的执行效率:
/*
* 对比三者的执行效率
* 从高到低:
* StringBuilder > StringBuffer > String
*/
@Test
public void test3() {
// 初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
// 开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
2.4 StringBuilder和StringBuffer的常用方法
/*
*
* 二、StringBuffer和StringBuilder中的常用方法
*
* StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接 StringBuffer
* delete(int start,int end):删除指定位置的内容
* StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
* StringBuffer insert(int offset,xxx):在指定位置插入xxx
* StringBuffer reverse() :把当前字符序列逆转
* public intindexOf(String str)
* public String substring(int start,int end)
* public int length()
* public char charAt(int n )
* public void setCharAt(int n ,char ch)
*
* 总结:
* 增:append(xxx)
* 删:delete(int start,int end)
* 改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
* 查:charAt(int n )
* 插:insert(int offset, xxx)
* 长度:length()
* 遍历:for + charAt() / toString()
*
*
*/
@Test
public void test2() {
StringBuffer s1 = new StringBuffer();
s1.append("abc").append("def").append("123");// 方法链的调用
System.out.println(s1);
s1.replace(3, 6, "hello");
System.out.println(s1);
s1.insert(3, "123");
System.out.println(s1);
}