闲暇之余阅读 jdk 源码。

 

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

(一)核心属性

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

    /** Cache the hash code for the string */
    private int hash; // Default to 0

String的核心结构,char型数组与 int 型 hash值。

 

(二)构造器

构造器方面,由于上述两个值是不可更改的,所以直接 对 String 构造,其实是没有用的,只不过是加上了一个引用。

    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

对 char 类型数组使用构造方法时,则会借用调用相关的复制数组的操作,对String的底层抽象,字符数组进行赋值。

    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

//
System.arraycopy(original, 0, copy, 0, len); //一个native方法,用于复制数组。
  

 

(三)方法内常用边界判断

if ((ooffset < 0) || (toffset < 0)
                || (toffset > (long)value.length - len)
                || (ooffset > (long)other.value.length - len)) {
            return false;
        }

连续的 ‘或’ 判断,采用逆向思维对边界判断。

 即判断当前字符串长度的长度减去开始位置后,与参数字符串的长度进行比较,判断边界条件。 

 

(四)Continue 或 break 的少用用法

startSearchForLastChar:
        while (true) {
            while (i >= min && source[i] != strLastChar) {
                i--;
            }
            if (i < min) {
                return -1;
            }
            int j = i - 1;
            int start = j - (targetCount - 1);
            int k = strLastIndex - 1;

            while (j > start) {
                if (source[j--] != target[k--]) {
                    i--;
                    continue startSearchForLastChar;
                }
            }
            return start - sourceOffset + 1;
        }

设置好需要跳转的点,条件符合后进行跳转。

 

(五)代码性能提升————减少getField操作。

public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }
}

value是String中的全局字符数组,在方法内部使用局部变量,得到字符数组的引用,可以有效地减少getField操作。从而提高代码运行效率。

参考:https://blog.csdn.net/gaopu12345/article/details/52084218

 

(六)intern()方法

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned.

Otherwise, this  String object is added to the pool and a reference to this code String object is returned.

intern方法用于减少String对象引用,实质上是减少String池中的引用数量。

参考:https://www.cnblogs.com/paddix/p/5326863.html

http://blog.csdn.net/seu_calvin/article/details/52291082

由于jvm在1.7后将常量池放到堆空间中进行统一管理,直接对此方法产生本质上的变化。即堆中的对象可见字符串常量池中的常量。

接下来用简单易懂的话说明intern方法在1.7之后是如何工作的。

例子在参考链接中说明十分详细,此处只对具体情况和具体过程进行总结。

首先要说明的是。

1.字符串的存储分为两个地方,分别为常量池与堆中的字符串对象。在1.7之前,常量池放在方法区,而字符串对象放在堆中,要想两个区域内有表示相同的字符串需要对两个区域分别赋值。1.7后由于常量池放在了堆中,导致了在new 一个字符串常量的时候,会直接生成字符串对象与字符串常量,此时的地址肯定是不同的。

2.final关键字修饰的字符串,在编译后将变量直接用常量表示。与在代码中直接写常量没有差别。

3.字符串的拼接实际上是使用的StringBuilder的append方法产生的新对象。与直接字符串常量拼接并不相同。

目前总结为以上三点。

intern方法具体如下:

当字符串使用intern方法时,字符串会去字符串常量池中寻找此字符串常量,如果找到就返回字符串常量的引用。如果没找到,就将字符串常量池中存放当前字符串对象的引用。

这种情况主要见于多个字符串对象拼接,拼接时就不会在字符串常量池中对新拼接的字符串值赋值到常量池,如下:

String str2 = new String("Jun")+new String("pb");
str2.intern();
String str1 = "Junpb";
System.out.println(str2==str1);

返回true,这是由于在intern时,常量池中没有Junpb这个字符串常量,就直接在常量中放入了str2的引用,即str1获取的是str2的引用。即为true。

耶!!! 上述都是自己的理解,如有错误,希望指正。本博文主要用于记录计算机之路的脚印。

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