s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?

今天和同学一起讨论==和equlas区别到底是啥,一本正经给他分析了一下,结果呢idea一运行发现自己是错的····。当然本篇文章不是主要讲它们区别。我们还是用code代码带我们了解底层String在jvm中生成过程。

目录

  • 引子
    • hashCode
    • 字符串比较
  • 常量池
  • 字符串常量池
    • Talk is cheap,show me your code
      • Test1
      • Test2
      • Test3
      • Test3
      • more
  • 结语

引子

今天我们就从下方这一段代码来引出我们要探究的问题

public void test3(){
        String s1 = "11";
        String s2 = "11";
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
}

起因就是这串代码,问输出结果hashCode相等不,下面两个是真是假呢。那小编问问大家你们知道吗?

结果: 1568 1568 true true,是不是觉得很简单,和你想的一样呢?

hashCode

首先我先分析hashCode()为啥相同吧!这个其实很简单,大家要是了解过哈希表的话,应该知道哈希表的key其实就是通过hash函数计算出了,至于这个计算公式我们可以自己覆盖hashCode()这个函数进行自己定义。

要是不了解大概的哈希表的话可以看一下我之前总结的博客:程序员必备:8种通用数据结构(总结)

关于hashCode函数我们也可以到String类中直接看源码,一目了之。

//String 类中的属性
/** The value is used for character storage. */
    private final char value[];
/** Cache the hash code for the string */
    private int hash; // Default to 0
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;
    }

从代码中我们可以看出String的字符串其实存放在char数组中,然后只要字符串值相同,那计算出来的h值肯定是相同的。(为什么讲这个,后面会牵扯到字符串常量池在jvm中生成形式)

字符串比较

