1 String 的不可变性
Java 中的 String 是不可变的,被final修饰,内部字符数组private final char value[]不可修改,任何对 String 的修改操作都会生成一个新的 String 对象。例如,调用 concat()、substring() 或 toUpperCase() 等方法时,会返回一个新对象而非修改原对象。
为什么被设计为不可变?
因为就是需要一个不可变的对象来满足一些场景的使用,比如防止敏感数据(如密码、URL、文件路径)在传递过程中被篡改。例如网络请求参数若使用可变String,可能被恶意代码修改导致安全漏洞。
推荐使用char[]存储密码等敏感数据,因其内容可显式清除,而String因不可变性可能长期驻留内存。
2 String 创建和比较
String 可以通过直接赋值(字面量)或 new 关键字创建。直接赋值会检查字符串常量池,避免重复创建;而 new 会强制在堆中创建新对象。
第一种情况
String str = "111";
str += "222";
str += "333";
System.out.println(str);
返回:true
第二种情况
public static void main(String[] args)
{
String str0 = new String("123");
String str1 = new String("123");
System.out.println(str0 == str1);
}
返回:false
分析原因:
JVM中有三个区域 堆栈,堆,常量池。
str0,str0,存在堆栈中
new String(“123”);堆中
"123"常量池
== 比较的引用
1)String str0 = “123”;编译的时候在常量池创建一个常量,然后str,先去常量池找123,如果有str也指向这个123.所有str0=str1;
2) new String(“123”)每次都会都会在堆内存开辟一块新区域存放123,是两块不同的堆内存,所有false。
总结:== 比较对象地址,equals() 比较内容。对于 String,应优先使用 equals() 以避免逻辑错误。
3 为什么要使用StringBuilder和StringBuffer拼接字符串?
一般用法:
String str = "111";
str += "222";
str += "333";
System.out.println(str);
反编译发现每次+,生成一个StringBuilder,内存浪费,效率低;
不常用
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
意思就是通过两次字符串的拷贝,产生一个新的字符数组buf[],再根据字符数组buf[],new一个新的String对象出来,这意味着concat方法调用N次,将发生N*2次数组拷贝以及new出N个String对象,无论对于时间还是空间都是一种浪费。
根据上面的解读,由于"+"拼接字符串与String的concat方法拼接字符串的低效,我们才需要使用StringBuilder和StringBuffer来拼接字符串。以StringBuilder为例:
StringBuilder sb = new StringBuilder("111");
sb.append("222");
sb.append("111");
sb.append("111");
sb.append("444");
System.out.println(sb.toString());
StringBuffer和StringBuilder原理一样,无非是在底层维护了一个char数组,每次append的时候就往char数组里面放字符而已,在最终sb.toString()的时候,用一个new String()方法把char数组里面的内容都转成String,这样,整个过程中只产生了一个StringBuilder对象与一个String对象,非常节省空间。StringBuilder唯一的性能损耗点在于char数组不够的时候需要进行扩容,扩容需要进行数组拷贝,一定程度上降低了效率。
特殊情况:
String str = “111” + “222” + “333” + “444”;
这种情况只会产生一个stringBuilder,因为111等都是编译期间即可得知的常量。
使用+,可读性差。
4 String 的 intern() 方法
intern() 方法会将字符串添加到常量池(如果尚未存在),并返回池中的引用。可用于减少重复对象。
5 编码问题
String 与字节数组转换时需明确指定字符编码(如 UTF-8),否则可能因平台默认编码导致乱码。
String str = "你好";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8); // 明确指定编码
String decoded = new String(bytes, StandardCharsets.UTF_8);
6 JDK7+允许switch使用String
底层通过hashCode()和equals()实现。
Java String核心问题解析

被折叠的 条评论
为什么被折叠?



