java虚拟机笔记 String相关概念及其面试题

String相关

String的基本特征
  • String被声明为final,不可被继承。
  • String实现了Serializable接口,表明String支持序列化;实现了Comparable接口,表明String是可以比较大小
  • jdk8及其以前底层用char数组实现;jdk9以后用byte数组加编码标记(因为比如中文需要用两个byte存储)实现,StringBuilder和StringBuffer也做了相应的更新。
  • 字符串常量池不会存储相同内容的字符(底层实现是一个固定长度的hashtable,长度可由-XX:StringTableSize=size设置)。
  • jdk7开始,字符串常量池从方法区移到堆中,原因如下:
    • 方法区垃圾回收率不高
    • 永久代内存空比较小
  • String、StrngBuilder、StringBuffer区别
    • String底层数组用final修饰,所以不可变;StringBuilder、StringBuffer底层数组可变(可修改)
    • StringBuilder线程不安全,但速度比StringBuffer快,StringBuffer线程安全(Synchronized实现线程安全),但速度较慢
    • 需要频繁操作修改字符串时建议使用StringBuilder或者StringBuffer
        String s1= "abc";//采用赋值操作,字面量存入字符串常量池中
        String s2=new String("abc");//new String操作在堆中新建一个String对象,值为"abc"
        System.out.println(s1==s2);//false;
字符串拼接操作
  • 常量和常量的拼接结果在常量池,原理是编译器优化
        String s1= "a"+"b"+"c";//编译后,s1 = "abc";
        String s2="abc";
        System.out.println(s1==s2);//true
  • 常量池不会存在相同内容的常量。
  • 只要其中一个是变量,结果就在堆中(在堆中新建了一个String对象,而不是放入字符串常量池中),变量拼接的原理是StringBuilder。
        String s1= "abcdef";
        String s2="abc";
        //拼接符号+前后出现了变量s2
        //通过new一个StringBulider并调用append()方法进行拼接,拼接完成后调用toString()在堆中新建了一个对象
        String s3 = s2+"def";
        System.out.println(s1==s3);//false;
        String s="";
        for (int i = 0; i < 10000; i++) {
            s+="a";//每次循环都会创建一个新的StringBuilder对象和String对象
        }
相关面试题
new String(“ab”)的时候创建了几个对象?,怎么证明?
  • 创建了两个对象:
    • 对象1:new String()
    • 对象2:字符串常量池中字面量为"ab"的对象(ldc指令)
      • 个人理解是创建String对象的时候使用了字面量"ab"作为参数,所以会将字面量加入字符串常量池中
  • 怎么证明:看字节码指令
    在这里插入图片描述
new String(“ab”)+new String()的时候创建了几个对象?,怎么证明?
  • 从字节码角度创建了五个对象

    • 对象1:new StringBuilder();
    • 对象2:new String();
    • 对象3:字符串常量池中字面量为"ab"的对象;
    • 对象4:new String();
    • 对象5:字符串常量池中字面量为"cd"的对象;
  • 如何证明:
    在这里插入图片描述

  • 深入剖析:

    • 通过查看StringBuilder的toString()方法,发现toString()方法是通过通过new String()实现的,所以实际上创建了6个对象。
    • 最后调用toString()方法的时候,等同于new String(“abcd”);但是字符串"abcd"并没有被加入字符串常量池中
      • 个人理解是因为这个new String()的时候传入的是一个value变量(value=“abcd”),而不是直接传入字面量作为参数,所以"abcd"没有加入字符串常量池
intern的使用
  • 方法实现:public native String intern();
  • 方法作用:可以判断当前字符串是否存在字符串常量池中,如果存在,则直接返回字符串常量池中当前字符串的地址;如果不存在则在字符串常量池中创建,并返回字符串常量池中当前字符串的地址。
public class StringIntern{
    public static void main(String[] args) {
        String s1 = new String("1");
        s1.intern();//实际上调用此方法前,字符串“1”已经存在字符串常量池中
        String s2 ="1";
        //因为s1引用的堆中的地址,s2是字符串常量池中的地址
        //所以一定是false
        System.out.println(s1==s2);//jdk6:false;jdk7/8:false

        //s3变量记录的地址类似于 new String("11");
        //但“11”不存在字符串常量池中
        String s3 = new String("1")+new String("1");
        // 在字符串常量池中加入“11”
        //jdk6中,字符串常量池在方法区(永久代),所以会在常量池新建一个对象,也就会有新的地址
        //jdk7/8,字符串常量池移到堆中,为了节省空间,所以常量池中的“11”对象直接引用了堆上的String对象的地址,而不是新建对象。
        s3.intern();
        String s4 = "11";//s4变量记录的地址是上一行代码执行时,在常量池中生成的“11”的地址
        System.out.println(s3==s4);//jdk6:false;jdk7/8:true
    }
}

在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值