字符串
分类
不可变字符串:
- 对应的类:String.
- 特点:字符串本身不能发生改变,与指向字符串的引用无关.
- 直接使用"",创建的是不可变字符串
可变字符串:
-
对应的类:StringBuilder/StringBuffer.
-
特点:字符串本身可以发生变化,与指向可变字符串的引用无关
-
创建可变字符串:
StringBuffer stringBuffer = new StringBuffer(“hello world”);
字符串的内存分析
字符串, 是一个引用数据类型。 但是字符串的引用, 和之前在面向对象部分的引用有一点差
别。
差别: 类的对象, 是直接在堆上开辟的空间。 字符串, 是在 常量池 中开辟的空间。 (常量
池, 是在方法区中的一个子空间)
-
String str = “hello world”;
此时, “hello world”, 是在 常量池 中开辟的空间。 str里面存储的, 其实是常量池
中的某一个内存的地址。当 str = “30”; 的时候, 其实, 并不是修改了 str指向的空间中的内容。 因为常量池
空间特性, 一个空间一旦开辟完成了, 里面的值是不允许修改的。 此时, 是在常量
池中开辟了一块新的空间, 存储了 “30”, 并把这个新的空间的地址给str赋值了。字符串类型, 之所以选择在常量池中进行空间的开辟, 而不是在堆上。 原因是需要
使用 享元原则 。 -
String str = new String(“hello world”);
String是一个Java中用来描述字符串的类, 里面是有构造方法的。
通过String类提供的构造方法, 实例化的字符串对象, 在堆上开辟的空间。 在堆空
间中, 有一个内部维护的属性, 指向了常量池中的某一块空间。
字符串拼接的内存分析
-
直接使用两个字符串字面量进行拼接
其实,就是直接将两个由双引号直接括起来的字符串进行拼接 。类似于 String str
= “hello” + “world”; 。这里,直接在常量池中进行空间操作。将常量池中拼接之后的结果, 地址给 str 进行
赋值。 -
使用一个字符串变量和其他的进行拼接
String s = new String(“hello”);
String s1 = s + “world”;
- 这里的拼接,不是在常量池中直接完成的。
- 在这个拼接的过程中, 隐式的实例化了一个String类的对象, 在堆上开辟了空间。堆上空间内部维护了一个指向了常量池中拼接结果的一个属性。 这个堆上的空间地址给左侧的引用进行了赋值。
字符串的常用方法
字符串的构造方法
字符串的非静态方法
因为字符串, 是常量。 任何的修改字符串的操作, 都不会对所修改的字符串造成任何的影响。 所有的对字符串的修改操作, 其实都是实例化了新的字符串对象。 在这个新的字符串中, 存储了修改之后的结果。 并将这个新的字符串以返回值的形式返回。 所以, 如果需要得到对一个字符串修改之后的结果, 需要接收方法的返回值。
- 常用的非静态方法
- 常用的静态方法
StringBuffer和StringBuilder类
都是用来操作字符串的类,我们成为可变字符串
字符串都是常量, 所有的操作字符串的方法, 都不能直接修改字符串本身。 如果我们需要得到修改之后的结果, 需要接收返回值。
StringBuffer和StringBuilder不是字符串类, 是用来操作字符串的类。 在类中维护了一个字符串的属性, 这些字符串操作类中的方法, 可以直接修改这个属性的值。 对于使用方来说,可以不去通过返回值获取操作的结果。
在StringBuffer或者StringBuilder类中, 维护了一个字符数组。 这些类中所有的操作方法,都是对这个字符数组进行的操作。
StringBuffer和StringBuilder从功能上来讲, 是一模一样的。 但是他们两者还是有区别的:
- StringBuffer是线程安全的。
- StringBuilder是线程不安全的。
备注
但凡是涉及到字符串操作的使用场景, 特别是在循环中对字符串进行的操作。 一定不要使用字符串的方法, 用StringBuffer或者StringBuilder的方法来做。
r是线程不安全的。
备注
但凡是涉及到字符串操作的使用场景, 特别是在循环中对字符串进行的操作。 一定不要使用字符串的方法, 用StringBuffer或者StringBuilder的方法来做。
由于字符串本身是不可变的, 所以String类所有的修改操作, 其实都是在方法内实例化了一个新的字符串对象, 存储拼接之后的新的字符串的地址, 返回这个新的字符串。 如果操作比较频繁, 就意味着有大量的临时字符串被实例化、被销毁, 效率极低。 StringBuffer、StringBuilder不同, 在内部维护了一个字符数组, 所有的操作都是围绕这个字符数组进行的操作。 当需要转成字符串的时候, 才会调用 toString() 方法进行转换。 当频繁用到字符串操作的时候, 没有中间的临时的字符串出现, 效率较高。