Java中Sring类源码分析学习


(一)关于如何查看Java中String类的源码?

String类源码在JDK文件中的src压缩包下,src-java-lang-String.java。


(二)String类源码分析

2.1 定义

public final class String
    implements java.io.Serializable, Comparable, CharSequence {

可以看到String类是被final修饰的,这就意味着String是不可以被继承,这样String类中的方法是没有机会被覆

盖的,简而言之,String类是不可变类。与此同时,该类实现了三个接口Serializable, Comparable,CharSequence。

2.2 属性

/** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    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
     * 
     * Object Serialization Specification, Section 6.2, "Stream Elements"
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
 /** The value is used for character storage. */
    private final char value[];

这是一个字符数组,并且是final类型,他用于存储字符串内容,从fianl这个关键字中我们可以看出,String的内容

一旦被初始化了是不能被更改的。 虽然有这样的例子: String s = “a”; s = “b” 但是,这并不是对s的修改,而是重新指

向了新的字符串,从这里我们也能知道,String其实就是用char[]实现的。

2.3 为什么字符串要被设计成不可变类

a、如果字符串可变的话,当两个引用指向指向同一个字符串时,对其中一个做修改就会影响另外一个。

b、在HashMap中,字符串的不可变能保证其hashcode永远保持一致,这样就可以避免一些不必要的麻烦。这也就意味着

每次在使用一个字符串的hashcode的时候不用重新计算一次,这样更加高效。

private int hash;//this is used to cache hash code.
以上代码中 hash变量中就保存了一个String对象的hashcode,因为String类不可变,所以一旦对象被创建,该hash值也

无法改变。所以,每次想要使用该对象的hashcode的时候,直接返回即可。
c、安全性方面:
String被广泛的使用在其他Java类中充当参数。比如网络连接、打开文件等操作。如果字符串可变,那么

类似操作可能导致安全问题。因为某个方法在调用连接操作的时候,他认为会连接到某台机器,但是实际上并没有(其他

引用同一String对象的值修改会导致该连接中的字符串内容被修改)。可变的字符串也可能导致反射的安全问题,因为他的

参数也是字符串。

2.4 Java中的equals()和hashcode()的关系

所有Java类的父类——java.lang.Object中定义了两个重要的方法:

public boolean equals(Object obj)
public int hashCode()
下面是一段摘取自网络的代码:

import java.util.HashMap;

public class Apple {
    private String color;

    public Apple(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Apple))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Apple) obj).color);
    }

    public static void main(String[] args) {
        Apple a1 = new Apple("green");
        Apple a2 = new Apple("red");

        //hashMap stores apple type and its quantity
        HashMap m = new HashMap();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Apple("green")));
    }
}
上面的代码执行过程中,先是创建个两个Apple,一个green apple和一个red apple,然后将这来两个apple存储在map中,存储之后再试图通过map的

get方法获取到其中green apple的实例。读者可以试着执行以上代码,数据结果为null。也就是说刚刚通过put方法放到map中的

green apple并没有通过get方法获取到。你可能怀疑是不是green apple并没有被成功的保存到map中,但是,通过debug工具可以

看到,它已经被保存成功了。

造成以上问题的原因其实比较简单,是因为代码中并没有重写hashcode方法。hashcodeequals的约定关系如下:

1、如果两个对象相等,那么他们一定有相同的哈希值(hash code)。

2、如果两个对象的哈希值相等,那么这两个对象有可能相等也有可能不相等。(需要再通过equals来判断)

如果你了解Map的工作原理,那么你一定知道,它是通过把key值进行hash来定位对象的,这样可以提供比线性存储更好的性能。实际上,Map的底层

数据结构就是一个数组的数组(准确的说其实是一个链表+数组)。第一个数组的索引值是key的哈希码。通过这个索引可以定位到第二个数组,第二个

数组通过使用equals方法进行线性搜索的方式来查找对象。

Java中Sring类源码分析学习_第1张图片

其实,一个哈希码可以映射到一个桶(bucket)中,hashcode的作用就是先确定对象是属于哪个桶的。如果多个对象有相同的哈希值,那么他们可以放

在同一个桶中。如果有不同的哈希值,则需要放在不同的桶中。至于同一个桶中的各个对象之前如何区分就需要使用equals方法了。

hashcode方法的默认实现会为每个对象返回一个不同的int类型的值。所以,上面的代码中,第二个apple被创建出来时他将具有不同的哈希值。可以通

过重写hashCode方法来解决。

public int hashCode(){
    return this.color.hashCode();   
}

所以,在判断两个对象是否相等时,不要只使用equals方法判断。还要考虑其哈希码是否相等。尤其是和hashMap等与hash相关的数据结构一起使用时。


(未完待续。。。。。。)

你可能感兴趣的:(Java中Sring类源码分析学习)