// 1:编译时就确定了字符串的内容
String strComplier = "A";
// 2:new,只有运行时才能确定字符串的内容
String strNew = new String("A");
Java 程序在运行的时候会维护着一个常量池,编译期生成的各种字面量和符号引用会在类加载后进入方法区的运行时常量池
。对于上述这种实现字符串的方式就可以在编译的时候确定字符串的内容,因此这一行生成的内存结构就如下图。
不严谨的讲:虚拟机栈中的 strComplier 存储的就是 A 在常量池中的地址。
因为使用的 new 的方式,所以这句代码只有运行的时候才能确定字符串的内容。而对于 new 关键字,java 是将对象的实例数据存放堆上的,但是又因 String 常量池的存在,因此实际上在堆上的 String 对象的数据又指向了字符串常量池。
不严谨的讲:虚拟机栈中的 strNew 存储的就是 strNew 这个对象在堆内存的地址,而 strNew 中的字符串数据又指向了常量池中的 A
==
比较两个对象的引用是否相等,也就是说比较两个地址是否相等
//true : 地址相同
String a = "A";
String a1 = "A";
System.out.println(a1 == a); //指向的都是 A 的地址,地址相同,返回的是 true
//false : 地址不同,在堆中是两个不同的对象,虽然指向常量池中的同一个值 A
String b = new String("A");
String b1 = new String("A");
System.out.println(b==b1);//分别指向的是在堆内存上的不同对象的地址,地址不同,返回的是 false
//false : 地址不同
String c = "A";
String c1 = new String("A");
System.out.println(c == c1);//一个指向常量池,一个指向堆,返回 false
//true : 地址相同
String d = "A";
String d1 = d;
System.out.println(d == d1);//把d中的 常量池地址 赋给d1,返回 true
//true : 地址相同
String a = "A1";
String a1 = "A" + 1;//编译时已确定
System.out.println(a == a1);//true
//false : 地址不同
String b = "A1";
String b1 = "A";
String b2 = b1 + 1;//编译时不确定
System.out.println(b == b2);//false
equals
比较的两个对象是否相等,也就是说是同一个对象,在jvm堆内是惟一的
String类对equals()方法进行了重写,只有值相等,才为true
//equals方法源码
public boolean equals(Object anObject) {
//判断是否是相同对象
if (this == anObject) {
return true;
}
//判断anObject是否是String类型
if (anObject instanceof String) {
//
String aString = (String)anObject;
//判断编码是否相同
if (coder() == aString.coder()) {//
//比较两个字符串是否完全相等
return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value);
}
}
return false;
}
String stringCompiler = "A";
String stringNew = new String("A");
StringBuilder stringBuilder = new StringBuilder("A");
StringBuffer stringBuffer = new StringBuffer("A");
//不是相同对象
System.out.println(stringCompiler == stringNew); // false
//不是相同对象,但都是String类型,编码、字符值都相同
System.out.println(stringCompiler.equals(stringNew)); // true
//stringBuilder不是String类型
System.out.println(stringCompiler.equals(stringBuilder)); // false
//stringBuffer不是String类型
System.out.println(stringNew.equals(stringBuffer)); // false
比较 | == | equals() |
---|---|---|
String s1 = new String(“java”); String s2 = new String(“java”); |
false s1、s2在堆中是不同的对象,地址不同 虽然都指向了常量池中的’'java" |
true String类对equals()进行了重写 只要值相同,就返回true |
String s1 = new String(“java”); String s2 = s1; |
true 同一对象,地址相同,值相同 |
true 同一对象,地址相同,值相同 |
String s1 = “java”; String s2 = “java”; |
true 编译时值已确定 指向常量池中同一地址 |
true 值相同 |
//编码
String.getBytes()//方法是得到一个操作系统默认的编码格式的字节数组。
String.getBytes(String decode)//方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示
//解码
new String(byte[] b,String decode)//按照指定的方法编码
//编码解码一致,正确输出
byte[] b_gbk = "中".getBytes("GBK");
String s_gbk = new String(b_gbk,"GBK");
System.out.println(s_gbk);//中
//编码解码不一致,输出乱码
byte[] b_gbk = "中".getBytes("GBK");
String s_utf8 = new String(b_gbk,"UTF-8");
System.out.println(s_utf8);//��
比较 | String | StringBuffer | StringBuilder |
---|---|---|---|
继承性 | final | final | final |
实现接口 | Serializable Comparable CarSequence |
Serializable CarSequence |
Serializable CarSequence |
速度 | 慢 | 中 | 快 |
线程安全性 | 线程安全 (final) | 线程安全(有锁) | 线程不安全 |
常用方法 | equals() | append() insert() |
append() insert() |
适用场景 | 少量的字符串操作 | 多线程下的大量操作 | 单线程下的大量操作 |
为什么 StringBuilder 不是线程安全的?