String类解析

来源:互联网 发布:查九牧卫浴型号软件 编辑:程序博客网 时间:2024/06/05 19:12

String类实现了Serializable, Comparable, CharSequence接口。

String类

String类被final所修饰,也就是说String对象是不可变类,是线程安全的

String类源码

1. 成员变量

String类中包含一个不可变的char数组用来存放字符串,一个int型的变量hash用来存放计算后的哈希值。

//用于存储字符串private final char value[];//缓存String的hash值private int hash; // Default to 0/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = -6849794470754667710L;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. String构造函数
//不含参数的构造函数,一般没什么用,因为value是不可变量public String() {    this.value = new char[0];}//参数为String类型public String(String original) {    this.value = original.value;    this.hash = original.hash;}//参数为char数组,使用java.utils包中的Arrays类复制public String(char value[]) {    this.value = Arrays.copyOf(value, value.length);}//从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到valuepublic String(byte bytes[], int offset, int length, String charsetName)        throws UnsupportedEncodingException {    if (charsetName == null)        throw new NullPointerException("charsetName");    checkBounds(bytes, offset, length);    this.value = StringCoding.decode(charsetName, bytes, offset, length);}//调用public String(byte bytes[], int offset, int length, String charsetName)构造函数public String(byte bytes[], String charsetName)        throws UnsupportedEncodingException {    this(bytes, 0, bytes.length, charsetName);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

关于不可变类

1. 什么是不可变类

所谓不可变类,就是创建该类的实例后,该实例的属性是不可改变的,Java提供的包装类和java.lang.String类都是不可变类。当创建它们的实例后,其实例的属性是不可改变的。

需要注意的是,对于如下代码

String s="abc";s="def";
  • 1
  • 2
  • 1
  • 2

你可能会感到疑惑,不是说String是不可变类吗,这怎么可以改变呢,平常我也是这样用的啊。请注意,s是字符串对象的”abc”引用,即引用是可以变化的,跟对象实例的属性变化没有什么关系,这点请注意区分。

2.String类被设计成不可变的原因

  1. 字符串常量池的需要

字符串常量池(String pool, String intern pool, String保留池) 是Java方法区中一个特殊的存储区域, 当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。 
如下面的代码所示,将会在堆内存中只创建一个实际String对象. 
代码如下:

String s1 = "abcd"; String s2 = "abcd"; 
  • 1
  • 2
  • 1
  • 2

假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象. 严格来说,这种常量池的思想,是一种优化手段.

String s1= "ab" + "cd"; String s2= "abc" + "d"; 
  • 1
  • 2
  • 1
  • 2

也许这个问题违反新手的直觉, 但是考虑到现代编译器会进行常规的优化, 所以他们都会指向常量池中的同一个对象. 或者,你可以用 jd-gui 之类的工具查看一下编译后的class文件. 
2. 允许String对象缓存HashCode

Java中String对象的哈希码被频繁地使用, 比如在hashMap 等容器中。

字符串不变性保证了hash码的唯一性,因此可以放心地进行缓存.这也是一种性能优化手段,意味着不必每次都去计算新的哈希码. 
3. 安全性

String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。 
假如有如下的代码:

boolean connect(string s){    if (!isSecure(s)) {throw new SecurityException();}    // 如果在其他地方可以修改String,那么此处就会引起各种预料不到的问题/错误    causeProblem(s);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4. 线程安全 
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。

总体来说, String不可变的原因包括 设计考虑,效率优化问题,以及安全性这三大方面. 

 如何实现一个不可变类

既然不可变类有这么多优势,那么我们借鉴String类的设计,自己实现一个不可变类。 
不可变类的设计通常要遵循以下几个原则:

  1. 将类声明为final,所以它不能被继承。
  2. 将所有的成员声明为私有的,这样就不允许直接访问这些成员。
  3. 对变量不要提供setter方法。
  4. 将所有可变的成员声明为final,这样只能对它们赋值一次。
  5. 通过构造器初始化所有成员,进行深拷贝(deep copy)。
  6. 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝
原创粉丝点击