1:String 数据结构
1-1:String 为什么是 final 的?
- 为了实现字符串池
- 为了线程安全
- 为了实现 HashCode 的不变性
1-2:String 的内部属性
String 内部其实就是一个 char 数组。
1 | private final char value[]; |
1-3:String 的常用方法
- charAt()
- indexOf()
- replace()
- subString()
- …
1-4:subString 原理
通过传入的参数构建一个新的 String 对象并返回这个对象。
1 | public String subString(int beginIndex) { |
而这个构造方法底部其实就是通过复制创建一个新数组。
1 | public String(char value[], int offset, int count) { |
1-5:String 长度有限制的
- 编译期的限制:字符串的 UTF8 编码值的字节数不能超过 65535,字符串的长度不能超过 65534。
- 运行时限制:字符串的长度不能超过 2^31-1,占用的内存数不能超过虚拟机能够提供的最大值。
2:String str = new String(“abc”);创建了几个对象
这段代码将创建 1 或 2 个字符串对象。
- 如果池中已存在字符串常量 abc,则只会在堆空间创建一个字符串对象 s1,s1 内部的 char value[] 则指向常量池中的 abc。
- 如果池中没有字符串常量 abc,那么它将首先在池中创建 abc 对象,然后在堆空间中创建 s1 对象,s1 指向堆中 new 的对象,而 s1 内部的 char value[] 则指向常量池中的 abc。因此将创建总共 2 个字符串对象。
所以 s1 指向堆内存,s2 指向常量池,但是 s1 的 value[] 和 s2 的 value[] 指向的是常量池中的同一个对象。所以 s1 == s2 为 false,s1.equals(s2) 为 true。
2-1:String str=”abc”,堆和常量池中的情况
会在常量池中创建对象,不会创建在堆中。
3:String 的==与 equals 问题
== 的作用:
- 基本类型:比较值是否相等。
- 引用类型:比较内存地址值是否相等。
equals 的作用:
- 引用类型:默认情况下,比较内存地址值是否相等。可以按照需求逻辑,重写对象的 equals 方法。
3-1:两个 st1 = “abc”相等问题
1 | String str1 = "abc"; |
因为用这种方式创建,这两个字符其实指向的是一个引用。
3-2:一个创建对象,一个 str1=“abc”
1 | String str1 = "abc"; |
因为创建对象时引用指向堆内存中的对象,而使用 equals 会比较对象内部对字符串的引用
3-3:一个 a+b+c,一个 abc
1 | String str1 = "a"+"b"+"c"; |
其实 str1 只会创建一个对象,因为编译器进行了优化。会自动处理为”abc”。
3-4:一个 str1+c,一个 abc
1 | String str1 = "ab"; |
因为 String 中+
的重载其实是转为 StringBuilder.append()添加再通过 toString()转为 String 类型。而 StringBuilder 的 toString()方法如下:
1 | public String toString() { |
相当于用 new 方式创建一个 String 对象,那么这两种比较结果就可以看出来。
3-5:两个 new String 对象
1 | String str1 = new String("abc"); |
创建两个对象,比较引用肯定是 false,但是内部数组指向的都是常量池中的同一数据,所以 equals 为 true。
4:拼接方式
5: String、StringBuffer 和 StringBuilder 区别
String 不可变,StringBuffer 和 StringBuilder 可变。而 StringBuffer 线程安全,StringBuilder 线程不安全。
5-1:StringBuffer 如何实现线程安全
StringBuffer 的方式为使用synchronized
修饰方法。
5-2:处理数据量较大的字符串用 String 还是 StringBuilder,为什么?
StringBuilder,因为 String 的各种拼接其实就是转化为 StringBuilder 来进行。
5-3:为什么 StringBuffer 和 StringBuilder 比 String 更快(不变性)
- String 类设计成 final 类型,每次有修改操作时,都会赋值给新的对象。
- 因为赋值给新的对象,原来的对象就不再引用,就会进行回收。
6:应用
6-1:如何把一段逗号分割的字符串转换成一个数组?
1 | char[] arr = s.split(","); |
6-2:String 和 char[] 数组谁更适合存密码
String,因为 String 不可变,所以不会对密码进行修改。