1. 微谈String

1. 微谈String

StringJava中属于用得比较多.既然用得多,那我们就必须对他有所了解.

1.概览

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

上面就是String的类定义信息,得:

  • final修饰,表示String类不能被继承.
  • 实现Serializable接口,表示可序列化.
  • Comparable该类支持在集合中排序,而不用传递外部排序器.
  • CharSequence提供了对不同类型的字符数组进行只读访问的方法.
private final char value[];

每一个String在内部都是一个char类型数组.

疑惑点:

  1. String为什么是不可变的?

    字符串在Java中大量被使用,而final有一个很好的特性就是:可以通过将多个重复项指向单个实例来共享他们.final

2.方法

String类其实并没有什么好说的,大部分都是一些工具方法.这边就讲几个常用的:

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;    }

String重写的equals()方法如上所示,先==判断引用,然后instanceof判断是否是同一类型,然后判断字符串长度,最后逐个字符比较.

可能存在疑惑的点:

  1. 为什么用==比较?

    因为String是一个对象,如果要比较的对象地址都一样,那里面的内容自然相等.

  2. 那为什么我们平常不能使用==比较String?

    其实很简单,存在下面这种情况:

     String s = new String("a"); String s1 = new String("a"); System.out.println(s == s1);// false

    就算值是相同的,但是所创建的对象地址不同,使用==自然为false.

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;    } 

上面是重写的hashCode()方法,默认的hashCode值为0,在调用过hashCode()方法后才会赋值.

从该方法看出,String计算hashCode的公式是:循环 string.length31 * hashCode + currentValue的和.

疑惑点:

  1. 为什么乘的是31而不是其他的数?

    主要是31有一个很好的特性:乘法可以被移位和减法代替,以获得更好的性能:31 * i = (i<<5)-i,虚拟机会自动进行这种优化.

public native String intern();

intern()方法也算比较关键的知识点了,涉及到的点也比较多,这里贴一篇写得比较好的文章:String#intern

3.杂谈

String类本身并不复杂,主要是底层所涉及到的字符串常量池一类的.

以前一直在纠结一个问题:

String name = "KChaste Sun"

这种创建String的方式,算不算是一个'String',直到我 看到了String的文档注释:

All string literals in Java programs, such as "abc", are implemented as instances of this class.

关于String还有以下常见问题:

 String hello = "hello"; String hell = "hell"; System.out.println(hello == "hell"+"o");// true System.out.println(hello == hell+"o");// false

造成的主要原因Chapter 3. Lexical Structure (oracle.com):

String literals in different classes in different packages likewise represent references to the same String object.

由常量表达式连接起来的字符串在编译时被计算,直接当作字面量处理.

Strings concatenated from constant expressions are computed at compile time and then treated as if they were literals.

在运行时通过连接计算的字符串是新创建,因此是不同的.

至于为什么这里为用==会相等,原因也很简单,直接使用双引号声明出来的String对象会直接存储在常量池中,如果已经存在,则返回同一引用.

还有一点就是关于String+=,在底层使用的是StringBuilderappend()方法,并且每次+=都会新建一个该实例,并且都会调用toString()方法,该方法会进行数组复制,所以在需要进行大批量字符串拼接的时候,请使用StringBuffer或者StringBuilder(老生常谈的了...).

String不可变其实也就是每次调用String的方法时,返回的都是一个新的String实例.

冷知识: 编译时String最大长度为65535,运行时受Integer.MAX_VALUE影响,这个65535并不是标准的65535这么长,而是根据你内容的编码的字节数来算的Java Virtual Machine Specification.

关于String就写到这里,后续写JVM之类的或许还会提及.

推荐阅读