Java String之你不知道的事

0.String 类

字符串广泛应用 在Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

本文讲述的String类基于jdk1.8
复制代码

1.直接赋值与new String()区别

在平时代码中,常常看到String的两种声明赋值方式,但是其中又包含着怎样的猫腻呢?两种方式又有着怎样的区别呢?请看下面代码示例:

public class TestString {

    public static void main(String[] args) {
        String s1 = new String("hh");
        String s2 = new String("hh");
        String s3 = "hh";
        String s4 = "hh";
        System.out.println(s1==s2);
        System.out.println(s1==s3);
        System.out.println(s3==s4);
    }
}
复制代码

输出结果如下:

false
false
true
复制代码

我们先来看一下代码涉及的内存模型图:

我们来逐行分析下:

1.String s1 = new String("hh");在编译时期,“hh”被编译到.class文件中的Constant pool中;类加载时期,判断字符串常量池是否存在“hh”,此时不存在,则从Constant pool取出“hh”存入字符串常量池。运行时期,在栈中创建s1引用,在堆中创建对象,对象内容为“hh”,s1指向堆中的地址1。

2.String s2 = new String("hh");在编译时期,“hh”被编译到.class文件中的Constant pool中;类加载时期,判断字符串常量池是否存在“hh”,此时存在,则不存入字符串常量池。运行时期,在栈中创建s2引用,在堆中创建对象,对象内容为“hh”,s2指向堆中的地址2。

3.String s3 = "hh";在编译时期,“hh”被编译到.class文件中的Constant pool中;类加载时期,判断字符串常量池是否存在“hh”,此时存在,则不存入字符串常量池。运行时期,在栈中创建s3引用,s3指向字符串常量池中的“hh”。

4.String s4 = "hh";在编译时期,“hh”被编译到.class文件中的Constant pool中;类加载时期,判断字符串常量池是否存在“hh”,此时存在,则不存入字符串常量池。运行时期,在栈中创建s4引用,s4指向字符串常量池中的“hh”。

此时我们再来分析下结果:

1.System.out.println(s1==s2);因为“==”判断引用的地址是否相同,s1与s2在堆中的引用地址不相同,因此为false。

2.System.out.println(s1==s3);s1的引用地址指向堆中地址1,而s3的引用地址指向字符串常量池中的“hh”,因此为false。

3.System.out.println(s3==s4);s3的引用地址指向字符串常量池中的“hh”,s4的引用地址指向字符串常量池中的“hh”,因此为true。

**总结:**new String()与直接赋值都会在字符串放入常量池之前进行判断,如果不存在则放入,如果存在则不放入。但是,new String()会在堆中多开辟一块空间存储字符串的值。因此,我们在赋值过程中应多使用直接赋值

2.String的intern()

intern() 方法返回字符串对象的规范化表示形式。这个方法会首先检查字符串池中是否有这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。即不会在堆中创建对象。

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

测试代码如下:

String a = new String("ab");
String b = new String("ab");
String c = "ab";
String d = "a" + "b";
String e = "b";
String f = "a" + e;

System.out.println(b.intern() == a);
System.out.println(b.intern() == c);
System.out.println(b.intern() == d);
System.out.println(b.intern() == f);
System.out.println(b.intern() == a.intern());
复制代码

结果如下:

false
true
true
false
true
复制代码

我们来逐行分析下结果:

1.b.intern() == a;b.intern()引用指向常量池中的引用,a引用指向堆中的引用,因此为false。

2.b.intern() == c;b.intern()引用指向常量池中的引用,c引用指向常量池中的引用,因此为true。

3.b.intern() == d;b.intern()引用指向常量池中的引用,d在编译期间会将“a”+“b”编译为“ab”,因此与d与c相同,因此为true。

4.b.intern() == f;b.intern()引用指向常量池中的引用,f为带有变量的操作,因此在编译期间不能存入常量池中,f的引用不指向常量池,因此为false。

5.b.intern() == a.intern();b.intern()引用指向常量池中的引用,a.intern()引用指向常量池中的引用,因此为true。

intern()使用场景

由于字符串中如果含有变量的字符串拼接,该字符串不会进入常量池中,因此会存在大量相同的字符串。使用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])).intern();
    }

    System.out.println((System.currentTimeMillis() - t) + "ms");
    System.gc();
}
复制代码

本文中的内容欢迎大家讨论,如有偏颇欢迎指正,文中例子是为了方面讲解特意举的,如有不当之处望谅解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值