一、String类:
1. 字符串类Java的底层实现的类,final修饰安全考虑
字符串本质是一个常量。(一旦被初始化在内存中只有一份)
字符串缓冲池:字符串常量池(保证了字符串只有一份)
2. 面试题:java中字符串常量池在什么地方?
jdk7之前,字符串常量池在方法区(方法区还有类的信息,小整形缓冲池,静态属性,方 法,引用)
jdk7之后,字符串常量池被设计到了堆内存中(小整形缓冲池,静态 被挪到了元数据区)
3. 字符串创建方式:
String s = "Jack"; // 这种方式创建字符串(先从常量池找Jack,找到了则返回 Jack这个单词的引用;若找不到,则在常量池中初始化字符串,再将地址返回给s)
String ss = new String("Jack"); //会创建一个堆内存
4. 面试题:下面这段代码会创建几个字符串对象
String s = "ljh"//有可能创建一个,有可能不创建
String ss = new String("ljh")//有可能创建一个,有可能创建两个
5. 字符串拼接问题:
字符串拼接时,如何判断拼接后的结果是否相等(==)
|-- 常量始终是常量
|-- 编译时,如果结果确定,则会相等??
package com.openlab.day15;
import org.junit.jupiter.api.Test;
public class TestString {
@Test
void test01() {
String s = "hello";
String ss = "hello";
System.out.println(s == ss);
}
@Test
void test02() {
String s = "hello";
String ss = new String("hello");
System.out.println(s == ss);
}
@Test
void test03() {
String s = "hello";
String ss = "world";
String sss = "helloworld";
System.out.println(s + ss);
System.out.println(s + ss == sss);
}
@Test
void test04() {
String s = "hello";
String ss = "world";
final String sss = "helloworld";
System.out.println(s + ss == sss);
}
}
package com.openlab.demo;
import org.junit.jupiter.api.Test;
class Test01 {
@Test
void test01() {
String s1 = "hello";
final String s2 = "hello";
String ss = new String("hello");
String s3 = "hello2";
String s4 = s1 + 2;
String s5 = s2 + 2;
System.out.println(s1 == s2);
System.out.println(s1 == ss);
System.out.println(s2 == ss);
System.out.println(s3 == s4);
System.out.println(s3 == s5);
}
}
因为字符串是常量,字符串在大量拼接,容易产生垃圾数据。
字符串是一种常量,一旦产生一个字符,如果常量池中,不存在这个字符串,则会将这个字 符串保存常量池(这个保存时永久性的、只有系统退出才会被销毁)
字符串不像其他变量,如int a = 10,当后面不用会自动回收,而字符串是常量,无法被回收
因此在开发过程中,一定注意,不要大量拼接字符串,以避免产生大量无用的字符串消耗内存
java为了解决这个问题,引入了两个字符串操作类:StringBuffer和StringBuilder
void test06() {
//拼接字符串 123456789101112...99100
String s = "";//存在常量池
for(int i = 1; i <= 100; i++) {
s += i;
//会产生很多的字符串"1" "12" "123"..."123...99" "123...99100"
//但我们需要的只是最后一个字符串,但在此过程中产生了很多没有用的字符串
}
System.out.println(s);
}
二、StringBuffer和StringBuilder
1. java提供给用来操作字符串的两个工具类,和字符串没有关系
是两个对象,不是常量,可以借助这两个对象对字符串进行拼接,因为在拼接过程中产生的 临时变量没有变量保存就消失了。如下面的for循环
for(int i = 0; i < 100; i++){
System.out.println(i);
}
//每次都赋给了i变量,最终只有一个变量i,其他99个在循环的过程中都舍弃了,因为不是常量,所以一旦创建不用,就自动回收了,不像字符串,是常量,一旦产生会加载到常量区,出不去,gc都没有用
这两个对象都是jdk底层提供用来拼接大量字符串从对象,不会产生大量无用常量,替代字符串的大量拼接,提高效率。
@Test
void testStringBuffer() {
StringBuffer buffer = new StringBuffer();//不写也是空空
//或者StringBuffer buffer = new StringBuffer("");
for(int i = 1; i <= 100; i++) {
buffer.append(i);//在尾部添加
//buffer.insert(0, i);//也可以向其他位置添加
}
System.out.println(buffer.toString());
System.out.println(buffer);
}
@Test
void testStringBuilder() {
StringBuilder builder = new StringBuilder();//不写也是空空
//或者StringBuffer buffer = new StringBuffer("");
for(int i = 1; i <= 100; i++) {
builder.append(i);
}
System.out.println(builder.toString());
System.out.println(builder);
}
2. 两个用法一样,都会输出:
(太长,一张显示不下)
3. StringBuffer和StringBuilder 区别:
1).StringBuffer:
所有操作方法上加上了同步锁,所以是线程安全的
synchronized:同步
如:append()方法
2).StringBuilder:
没有加同步锁,因此非线程安全的!!!
一般情况下建议使用StringBuilder,效率更高(不加锁效率高)
但是不加锁在高并发,多线程情况下可能会出现线程安全问题,用StringBuffer(但是在这种情况下很少用字符串,所以还是StringBuilder用的更多一点)