String的简介
Java中的String类是一个经常使用的,String 类代表字符串。这个字符串属于常量,它们的值在创建之后就不能更改这一点可以从String的成员变量源码中看出。
/** String的属性值 */
private final char value[];
/** The offset is the first index of the storage that is used. */
/**数组被使用的开始位置**/
private final int offset;
/** The count is the number of characters in the String. */
/**String中元素的个数**/
private final int count;
/** Cache the hash code for the string */
/**String类型的hash值**/
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
从这里面还可以看出String底层使用字符数组来进行维护,而且值的类型为final所有无法修改。而且存储String数据也不一定从数组的第0个元素开始的,而是从offset所指的元素开始。
String的几个问题
一、关于字符串常量池
字符串常量池这个问题涉及到一个设计模式,叫“享元模式”,即 共享元素模式 :
也就是说:一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有地方都引用这一个元素
Java中String部分就是根据享元模式设计的,而那个存储元素的地方就叫做“字符串常量池 - String Pool”
String x1 = "hello";
String x2 = new String("hello");
这两句话的执行过程天差地别**,第一句话**会先在字符常量池中查找有没有已经创建过相同的对象,如果有直接使用,一个没有则创建对象。
而第二句话的过程就相对复杂一些,首先**"hello"就会先执行第一句话的过程,然后因为又使用了new**这个关键字又在Java Heap中创建了1个对象,然后调用接收String参数的构造器进行了初始化,最终x2的引用这个String对象。也就是说如果只有
String x2 = new String(“hello”);
这一句话就会创建出两个String对象
二、关于字符串的比较
对于字符串的比较,使用" == "还是"equals"呢?
" == "是直接比较的两个对象的堆内存地址,如果相等,则说明这两个引用实际是指向同一个对象地址的。
“equals” 如果是一个没有重写过的equals,这直接调用Object的equals,此时和" == "没有任何的区别,但String类重写了这个方法会对字符串的内容进行比对
String x1 = "hello";
String x2 = new String("hello");
System.out.println(x1 == x2);//false
System.out.println(x1.equals(x2));//true
以上两个不属于同一个对象,但它们的字符串内容相同,面对两个不同的比较方法就有了这两种结果。
三、关于字符串的"+"运算
String x1 = "hello world";
String x2 = "hello " + "world";
String x3 = "hello ";
String x4 = "world";
String x5 = x3 + x4;
System.out.println(x1 == x2);//true
System.out.println(x5 == x1);//false
从上面的这个代码可以看出,针对字符串常量和变量"+"的运行过程并把相同 :
当为常量时,"hello “和"world"这种在编译时可以确定的叫做常量,常量在进行”+"操作时,会首先在字符串常量池中进行寻找,如果能找到拼接后字符串,编译期间就会进行优化变成String x2 = “hello world”;所以x1 == x2
当为变量时,由于不是已知字面量,是一个不可预料的部分,编译器不会优化,必须等到运行时才可以确定结果。此时的"+"操作会将String转为StringBuilder进行append操作,在通过toString返回一个String,通过return返回的String自然就和前面的String不是同一个对象了