(String)字符串原理详解

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设计与实现等等一些高质量书籍,关注公众号即可领取哦。
在这里插入图片描述

发布了9 篇原创文章 · 获赞 39 · 访问量 544

猜你喜欢

转载自blog.csdn.net/qq_36526036/article/details/104831339