String的一点内容(基于JDK8)

先贴一段代码:

/**
 * @author admin
 * @date 2021/4/7  20:32
 */
public class Test004 {
    public static void main(String[] args) {
        String str1 = new StringBuilder("hello").append("world").toString();
        String str2 = str1.intern();

        String str3 = new StringBuilder("ja").append("va").toString();
        String str4 = str3.intern();

        String str5 = "java";
        String str6 = str5.intern();
        String str7 = "ja";
        String str8 = "va";
        String str9 = str7 + str8;
        String str10 = "ja" + "va";

        System.out.println(str1 == str2);
        System.out.println(str3 == str4);
        System.out.println(str5 == str6);
        System.out.println(str5 == str3);
        System.out.println(str5 == str4);
        System.out.println(str5 == str9);
        System.out.println(str5 == str10);
    }
}

上面这段代码的输出结果是什么呢?

答案:

① true
② false
③ true
④ false
⑤ true
⑥ false
⑦ true

不知道大家的答案和这样是否一样

首先,先说一下“==”这个运行符号。

1、对于基本数据类型,比较的是值是否相等

2、对于引用数据类型,比较的是在内存中的存放地址。

再说一下intern()这个方法:

intern() 方法返回字符串对象的规范化表示形式。

它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。

具体可以看一下源码中的解释:

解析:

        String str1 = new StringBuilder("hello").append("world").toString();
        String str2 = str1.intern();

        String str3 = new StringBuilder("ja").append("va").toString();
        String str4 = str3.intern();

这几行代码看起来逻辑结构是一样的,只是str1与str2的值都是“helloworld”,str3与str4的值都是“java”,但是为什么经过“==”运算符会出现不一样的结果呢?接下来咱们再来一一分析这个结果。

①结果为true,在new出来一个str1也就是“helloworld”之前,先在堆内存new一块空间,然后去常量池查找是否存在“helloworld”,存在就将常量池中的字符复制到堆内存,然后在栈内存保存这个String对象的地址,不存在就去常量池开辟一个空间去存储“helloworld”.然后后续一样在堆内存保存,栈内存放地址。str2执行intern()方法,是不会再去重新创建“helloworld”这个对象的,而是去常量池找这个字符对象,有的话直接拿出来,没有就将创建好String对象的存入常量池并返回这个“helloworld”的栈内存中的地址。所以说new出来的“helloworld”对象和从常量池中取出来的对象是同一个对象,所以说“==”运算,它的引用地址是一样的,所以返回结果为“true”。

②第二个结果是false,大家可能会想,这个“java”字符串和“helloworld”字符串一样啊,还是先new一个,然后执行intern()方法就不会去创建这个对象了,结果应该还是ture啊,其实并不是这样。

在java程序执行时,在类加载过程中执行system类时,会先去初始化system类,在线程初始化后调用,这里我们看一下它的initializeSystemClass()方法,在这个方法中又调用了sun.misc.Version.init(),我们看一下这个内部结构

我们发现,“java”这个字符串已经被定义过了,因为就是说在执行sun.misc.Version.init()时,已经将“java”这个字符串加载到运行时字符常量池中,所以,我们在new一个str3-“java”字符串后,再去执行intern()方法时,这str4拿到的“java”字符串并不是将str3这个对象存放入常量池,而是从常量池拿到初始化system类时已经创建出来的“java”字符串,所以new出来的“java”和运行时常量池中的“java”不是一个对象,所以str3与str4进行“==”运算时,得到的内存地址是不一样的。所以返回false.

可以看一下initializeSystemClass()这个方法的源码注释:

③str5是直接定义的一个字符串“java”,也就是直接去常量池查找是否存在"java",显然这个在常量池中是存在的,str6是对str5执行intern()方法,还是从常量池中取“java”这个值,所以返回的就是true;

其实"java"这个字符比较特殊因为在初始化system类时,本身已经定义了“java”这个字符串,我们换个字符串“helloworld”,

String str11 = "helloworld";
System.out.println(str11 == str1);

显然这个的结果也是false,一个是new出来的,在栈内存有它的地址,一个是直接定义的,虽然值还是常量池中的值,但是会在栈内存重新开辟一块空间来存放地址。所以引用地址不一样,,这里大家可以看一下String通过字面量赋值和New一个对象赋值两种方式的区别。

④false,str5是直接在常量池中拿到初始化system类时定义的“java”,str3是新创建出来的,地址不一样。

⑤ture,str5是直接在常量池中拿到初始化system类时定义的“java”,str4虽然是对str3执行intern()方法,但是还是从常量池中拿到初始化system类时定义的“java”,

⑥false,str5是直接在常量池中拿到初始化system类时定义的“java”,str9中含有变量,不会进入常量池查找,所以地址不一样。

⑦true,str5是直接在常量池中拿到初始化system类时定义的“java”,str10是字符串相加得到的,因为都是静态字符串的结果会添加到字符串池,所以str10还是一样的java地址。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值