分析了hashCode值,我们接着分析,为什么s1 == s2,和s1.equlas(s2)为啥都是true呢?
关于equlas方法比较我觉的还是直接看源码吧,清晰明了:

    public boolean equals(Object anObject) {
        if (this == anObject) {//比较是不是同一个对象
            return true;
        }
        if (anObject instanceof String) {//判断是不是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;
    }

是吧,看完源码之后equals比较的不是对象而是字符串值而已,所以true就理所当然。
关于第一个s1==s2;举个其他的例子吧。

Object a = new Object();
Object b = new Object();
System.out.println(a == a);
System.out.println(a == b);
这个答案就没有异议了吧,true和false.

那你有没有疑问s1和s2不是两个的对象吗?怎么也是true。
有这个问题证明你已经上道哈,嘿嘿这就要看我们jvm底层实现你就会明白了。

常量池

准备一下知识,了解一下什么是常量池。
其实常量池分为三种:

  1. class常量池(本地磁盘,静态的)
  2. 运行时常量池(JVM的方法区)
  3. 字符串常量池(JVM的堆区)
    说到class常量池也就是final修饰的一些常量,在编译时候就已经生成在本地,没运行就存在故称为静态,运行时常量池也就是jvm运行java类所产生的常量(如 基本数据类型创建;int,float等)。由于字符串创建使用频率高,所以jdk版本把之与运行时常量池分离,单独创建一个常量池放在堆区。

字符串常量池

再讲字符串常量池在jvm中的运行,还需要了解java对象怎么在jvm中存储的,我们才能通过HSDB去跟踪。
之前在上一篇文章讲过java类在jvm存储形式,今天就在讲一下 java对象在jvm存储形式。
class是java的类(java代码)
InstanceKlass就是jave类在jvm中的存在形式(c++代码)

java中new一个新对象(java代码)
InstanceOop就是jave对象在jvm中的存在形式(c++代码)

不知道 什么是对象或者是类的话自行百度吧!
还有有人如果疑问为什么存在jvm中就换个形式,直接class保存进去不行吗?
非常好的问题,但是你先百度找找原因吧,这里就不阐述了。

言归正传继续介绍字符串常量池,我们称呼字符常量池在JVM中对应的类是StringTable,底层实现是一个hashtable。
哈希表的key实现是通过String的value计算出来的,而哈希表的value则是将Java的String类的实例instanceOopDesc封装成HashtableEntry。

言归正传继续介绍字符串常量池,在java中如果你创建一个String,那么大多情况都会保存在字符串常量池中,接下来我就用代码和图片的形式给大家讲解吧。

Talk is cheap,show me your code

Test1

String s1 = "1";

这样一串代码到底在JVM中创建几个String对象(InstanceOop)呢?
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第1张图片
另外附加一个idea查看memory的图:
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第2张图片
发现内存中其实就是只有一个String至于那个char[]细心地同学应该知道,它就是String中自带的属性。(之后就不分析那么详细了)
从图上可以看出过程:

  1. 首先去字符串常量去找"1",如果找到直接返回String对象
  2. 没找到(图上就是没找到),需要创建一个String对象,char[]数组对象
  3. 将这个String对象的InstanceOopDec封装成HashTableEntry,作为StringTable的value进行存储

Test2

String s1 = "1";
String s2 = "1";

这样一串代码到底在JVM中创建几个String对象(InstanceOop)呢?
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第3张图片
字符串s2在解析保存时候在字符串常量池直接找到了,就会把里面的HashTableEntry在变成String返回给s2指向,所以s1和s2在底层的JVM中其实就是一个String,那么s1==s2也就理所当然了。
用idea再证明一下吧
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第4张图片
可以看到在没创建s1时候内存中char[]只有2605,String只有2243
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第5张图片
运行了s1之后
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第6张图片
所有运行结束了,还是String都只是+1,这也能证明s1和s2在jvm中指向的是一个对象。

Test3

当然前面的都只是开胃菜,接下来,来点难的。

String s1 =new String("1");

这样一串代码到底在JVM中创建几个String对象(InstanceOop)呢?
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第7张图片
这个与之Test1相比,它不仅仅需要把字符串"1"存进字符串常量池,还多new了一个String对象。所以它在jvm中生成2个String对象。

Test3

当然前面的都只是开胃菜,接下来,来点难的。

String s1 =new String("1");
String s2 =new String("1");
System.out.println(s1 == s2);

这样一串代码到底在JVM中创建几个String对象(InstanceOop)呢?输出的结果又是什么呢?
这样我先说答案大家可以对比一下你们的思考是否正确。在jvm中创建了3个String对象(InstanceOop),输出结果是false。大吃一惊吗???

s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第8张图片
来看看一怎么创建的吧
s1=‘1‘,s2=‘1‘;s1==s2结果为true还是false?你真的知道原理吗?_第9张图片
首先创建s1没什么疑问了,接着就是s2它们两个都需要从常量池直接去找,如果有就不在将字符串存到常量池中,很明显s1创建结束,s2就不会将字符串再次存入常量池中了,但是s2仍然会new一个String,这也是它们为啥最后不相同原因所在,s1和s2在jvm中指向的不是一个String对象。

more

再给大家多举几个例子,但是不在画图和演示了,太累了!希望大家体谅

String s1 =1;
String s2 =new String("1");
System.out.println(s1 == s2);

结果:2个String对象,false

String s1 =1;
String s2 =new String("1");
s2.intern();
System.out.println(s1 == s2);

结果:true
intern
1、去常量池中找字符串,有,直接返回
2、没有,就会String对应的InstanceOopDesc封装成HashtableEntry,存储

结语

其实最后感觉还有好多东西,好多情况没讲,那就留到之后我再更新吧。其实这次主要给大家分析String在jvm中的如何形成的。大家如果掌握这个知识,你就是随便String,随便new都影响不了你判断是否相等。好了这次分享就到这里咯。
感谢大家的观看,有什么错误之处欢迎大家指出来。

你可能感兴趣的:(java,String,java,字符串,jvm,hashtable,hash)