令人头疼的String该怎么破?

String是日常编码中出现频率最高的类之一了吧,但是关于它的知识点可不少,下面就来来聊聊它吧

首先我们要知道String不属于Java中的八种基础类型之一,有多少新手曾误以为String是基础类型(我就是。。。)

众所周知String为了保证线程安全和效率的问题采用了不可变设计

	@Stable//内部储存String的byte数组成员变量
    private final byte[] value;

一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。特别要注意的是,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。

//当我们调用subString最终调用的方法
public static String newString(byte[] val, int index, int len) {
        return new String(Arrays.copyOfRange(val, index, index + len), (byte)0);
    }

常量折叠

String s = “a” + “b”,编译器会进行常量折叠(因为两个都是编译期常量,编译期可知),即变成 String s = “ab”

	public static void main(String[] args) {
        String s1 = "a" + "b";
        String s2 = "ab";
        System.out.println(s1 == s2);// true
    }

再来看看这个例子

	public static void main(String[] args) {
        String s = "a";
        String s1 = s + "b";
        String s2 = "ab";
        System.out.println(s1 == s2);//false
    }

为什么会出现false呢?

对于能够进行优化的语句(String s = “a” + 非final修饰变量 / new String() ) 最后会调用 StringBuilder 的 append() 方法替代,最后调用 toString() 方法 (底层可以看作是一个 new String()操作)

而凡是new出来的String和字面量做 == 判断那自然是不相等的,因为new出来的对象在堆内存新开辟了一块空间

	public static void main(String[] args) {
        String s = new String("ab");
        String s1 = "ab";
        System.out.println(s == s1);//false
    }

原来拼接前后有变量的操作最终变成了StringBuilder.append().toString()了,我相信下面这个例子你们也能判断了

	public static void main(String[] args) {
        String s1 = new String("a") + "b";//常量池中不会放入”ab“
        String s2 = "ab";//直接定义字面量会在常量池中放入”ab“
        System.out.println(s1 == s2);
    }

intern()方法

字符串常量池我相信大家都听过,暂且先把它看作是一个Set。JDK6之前都是放在方法区中,而JDK7中字符串常量池又移到了堆中,JDK8中字符串常量池移到了元空间中,因为方法区被元空间取代了。
intern的作用是如果常量池中有这个字符串的值,则返回该字符串的地址,否则就把堆中new的字符串地址放在常量池中,并返回地址。

	public static void main(String[] args) {
        String s1 = "ab" + new String("cd");//此时常量池中没有“abcd”的地址
        String s2 = s1.intern();//把“abcd”地址放入常量池,并返回
        String x = "abcd";//常量池中有了“abcd”地址,直接返回常量池中的地址
        //注意,此时三个变量都是相等的,它们都是指向堆中”abcd“的指针
        System.out.println(x == s2);//true 
        System.out.println(s1 == s2);//true
    }

但是上面的代码在JDK6与JDK7+执行结果是不同的,JDK6中,往常量池放入的操作指的是创建一个字符串副本,并返回副本的地址,而JDK7+中常量池放的是堆中字符串的地址

创建对象问题

new String(“ab”)创建了几个对象?

2个,一个在堆中创建,一个在字符串常量池中

new String(“a”) + new String(“b”)呢?

4+2=6个,别忘了用’+'拼接会有StringBuilder!最后调用StringBuilder.toString()时会new 一个 String(“ab”),但"ab"不会放入常量池

总结
其实日常编码中完全不用记这么多规则,比较String值的时候永远用equals就行了,除了应付面试没必要纠结这些问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值