字符串

String类

概念:String是不可变类,即一但一个String对象被创建,包含在这个对象的字符序列是不可改变的,直至该对象被销毁。

1 //String的主要成员变量
2 private final char value[];
3 /**
4 value指向的是一个字符串数组,字符串中的字符就是用这个value变量存储起来的,并且用final修饰,说明value一旦赋予初始值之后,value指向的地址就不能再改变了,虽然value指向的数组是可以改变的,但是String也没有提供相应的方法让我们去修改value指向的数组的元素,所以说String是一个不可变类,String对象的不可变还有一点就是对String类型的所有改变内部存储结构的操作都会new出一个新的String对象
5 */

String类是被final修饰的,不能有子类,里面的一些方法也加上了final

例子:也就是说创建"abc"字符串后,"abc" 自出生到最终死亡,不可变,不能变成"abcd",也不能变成"ab"

注意:"abc" 这是一个字符串对象,字符串在java中有优待,不需要new也是一个对象,属于String类型。

在java中随便使用双引号括起来的都是String对象。

例如:"abc","def","hello world!",这是3个String对象

在JDK当中双引号括起来的字符串,例如:"abc" "def"都是直接存储在“方法区”的“字符串常量池”当中的。

注解:在JDK1.7时,把字符串常量池移动到了堆内存中

为什么SUN公司把字符串存储在一个“字符串常量池”当中呢。因为字符串在实际的开发中使用太频繁。

为了执行效率,所以把字符串放到了方法区的字符串常量池当中

当使用Sring str = "abc";这种形式创建字符串,那么String就不会在堆内存中创建对象,"abc"存储在字符串常量池中,而str存储的是"abc"在字符串常量池中的地址

当使用String str = new String("abc");这种形式创建字符串,需要在堆内存中创建字符串对象,而在String对象在堆内存中保存的是"abc"在字符串常量池中的内存地址,而str保存的是对象在堆内存中的地址

例子:

  1. 第一个例子

    1 String str1 = "abc";
    2 String str2 = "abc";
    3 System.out.println(str1 == str2);//true
    4 //为true原因:在字符串常量池中相同的字符串只会存储一份,不会重复,而str1和2都是同一个地址
  2. 第二个例子

    1 String str1 = "abc";
    2 String str2 = "abc";
    3 String str3 = "ab" + "c";
    4 System.out.println(str1 == str3);//true
    5 //为true原因:"ab"和"c"都是字面值常量,在编译时直接编译成"abc",而"abc"在字符串常量池中存在了,就不会再创建,直接把地址赋值给str3
  3. 第三个例子

    1 String str1 = "abc";
    2 final String s1 = "ab";
    3 final String s2 = "c";  
    4 String str4 = s1 + s2;
    5 System.out.println(str1 == str4);//true
    6 //为true原因:s1 和s2都是常量,在编译时直接编译成"abc"
  4. 第四个例子

    1 String str1 = "abc";
    2 String s3 = "ab";
    3 String s4 = "c";
    4 String str5 = s3 + s4;
    5 System.out.println(str1 == str5);//false
    6 //为false的原因:s3、s4时变量,字符串拼接时,如果有变量参与的情况下,String底层使用的是StringBuilder,所以内存地址不一样了
    7 //String str5 = s3 + s4;拼接new StringBuilder(s3).append(s4).toString()

    注意:频繁的字符串拼接最好不要使用 +,因为java中的字符串是不可变的,每一次拼接都会new StringBuilder拼接产生新字符串。这样会占用大量的内存。造成内存空间的浪费。

    如果以后需要进行大量字符串的拼接操作,建议使用JDK中自带的:

    • java.lang.StringBuffer

    • java.lang.StringBuilder

  5. 第五个例子

    1 String s5 = new String("hello");
    2 String s6 = new String("hello");
    3 //上面两行一共创建了3个对象:
    4 //字符串常量池中有1个:"hello"
    5 //堆内存当中有两个String对象。

通过例子,字符串对象之间的比较不能使用“==”,"=="不保险。应该调用String类的equals方法,String类已经重写了toString()和equals()方法

注解:使用equals()方法进行比较时,把已知道的放在前面,未知的放在equals()方法中,这样可以避免空指针异常

String类的构造方法

1 String s = "abc";
2 String s = new String("abc");
3 String s = new String(byte数组);//将整个数组转换为字符串
4 String s = new String(byte数组, 起始下标, 长度);//将数组中的一部分转换为字符串
5 String s = new String(char数组);
6 String s = new String(char数组, 起始下标, 长度);
7 ...

 

valueOf()方法,参数是一个对象的时候,会自动调用该对象的toString()方法

没有重写toString()方法之前打印输出的是对象内存地址

为什么输出一个引用的时候,会调用toString()方法?

通过源代码可以看出println()这个方法,先调用了String中的valueOf()方法,然后valueOf()方法调用了toString()方法

注解:在输出任何数据的时候println()都是先转换成字符串,再调用toString(),然后输出,当println()里的是对象,也是先转换为字符串,然后调用toString(),只不过调用的是对象的toString()方法,如果对象没有重写,就打印对象内存地址

StringBuffer类

含义:StringBuffer代表可变的字符序列,StringBuffer称为字符串缓冲区,实现了序列化接口

继承了AbstractStringBuilder

工作原理:预先申请一块内存,存放字符序列,如果字符序列满了,会重新改变缓存区的大小,以容纳更多的字符序列。StringBuffer是可变对象,与String最大的不同在StringBuilder中是提供了响应的方法让我们去修改value指向的数组的元素,这也是StringBuffer的字符串序列可变的原因。

JDK1.8,StringBuffer底层是一个char数组,StringBuffer默认的初始化容量是16

StringBuffer是线程安全的,StringBuffer中的每个方法都加上了synchronized锁

StringBuffer拼接字符使用的是append()方法,在字符串的末尾追加,不会新建对象

append()在进行字符串追加的时候,会调用有ensureCapacityInternal()方法判断是否需要进行扩容

而ensureCapacityInternal()方法中扩容调用的是Arrayys数组中copyOf()方法

如何优化StringBuffer的性能?

  • 在创建StringBuffer的时候尽可能给定一个初始化容量。

  • 最好减少底层数组的扩容次数。预估计一下,给一个大一些初始化容量。

  • 关键点:给一个合适的初始化容量。可以提高程序的执行效率。

StringBuilder类

含义:StringBuilder代表可变的字符序列,称为字符串缓冲区,实现了序列化接口

继承了AbstractStringBuilder

工作原理:和StringBuffer相同

StringBuilder不是线程安全的

JDK1.8,StringBuilder底层是一个char数组,StringBuilder默认的初始化容量是16

posted @ 2020-12-12 11:43  想变强的菜鸟  阅读(59)  评论(0编辑  收藏  举报