字符串常量池以及String.intern()方法

一、字符串常量池

JVM的运行时内存可以分为堆、方法区、程序计数器、虚拟机栈和本地方法栈。
在这里插入图片描述
而在方法区中有一个字符串常量池,用来保存字符串这个不可变量。如果我们使用String str=new String(“java虚拟机”)来new一个string对象,则该对象的实例保存在堆中。

如果我们使用String str="java虚拟机"来创建一个字符串,jvm首先会在字符串常量池中创建该String的实例,然后将常量池中该实例的引用返回给str。

new出来的String保存在堆中,如果我们想让字符串常量池中也保存该string的实例呢?可以使用String.intern()这个方法将字符串复制到常量池中,返回在常量池中的引用。

二、intern方法在1.7和1.6及之前差异

字面量:指的是String的值,两个字符串equals为true,字面量相同

jdk1.6及之前
如果常量池存在该字面量的字符串,返回这个常量池的对象引用
常量池不存在这个字面量的字符串,a.intern()会常量池创建字面量一样的字符串,返回常量池(新建)的对象引用
jdk1.7及之后
如果常量池存在该字面量的字符串,返回这个常量池的对象的引用(同1.6)
如常量池不存在字面量的对象,在常量池中记录首次出现的实例引用。调用intern()返回这个引用。

三、代码验证

3.1 StringTest1

package com.web;

public class StringTest1 {
    public static void main(String[] args) {
        String s1=new String("123");
        String internS1 = s1.intern();
        String poolsS1 = "123";
        System.out.println("s1堆上字符串 与 internS1的字符串:"+(s1 == internS1));
        System.out.println("s1常量池字符串 与 internS1的字符串:"+(poolsS1 == internS1));
        System.out.println("s1常量池字符串 与 s1堆上字符串:"+(poolsS1 == s1));

        System.out.println("--------------------------------------------------------");

        String s2=new String("zhangsan")+new String("feng");//执行完这行代码后,常量池中会有"java"和"虚拟机",但是不会有"java虚拟机"。
        String internS2 = s2.intern();
        String poolsS2 = "zhangsanfeng";
        System.out.println("s2堆上字符串 与 internS2的字符串:"+(s2 == internS2));
        System.out.println("s2常量池字符串 与 internS2的字符串:"+(poolsS2 == internS2));
        System.out.println("s2常量池字符串 与 s2堆上字符串:"+(poolsS2 == s2));
    }
}

3.2 执行结果

在这里插入图片描述

3.3 具体分析(都是1.8上运行的)

对于s1
String s1 = new String(“123”);堆上创建123字符串,常量池创建123字符串对象。(第一次运行,之前常量池是空的,没有123字符串)

String internS1 = s1.intern();intern()方法找到常量池上“123”字面量的字符串,返回这个引用
String poolsS1 = “123”;poolsS1 指向常量池创建的123字符串
poolsS1 == internS1 != S1
对于s2
String s2 = new String(“zhangsan”) + new String(“feng”);常量池有"zhangsan"和“feng”字符串对象,堆上“zhangsan”字符串对象。

String internS2 = s2.intern();intern()方法在常量池中记录首次出现的实例(堆上)的引用,返回第一次hello的字符串
poolsS2 == internS2 == S2

3.4 StringTest2(都是1.8上运行的)

public class StringTest2 {
	public static void main(String[] args) {
		String str1 = new StringBuilder("ali").append("baba").toString();
		System.out.println(str1 == str1.intern());

		String str2 = new StringBuilder("ja").append("va").toString();
		System.out.println(str2 == str2.intern());
	}
}

3.5结果

在这里插入图片描述

3.6 分析

jdk1.7之后,字符串常量池转移到堆中,则只需要在常量池中记录一下首次出现的实例引用即可,因此intern()方法返回的引用和由StringBuilder创建的字符串实例就是同一个,所以str1 == str1.intern()为true。
但是"java"这个字符串在执行StringBuilder().toString()之前就已经出现过了,不符合intern()"首次遇到"原则,则返回false。

原因:jvm从启动,到执行main里面的第一条代码,要经历很多的,比如加载rt.jar里面所有的Class,加载一个class肯定要执行static{}中内容,况且rt.jar中的jdk的类里面有很多xxx.startWith(“java”)或者其他用到"java"的代码,jvm启动的时候直接按照常量加载进来了丢到internmap里面了。
在这里插入图片描述
四、方法参数传递机制
Person

package com.example.demo;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Getter
@Setter
public class Person {
    private Integer id;
    private String personName;

    public Person(String personName) {
        this.personName = personName;
    }
}

TestTransferValue

package com.example.demo;

public class TestTransferValue {
    public void changeValue1(int age) {
        age = 30;
    }

    public void changeValue2(Person person) {
        person.setPersonName("XXX");
    }

    public void changeValue3(String str) {
        str = "XXX";
    }

    public static void main(String[] args) {
        final TestTransferValue testTransferValue = new TestTransferValue();
        int age = 20;
        testTransferValue.changeValue1(age);
        System.out.println("age------------" + age);

        final Person person = new Person("abc");
        testTransferValue.changeValue2(person);
        System.out.println("person------" + person.getPersonName());

        String str = "abc";
        testTransferValue.changeValue3(str);
        System.out.println("person------" + str);
    }
}

测试结果
在这里插入图片描述
结果分析
age定义的位置是局部变量,基本数据类型传的是变量的副本,changeValue1不会改变原始变量的值。

person引用数据类型传的是对象的引用,(指向同一个对象),changeValue2会改变变量的值。

String 类型的引用类型存放在字符串常量池中,有则用之,无则创建。
在这里插入图片描述

参考文章
https://www.jianshu.com/p/8966c51e9728
https://www.jianshu.com/p/be66e22f5fc8
https://blog.csdn.net/w1764662543/article/details/95360095
https://www.cnblogs.com/wa1l-E/p/14216386.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值