一、创建String对象的两种方式

  1. 直接赋值
  2. new
//1.直接使用赋值方法获取一个字符串对象
String s1 = "abc";
System.out.println(s1);  //abc

//2.使用new的方式来获取一个字符串对象
//空参构造:可以获取一个空白字符串对象
String s2 = new String();
System.out.println("a"+s2+"b"); //ab   s2=""

//3.传递一个字符串,根据传递的字符串内容再创建一个新的字符串对象
String s3 = new String("abc");
System.out.println(s3); //abc

//4.传递一个字符数组,根据字符数组的内容再创建一个新的字符串对象
char[] chs={'a','b','c','d'};
String s4 = new String(chs);
System.out.println(s4); //abcd

//5.传递一个字节数组,根据字节数组的内容在创建一个新的字符串对象
byte[] bytes={95,96,97,98,99};
String s5 = new String(bytes);
System.out.println(s5); //abcd

image-20220903092515256
image-20220903092515256

二、不同创建对象方式的内存区别

字符串池(StringTable)是存在于堆中的一块区域(JDK1.7之后串池从方法区移动到了堆内存中。)

1.直接赋值

public class MainApp{
    public static void main(String[] args){
        String s1="abc";
        String s2="abc";
    }
}

image-20220903100900732
image-20220903100900732

当使用双引号直接赋值时,系统会检查该字符串在串池中是否存在。

例如上文的代码段,对s1赋值时,在串池中未找到"abc"字符串,于是新建一个,当对s2赋相同值时,在串池中找到了该值,于是就将该值的地址直接给到s2。

不存在:创建新的
存在:复用

2.new

public class MainApp{
    public static void main(String[] args){
        char[] chs = {'a','b','c'};
        String s1 = new String(chs);
        String s2 = new String(chs);
    }
}

image-20220903101440318
image-20220903101440318

每new一次就开辟了一个新的内存空间。

三、String字符串比较方法

1.==号

String s1 = "abc";
String s2 = "abc";
String s3 = "aaa";
System.out.println(s1==s2);//true
System.out.println(s1==s3);//false
  • ==号比较的是什么?

    1. 比较基本数据类型

      int a = 10;
      int b = 20;
      System.out.println(a==b);//false

    基本类型比较的是数据值。

    1. 引用数据类型

      String s1 = new String("abc");
      String s2 = new String("abc");
      System.out.println(s1==s2);//false

    引用数据类型比较的是地址值。

思考:

String s1 = new String("abc");
String s2 = "abc";
Syetem.out.println(s1==s2);//true or false ?

只要new出来的就在实在堆里开辟的空间,而s2记录的为StringTable串池里的内容。

Scanner sc = new Scanner(System.in);
String s1 = sc.next(); //abc
String s2 = "abc"
System.out.println(s1==s2); //false

这里为false的原因与上面相同,通过键盘输入获取的字符串也相当于new出来的,其地址存在堆内存中,而"=="比较的是地址值,所以返回false

2.equals方法

  • boolean equals() 比较的两个值完全一样为true,否则为false
  • boolean equalsIgnoreCase() 忽略两个比较值的大小写后再进行比较

四、StringBuilder

static void test1(){
    String s ="";
    long timeAgo=System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        s = s + "abc";
    }
    long timeNow=System.currentTimeMillis();
    System.out.println("test1执行耗时"+(timeNow-timeAgo)+"ms");
}
static void test2(){
    StringBuilder s=new StringBuilder();
    long timeAgo=System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        s.append("abc");
    }
    long timeNow=System.currentTimeMillis();
    System.out.println("test2执行耗时"+(timeNow-timeAgo)+"ms");
}

写出上面两个方法,对字符串s不断拼接1000000个“abc”,一个使用s+="abc",另一个使用StringBuilder类中的append()方法。

打印结果为

test1执行耗时138645ms

test2执行耗时39ms

StringBuilder可以看作一个容器,创建后里面的内容可变。

String s1 = "a";
String s2 = "b";
String s3 = "c";
String s4 = "d";
String s5 = "e";
String s6 = s1+s2+s3+s4+s5;

当运行上面代码段时,s1和s2首先会拼接成一个新的字符串,然后用新的字符串再跟s3拼接...整个拼接过程中生成了很多无用的字符串,每个字符串都在StringTable串池中开辟了新的地址,非常影响内存。

StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");
sb.append("e");

当通过StringBuilder拼接字符串时不会产生无用字符串,始终在一个地址值上对字符串进行拼接。