String类和常量池

String类

String类不会在原有的数据上修改(添加或删除),而是重新开辟一块堆内存放入修改的内容

修改String类不会在原有的数据上修改(添加或删除),因为其内部的char[]是被final修饰的,而是重新开辟一块堆内存放入修改的内容,原有引用重新指向这一块堆内存。应该使用StringBuilder类的append()方法,它会在堆存的原有的数据上修改,不会开辟新的堆内存空间。

+号连接符连接两个s = " A" + “B”,会在编译阶段优化成s = “AB”,连接其他的会创建新的StringBuilder对象append拼接。

那如果用+号连接符修改呢?+号连接符连接两个" "

(如:s = " A" + “B”),则会在编译阶段:java源文件编译成.class文件时,会被优化成s = “AB”,放入常量池,这个可以,但这种灵活度太差,都是写死的。

若连接其他情况(如:int i = 10; s = “abc” + i ), 程序运行时,会创建一个新的StringBuilder对象,append(“abc”).append(String.valueOf(i))之后,在toString()转换为String类返回

/**
 * 测试代码
 */
public class Test {
    public static void main(String[] args) {
        int i = 10;
        String s = "abc";
        System.out.println(s + i);
    }
}

/**
 * 反编译后
 */
public class Test {
    public static void main(String args[]) {    //删除了默认构造函数和字节码
        byte byte0 = 10;      
        String s = "abc";      
        System.out.println((new StringBuilder()).append(s).append(byte0).toString());
    }
}

String s1 = “A”;
String s2 = “B”
s = s1 + s2
也会创建一个StringBuilder对象,编译阶段不会优化。

String类常量池

为了减少内存开销,在创建字符串常量时,会先去字符串常量池中检查是否存有这个字符串常量,如果有,直接返回字符串常量池中的字符串引用(即地址),如果没有,则在字符串常量池中创建一个字符串常量,并返回字符串引用。

JDK7后,常量池被放在了堆内存中,

/**
 * 字符串常量池中的字符串只存在一份!
 * 运行结果为true
 */
String s1 = "hello world!";
String s2 = "hello world!";
System.out.println(s1 == s2);
/**
 * 运行结果为true false
 */
String s1 = "AB";
String s2 = "AB";
String s3 = new String("AB");
System.out.println(s1 == s2);
System.out.println(s1 == s3);

在这里插入图片描述

String对象的intern()方法会首先在字符串常量中找有没有该字符串常量,如果有不在创建新的字符串常量,直接返回字符串常量在字符串常量池中的地址,没有则创建一份,并返回地址值。

static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];

public static void main(String[] args) throws Exception {
    Integer[] DB_DATA = new Integer[10];
    Random random = new Random(10 * 10000);
    for (int i = 0; i < DB_DATA.length; i++) {
        DB_DATA[i] = random.nextInt();
    }
    long t = System.currentTimeMillis();
    for (int i = 0; i < MAX; i++) {
        //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
         arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
    }

    System.out.println((System.currentTimeMillis() - t) + "ms");
    System.gc();
}

arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();使用intern()会减少很多内存,它不会创建1000 * 10000个字符串常量,因为有相同的。
如果不使用arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])),则会创建1000 * 10000个String对象,但注意:Sting()对象也会指向字符串常量池中的字符串常量。

String s = new String(“hello”),创建了几个对象?两个或者一个,若字符串常量"hello"已存在于常量池中,则创建new String()对象;若字符串常量"hello"不存在于常量池中,则会先在常量池中创建字符串常量"hello",再创建new String()对象。

测试题:

 public static void main(String[] args) {
        String s1 = "AB";
        String s2 = new String("AB");
        String s3 = "A";
        String s4 = "B";
        String s5 = "A" + "B";
        String s6 = s3 + s4;
        System.out.println(s1 == s2);
        System.out.println(s1 == s5);
        System.out.println(s1 == s6);
        System.out.println(s1 == s6.intern());
        System.out.println(s2 == s2.intern());
        System.out.println(s2.intern() == s6.intern());
    }

false
true
false
true
false
true

在这里插入图片描述
解析:真正理解此题目需要清楚以下三点
1)直接使用双引号声明出来的String对象会直接存储在常量池中;
2)String对象的intern方法会得到字符串对象在常量池中对应的引用,如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;
3) 字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap -c命令获得class文件对应的JVM字节码指令就可以看出来。

https://blog.csdn.net/ifwinds/article/details/80849184

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值