您的位置:首页 > 编程语言 > Java开发

Java--String源码解析(JDK1.7)

2017-02-16 18:34 393 查看
根据JDK1.7中String的源码,对String类进行了比较深入的学习,整理了一份笔记。

一、定义和构造函数

先看源码:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash; // Default to 0

// 构造空的字符串
public String() {
this.value = new char[0];
}

// 通过字符串构造字符串
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}

// 通过char数组构造字符串
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}


通过源码可以理解到:

1.String类是final类,不可以被继承

2.String通过char[ ]存储,String类和字符串是两个概念

我们平时一般不会区分String字符串 两个概念,其实String指的是一个实例类,而字符串其实就是源码里的那个 char[ ] value 字符数组

3.String类初始化后是不可变得

我们创建的String对象中的字符串是通过private final char value[] 存储的,也就是说String对象创建后就不能进行修改对象中存储的内容,所以说String类是不可变的。

即使对Stri
4000
ng进行substring,concat或者replace操作,原来的字符串都没有受到任何改变,调用方法后其实返回了一个新的字符串,也就是char[ ] value指向了一个新的字符串。这个在下面的字符串常量池中详细讲。

二、String类在内存中的实现

(1)字符串常量池

字符串是我们平时经常使用的,频繁的读写操作消耗了大量的时间和内存空间。所以JVM为了提高性能和减少时间空间开销,在内存中开辟了一段空间作为字符串常量池

每当我们创建一个字符串常量时,JVM会先去字符串常量池检查是否已经存在,如果这个字符串已经存在,就直接返回这个已存在字符串的地址;如果不存在,就在字符串常量池中创建这个字符串并返回其地址。所以在字符串常量池中不会存在两个相同的字符串。

(2)字符串常量和String实例

字符串常量是存储在字符串常量池中的,而String实例是存储在内存堆上的。通过下面的例子可以知道两个区别和关系。

String a = "test";
String b = "test";
String c = new String("test");


这三条语句的具体执行情况如下:

① 在栈区创建变量a,然后在字符串常量池创建“test”,地址返回给a

② 在栈区创建变量b,发现字符串常量池已存在“test”,直接把地址返回给b

③ 在栈区创建变量c,在堆区创建String类的实例,实例中创建char[ ] 变量value,发现字符串常量池已存在“test”,直接把地址返回给value

通过下面的图可以清楚理解String类型数据之间的关系



(3)编译时确定字符串常量

有一些字符串常量在程序编译时就确定了,有一些要等运行时才能确定,通过以下代码,我们来看看他们的区别:

public void test() {
String s1 = "Hello";
String s2 = "World";
String s3 = "HelloWorld"
String s4 = "Hello" + "World";
String s5 = s1 + s2;
String s6 = "Hello" + s2;
String s7 = "Hello" + new String("World");
System.out.println(s3 == s4);  // true
System.out.println(s3 == s5);  // false
System.out.println(s3 == s6);  // false
System.out.println(s3 == s7);  // false
}


s1 , s2 , s3 , s4 在编译时字符串就已经确定了,s5 , s6 , s7 要等运行时字符串才能确定。

s3 == s4 为true,就不用解释了

后面s5, s6, s7结果为false,原因在于str1 + str2 运算这个运算:

① str1 + str2 运算中,如果str1和str2都是字符串常量,在编译时直接就进行了运算。所以s1、s2、s3、s4 在编译时字符串就已经确定了,s5、s6、s7 要等运行时字符串才能确定。

② str1或str2中有一个不是字符串常量时,str1 + str2 通过StringBuilder的toString( )方法返回了一个新的String对象,s5、s6、s7指向堆区中对应的对象。

String s6 = “Hello” + s2; 真正的运行情况如下:

StringBuilder tmp = new StringBuilder(“Hello”);

String s6 = tmp.append(s2).toString( );

三、主要方法及实现

整理了String类的常用方法,及其源码在方法的实现过程中的一些细节,由于源码比较简单就不单独贴出来了

public int length()

public boolean isEmpty()

public char charAt(int index)

如果索引越界,抛异常

public int compareTo(String anotherString)

从左至右逐个比较每个字符,如不相等返回 char1 - char2

大于anotherString时返回正数,相等返回0,小于返回负数

长度不同时,先比较长度相同的位置,若相同时再返回len1 - len2

public int compareToIgnoreCase(String anotherString)

忽略大小写进行比较

通过内部类CaseInsensitiveComparator的compare方法实现

public String substring(int beginIndex)

从索引位置开始到字符串结尾,例如”Hello”.substring(2) 得到”llo”

public String substring(int beginIndex, int endIndex)

返回子字符串中不包括索引位置endIndex的字符

public String replace(char oldChar, char newChar)

public String[] split(String regex)

返回上个该字符出现位置到现在出现位置之间的字符串

“abcaac”.split(‘a’) 得到 {“”,“bc”,“”,“c”}

public String trim()

去除头尾小于等于”空格”的字符

public char[] toCharArray()

public String toLowerCase()

public String toUpperCase()

public int indexOf(int ch)

此处虽然是个INT型变量,当时使用时直接可以写CHAR型变量,如 “HELLO”.indexOf(‘L’);

存在时返回从左至右最早出现的字符索引位置

public int indexOf(int ch, int fromIndex)

存在时返回从左至右最早出现的字符索引位置

public static String valueOf(Object obj)

类方法

obj可以是所有基本数据类型及其数组

public boolean equals(Object anObject)

比较两个字符串的值是否相等

四、常见问题

1.关于String str = new String(“abc”)创建了多少个对象?

这个问题在网上找答案都说是2个对象,那如何理解这2个对象呢?

首先必须弄清楚创建对象的含义,创建是什么时候创建的?然后还要区分开该段代码执行过程类的加载过程是有区别的。在类加载的过程中,在常量池中创建了一个”abc”对象,而在代码执行过程中确实只创建了一个String对象。

2.关于equals和==

(1)对于==,如果作用于基本数据类型的变量(byte,short,char,int,long,float,double,boolean ),则直接比较其存储的”值”是否相等;如果作用于引用类型的变量(String),则比较的是所指向的对象的地址(即是否指向同一个对象)。

(2)equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。在Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象

(3)对于equals方法,注意:equals方法不能作用于基本数据类型的变量。如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;而String类对equals方法进行了重写,用来比较指向的字符串对象所存储的字符串是否相等。其他的一些类诸如Double,Date,Integer等,都对equals方法进行了重写用来比较指向的对象所存储的内容是否相等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  string java 源码