欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JDK源码学习笔记——String

程序员文章站 2022-03-26 09:09:00
1、学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2、hashCode 为什么是31? (1)计算hashcode值一般选质数 (2)太小的数计算的hashcode值冲突率高,太大的数乘法计算会溢出int范围 (3)有以上两点和实验得 出:31, 33, ......

1、学习jdk源码,从以下几个方面入手:

  类定义(继承,实现接口等)

  全局变量

  方法

  内部类

2、hashcode

  private int hash;   
  public int hashcode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h;   }

 

为什么是31?

(1)计算hashcode值一般选质数

(2)太小的数计算的hashcode值冲突率高,太大的数乘法计算会溢出int范围

(3)有以上两点和实验得 出:31, 33, 37, 39 ,41 作为乘子比较合适

(4)这几个数字中31的乘法运算可以被优化:31 * i == (i << 5) - i

3、构造两种:

  (1)直接将otherstr引用给this

  (2)数组copy

4、string对“+”的支持

public static void main(string[] args) {
    string s1="a" + "b";// 编译之后 string s1 = "ab";
    string s = "a";
    string s2= s+ "b";// 编译之后 string s = (new stringbuilder(string.valueof(s))).append("b").tostring();
}

5、jdk1.7修改substring()

// jdk1.6
string(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}
// substring方法部分
return ((beginindex == 0) && (endindex == count)) ? this : new string(offset + beginindex, endindex - beginindex, value);


// jdk1.7
public string(char value[], int offset, int count) {
    .
    .
    .
    this.value = arrays.copyofrange(value, offset, offset+count);
}
// substring方法部分
return ((beginindex == 0) && (endindex == value.length)) ? this : new string(value, beginindex, sublen);

jdk1.6的substring:

(1)直接将引用赋值,性能好,共享内部数组节约内存

(2)由于原string的value是private final,可以保证安全性

(3)可能导致内存泄漏

string alongstring = "...a very long string..."; // 很长
string apart = data.substring(2, 4);
return apart;

假设从一个很长的字符串中提取一小段内容:

当alongstring不再使用,apart继续使用时,

alongstring被回收,alongstring的value还被apart的value引用,不能被回收

导致内存泄漏

6、编程技巧学习

/**
 * 先比较是否同一个对象
 * 先比较长度
 * 虽然代码写的内容比较多,但是可以很大程度上提高比较的效率
 */
public boolean equals(object anobject) {
    if (this == anobject) {// 先比较是否同一个对象
        return true;
    }
    if (anobject instanceof string) {
        string anotherstring = (string)anobject;
        int n = value.length;
        if (n == anotherstring.value.length) {// 先比较长度
            char v1[] = value;
            char v2[] = anotherstring.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

 

static int indexof(char[] source, int sourceoffset, int sourcecount, char[] target, int targetoffset, int targetcount, int fromindex) {
    if (fromindex >= sourcecount) {
        return (targetcount == 0 ? sourcecount : -1);
    }
    if (fromindex < 0) {
        fromindex = 0;
    }
    if (targetcount == 0) {
        return fromindex;
    }

    char first = target[targetoffset];
    int max = sourceoffset + (sourcecount - targetcount);

    for (int i = sourceoffset + fromindex; i <= max; i++) {
        /* look for first character. 先比较第一个字符*/
        if (source[i] != first) {
            while (++i <= max && source[i] != first);
        }

        /* found first character, now look at the rest of v2 */
        if (i <= max) {
            int j = i + 1;
            int end = j + targetcount - 1;
            for (int k = targetoffset + 1; j < end && source[j]
                    == target[k]; j++, k++);

            if (j == end) {
                /* found whole string. */
                return i - sourceoffset;
            }
        }
    }
    return -1;
}

 

/**
 * 三目运算符代替多个if
 */
public boolean equalsignorecase(string anotherstring) {
    return (this == anotherstring) ? true
            : (anotherstring != null)
            && (anotherstring.value.length == value.length)
            && regionmatches(true, 0, anotherstring, 0, value.length);
}

7、intern()

 (1)string s = new string("abc");创建个几个对象

  类加载时创建"abc"放入常量池  第一个

  执行代码时new string()  第二个

 (2)string存入常量池方式:

   一,直接使用双引号声明出来的string对象,在类加载时会直接存储在常量池中。

   二,如果不是用双引号声明的string对象,可以使用string提供的intern方法。

    intern 方法:

    如果常量池中存在当前字符串, 就会直接返回当前字符串。 如果常量池中没有此字符串, 会将此字符串放入常量池中后,再返回。

 (3)jdk6 和 jdk7 下 intern 的区别

  在 jdk1.2 ~ jdk6 的实现中,hotspot 使用永久代实现方法区

  jdk7+ 移除永久代  字符串常量和类引用被移动到 java heap中

   jdk6 intern:如果常量池中存在当前字符串, 就会直接返回当前字符串。 如果常量池中没有此字符串, 会将此字符串复制一份到方法区,放入方法区中常量池,再返回。

   jdk7 intern:如果常量池中存在当前字符串, 就会直接返回当前字符串。 如果常量池中没有此字符串, 会将此字符串的引用存储一份,放入堆中常量池,再返回。

举例:

public static void main(string[] args) {
    string s = new string("1");
    s.intern();
    string s2 = "1";
    system.out.println(s == s2);// jdk1.6-false jdk1.7-false

    string s3 = new string("1") + new string("1");
    s3.intern();
    string s4 = "11";
    system.out.println(s3 == s4);// jdk1.6-false jdk1.7-true
}

public static void main(string[] args) {
    string s = new string("1");
    string s2 = "1";
    s.intern();
    system.out.println(s == s2);// jdk1.6-false jdk1.7-false

    string s3 = new string("1") + new string("1");
    string s4 = "11";
    s3.intern();
    system.out.println(s3 == s4);// jdk1.6-false jdk1.7-false
}

 

不再过多解释: (注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向)

JDK源码学习笔记——String

 

 JDK源码学习笔记——String

JDK源码学习笔记——String

 

 (4)应用举例:

static final int max = 1000 * 10000;
static final string[] arr = new string[max];

public static void main(string[] args) throws exception {
    integer[] db_data = new integer[10];
    random random = new random(10 * 10000);
    for (int i = 0; i < db_data.length; i++) {
        db_data[i] = random.nextint();
    }
    long t = system.currenttimemillis();
    for (int i = 0; i < max; i++) {
         arr[i] = new string(string.valueof(db_data[i % db_data.length])).intern();
    }

    system.out.println((system.currenttimemillis() - t) + "ms");
    system.gc();
}

 

8、private final char[] value;final--->string的长度是不能改变的

参考 为什么java要把字符串设计成不可变的

参考 java中string类为什么要设计成不可变的

(1)常量池高效,常量池里string对象改变,引用受影响

(2)安全,不可变,只能读不能写,保证线程安全

 

 

 

参考资料:

1、《成神之路-基础篇》java基础知识——string相关

2、科普:为什么 string hashcode 方法选择数字31作为乘子

3、java7为什么要修改substring的实现

4、深入解析string#intern