Java-String

关于字符串 推荐阅读源码,或者https://blog.csdn.net/zhxdick/article/details/80803507

以下是几个String的问题,总结一下。

String 被final修饰 不能被继承

final的作用:

  1. ​ 修饰变量 表示变量只能一次赋值以后值不能被修改

    ​ 如果是修饰了一个基本类型数据,就表示一个常量

    ​ 如果是引用类型数据,则引用初始化后将永远指向一个内存地址(就相当于一个不能变的指针)

    ​ 但是该引用对象中的内容是可以变的

  2. ​ 修饰方法 该方法不能被重写,但可以继承

  3. ​ 修饰类 该类不能被继承

final修饰变量的本质: final修饰的变量会指向一块固定的内存, 这块内存中的值不能改变.

参考https://blog.csdn.net/qq_24309787/article/details/100942044

那为何String要被修饰成final?

为了实现字符串常量池:字符串常量池可以在程序运行时节约很多内存空间,因为不同的字符串变量指向相同的字面量时,都是指向字符串常量池中的同一个对象。这样一方面能够节约内存,另一方面也提升了性能。

但是仅有一个final不能保证字符串常量,原因在上面final作用第一条已说明。我们看String源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
//String类被final修饰了,表明其不可被继承。
//实现了 Serializable接口--->使其可以序列化,方便数据的传输
//实现了Comparable接口,可以调用Collections.sort() 和 Arrays.sort() 方法排序,并且String类实现 compareTo() 方法。
//实现了CharSequence接口,该接口能表示char值的一个可读序列。几个String兄弟类都实现了此接口。

@Stable
private final byte[] value;
private final byte coder;

private int hash; // Default to 0

...
}

其中的final修饰的byte数组用来存储String内容,然后再加上private修饰,本身也没有提供修改自己value数组的方法,所以value数组不可变,即字符串不可变,生成了字符串常量池。

还有因为其不可变所以String是线程安全的,同一个字符串实例可以被多个线程共享。

因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。

​ 参考https://blog.csdn.net/zhxdick/article/details/80803507

https://www.jianshu.com/p/9c7f5daac283

JDK8 和JDK9 String的变化

​ 再JDK8中 String中是用char数组来存储 char是由UTF16编码咱用两个字节,但是很多都只用了一个字节(ASICII),所有再9中对其进行了优化,将char[] 更改为了 byte [] 以及一个coder来存储。

​ coder一般只有两个值:分别用来表示8位表示还剩16位表示

1
2
static final byte LATIN1 = 0; 
static final byte UTF16 = 1;

关于== 和equal()

“==”判断的是两个对象的内存地址是否一样,适用于原始数据类型和枚举类型(它们的变量存储的是值本身,而引用类型变量存储的是引用);equals是Object类的方法,Object对它的实现是比较内存地址,我们可以重写这个方法来自定义“相等”这个概念。比如类库中的String、Date等类就对这个方法进行了重写。
综上,对于枚举类型和原始数据类型的相等性比较,应该使用”==”;对于引用类型的相等性比较,应该使用equals方法。

下面是String的equal函数,equal()函数的object的一个函数,String对其进行了重写,首先它判断了两者内存地址是否一样,然后将anObject转换为String比较内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
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;
}