以下为 Jean 学习笔记和习题整理,供学习和参考
# String
String 底层就是 char [],char 类型的数组,使用时开发者不需要直接操作底层数组,用更加简便的方式即可完成对字符串的使用。
特点:
不变性:对 String 对象进行任何操作都是创建一个新的对象,再把引用指向该对象。不变的模式主要作用在当一个对象需要被多线程共享并频繁访问时,可以保持数据的一致性。
常量池优化:String 创建对象之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
final: 使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。这也是 String 为什么不可变
源码:private final char value [];
❓String 真的不可变的吗?
1.String 不可变但不代表引用不可以变;
2. 通过反射是可以修改所谓的 “不可变” 对象。
String s = "hello world"; | |
System.out.println("s=" + s); // s=hello world | |
// 获取 String 类中的 value 字段 | |
Filed valueFieldOfString = String.class.getDeclarField("value"); | |
// 改变 value 属性的访问权限 | |
valueFiledOfString.setAccessible(true); | |
// 获取 s 对象上的 value 的属性 | |
char[] value = (char[])valueFiledOfString.get(s); | |
// 改变 | |
value[5] = '_'; | |
System.out.println("s=" + s); // s=hello_world |
和 Integer 一样,String str = "i"; 和 String str = new String ("i"); 不一样,前者被 JVM 分配到常量池中,而 new 出来的对象都在堆内存中。
String 的常用方法:
- indexOf (): 返回指定字符的索引
- charAt (): 返回指定索引处的字符
- replace (): 替换
- trim (): 去除两端空白
- split (): 分割字符串的 byte 类型的数组;
- length (): 返回字符串的长度
- toLowerCase (): 小写 -> 大写
- toUpperCase (): 大写 -> 小写
- substring (): 截取
- equals (); 比较
# StringBuffer
上面说了 String 的源码里面,private final char value [];, 所以 String 对象是不可变的,而 StringBuilder 和 StringBuffer 都是继承自 AbstractBuilder 类,在 AbstractBuilder 中也是使用字符数组保存字符串,char [] value, 这两种对象都是可变的,String 中的对象都是不可变的,可以理解为常量,线程安全。
StringBuffer 对方法加了同步锁或者对调用方法加了同步锁,所以线程是安全的,StringBuffer 每次都会对 StringBuffer 对象本身操作。
# StringBuilder
线程不安全,性能比 StringBuffer 获得了 10%-15% 提升。
性能:String<StringBuffer<StringBuilder
# 问题:
# 1.HashMap 用 String 作为 key 有什么好处?
HashMap 内部通过 key 的 hashcode 来确定 value 的存储位置,因为字符串不可变的,所以创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,相对于其他对象更快;