目录
1、JDK1.8中String类的源码定义
1.1、主要的类变量如下所示:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
//存储字符串的字符数组
private final char value[];
//字符串的hash值 用来标记字符的唯一性
private int hash; // Default to 0
}
从源码中可以看到:
- String类是由final关键字修饰的,因此String类相当于一个常量,所以String类是线程安全的
- String类有两个个类变量,类变量如下
<1>value[] 字符数组:表示字符串的本质是一个字符数组
<2>hash码:标记字符串的唯一性。
总结:String对象一旦被创建就不能被改变,因此对String对象的任何改变操作都不会影响当前String对象,但是会创建一个新的String对象。
2、String类对象在JVM内存的存储方式
2.1、String的创建方式
String的创建方式有两种,分别如下:
- String a = “stringTest”;
- String b = new String(“stringTest”);
2.2、字符串常量池解析
字符串常量池是JVM为了实例化字符串对象做的优化,旨在提高JVM的性能和内存的开销。字符串常量池的原理如下:每当我们新建一个字符串对象时,JVM都会去常量池中检查该字符串是否存在于常量池中,如果存在则直接返回常量池中的引用,如果不存在常量池中,则实例化该字符串对象放在常量池中。因为每一个字符串都是惟一的,所以字符串常量池中一定不会存在两个相同的字符串。
2.3 字符串在JVM中的存储方式
先看一下下面这个程序,然后分析每一个语句:
public class StringCode {
public static void main(String[] args) {
String A = "stringTest";
String B = "stringTest";
String C = "string" + "Test";
String D = new String("stringTest");
}
}
- 对于实例变量A: 因为字符串"stringTest"对象是第一次创建的,所以JVM在常量池(存在于方法区)中肯定找不到该字符串,所以JVM会在常量池中实例化一个"stringTest"对象,然后将"stringTest"对象的引用返回给实例变量"A";
- 对于实例变量B: JVM会先去常量池中检查字符串"stringTest"是否存在,因为实例变量a已经创建了"stringTest"对象,所以这一次JVM不会再创建"stringTest"实例了,而是直接返回stringTest"对象的引用给实例变量"B";
- 对于实例变量C:因为在编译阶段,会将String C = “string” + “Test”; 变成 String C = “stringTest”; 进行处理,所以创建过程和实例变量B的创建过程一致。
- 对于实例变量D:因为new关键字一定会创建一个新的对象,但是这个对象却是存在于堆中,但是我们前面说了JAVA中String对象是唯一的,所以在堆中的这个对象实际上也是指向了常量池中的"stringTest"。
对象的依赖关系如下图所示:
3、关于String对象的比较(equals和==)
3.1、equals方法和==原理简介
- equals方法是Object类的方法,在Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。但是String类重写了equals方法,是用来比较所指向的对象的值是否相等。
- 对于==,如果比较的是基础数据类型(int,long,float,double,byte,short,char,boolean ),则直接比较它们的值就好了。如果比较的是引用类型的变量,则比较的是它们所引用的地址是否相同,即是否指向同一个对象。
3.2、equals方法和==的比较
通过以下代码,看一看equals方法和==方法区别:
public class StringCode {
public static void main(String[] args) {
String a = "stringTest";
String b = "stringTest";
String c = new String("stringTest");
String d = new String(a);
//ab结果为true
Boolean ab = a.equals(b);
//ab2结果为true
Boolean ab2 = a == b;
//ac结果为true
Boolean ac = a.equals(c);
//ac2结果为false
Boolean ac2 = a == c;
//ad结果为true
Boolean ad = a.equals(d);
//ad2结果为false
Boolean ad2 = a == d;
//cd结果为true
Boolean cd = c.equals(d);
//cd2结果为false
Boolean cd2 = c == d;
}
}
如果理解了以上的内容,相信很容易得出程序中的结果的。现在详细说一下原因:
- 对于ab = a.equals(b)和ab2 = a == b的结果为true,很容易理解,因为a和b指向的是同一个对象,所以a和b的引用相同,值也相同。
- 对于ac = a.equals©结果为true和ac2 = a == c的结果为false,因为c = new String(“stringTest”),所以c肯定在堆中是新建一个实例对象了,所以a和c的值相同,但是a和c的引用就不是同一个引用啦。、
- 对于ad = a.equals(d)结果为true和ad2 = a == d的结果为false,原理同ac的分析一致。
- 对于cd = c.equals(d)的结果为true和cd2 = c == d的结果为false,因为c和d都是new出来的一个新对象,所以cd2的值肯定时false,但是new出来的对象的值都是"stringTest",所以cd的值为true。
如侵权,请告知,立删!
欢迎各位关注我的JAVAERS公众号,陪你一起学习,一起成长,一起分享JAVA路上的诗和远方。在公众号里面都是JAVA这个世界的朋友,公众号每天会有技术类文章,面经干货,也有进阶架构的电子书籍,如Spring实战、SpringBoot实战、高性能MySQL、深入理解JVM、RabbitMQ实战、Redis设计与实现等等一些高质量书籍,关注公众号即可领取哦。