五道String面试题

451 阅读6分钟

今天大致的阅读了String类的源码,并刷了常见的面试题,在此做个笔记。

面试题一:判断下列程序运行结果

package String_test;

public class test_1 {
    public static void main(String[] args) {
        String str1 = "HelloWorld";
        String str2 = "HelloWorld";
        String str3 = new String("HelloWorld");
        String str4 = "Hello";
        String str5 = "World";
        String str6 = "Hello" + "World";
        String str7 = str4 + str5;

        System.out.println("str1 == str2 result: " + (str1 == str2));    //1. true

        System.out.println("str1 == str3 result: " + (str1 == str3));    //2. false

        System.out.println("str1 == str6 result: " + (str1 == str6));    //3. true

        System.out.println("str1 == str7 result: " + (str1 == str7));    //4. false

        System.out.println("str1 == str7.intern() result: " + (str1 == str7.intern()));        //5. true

        System.out.println("str3 == str3.intern() result: " + (str3 == str3.intern()));        //6.false
    }
} 

画内存图逐个的分析每种情况:第一个:str1与str2指向同一个地址,故相等。

第二个:new代表创建了一个对象,str3指向堆内存中的引用,故str1与str3指向的地址不同。需要注意的是:字符串常量池中不可能存在两个一样的字符串值,向这里堆内存指向的实际还是运行时常量池中的HelloWorld值

第三个:由于“Hello”与“World”都是常量,用+号在编译时会被自动编译成String str6 = "HelloWorld",所以两个引用都是指向常量池中的地址

第四个:关键点在于理解String str7 = str4+str5。在jdk文档中有这么一段话

java语言提供了字符串串联运算符特殊支持(+),和其他对象转换为字符串。字符串连接是通过StringBuilder实施(或StringBuffer)类及其append方法。字符串的转换是通过方法toString实施,由Object和继承的所有类的java。

可见jvm会在堆中创建一个以str4为基础的StringBuilder对象,在通过append方法添加,最后通过toSting()返回一个String对象。故str7指向的还是堆内存的对象而str1指向的是常量池中的地址,两者指向地址不相同。由此可见操作字符尽量用StringBuilder而不用"+"

第五个:intern方法使用:一个初始为空的字符串池,它由类String独自维护。当调用 intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。这里str1指向常量池中的“HelloWorld”对象,str7.intern():此时常量池中已经有"HelloWorld"字符串值,所以地址指向相同

第六个:str3指向的是堆内存,而str3.intern()返回的是常量池中已有字符串“HelloWorld”的引用,故两者指向地址不同。

面试题二:java中的String为什么设计为final类?

  1. **允许String对象缓存hashCode值:**在java中String类型是非常常用的,涉及到大量的增删改查。字符串不变性保证了hashCode的唯一性,这是一种优化手段意味着不必每次都去计算hash值,这也是为什么HashMap建议用String,Integer这种不可变对象当作Key值
  2. **字符串常量池需要:**java中将字符串值存放在字符串常量池中,如果String对象是可变的,会产生很多逻辑错误,比如改变一个对象会影响到另一个独立对象。
  3. **安全性:**网络地址的url,文件路径path通常情况啊下都是用String类型来保存,如果不是固定不变的可能产生很多安全隐患。

面视题三:有什么办法可以改变String?

如果问了这个就很尴尬,sun公司特意设计的不可变,要强行改变只能通过反射这种骚操作

package String_test;

import java.lang.reflect.Field;

public class test_2 {
    public static void main(String[] args) throws Exception {
        String str = "王老吉真解渴";
        System.out.println("str=:  "+str);
        
        //通过反射改变获取内部的value字符数组
        Field field = String.class.getDeclaredField("value");
        field.setAccessible(true);
        field.set(str, new char[]{'加','多','宝','也','解','渴'});
        
        System.out.println("str=:  "+str);
    }
}

面试题四:下列代码创建了几个对象?

String st1 = new String(“abc”);

常量池一个“abc”对象,堆中一个"abc"对象,总共两个。

String st1 = new String(“abc”);
String st2 = new String(“abc”);

3个对象。 字符串在常量池中是唯一的,堆内存中有两个,常量池中一个。

面试题五:谈一下String,StringBuilder,StringBuffer的区别?

1.String类是字符串常量,而StringBuilder与StringBuffer是字符串变量。前者不可变后者可变

2.StringBuilder是非同步的,StringBuffer类的API都套上了一层synchronized同步修饰,所以StringBuffer适合在多线程场景使用(实际基本不用),StringBuilder类适合单线程使用,它两用的多的就是append和insert方法

它三的适用场景可以看下知乎:www.zhihu.com/question/20…

Integer相关

  • Java 基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean;这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。

  • 两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。

    /**
    *此方法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外的其他值。
    */
        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    
        @Test
        public void test2() {
            Integer i1 = 33;
            Integer i2 = 33;
            System.out.println(i1==i2);  //true
            Double d1 = 33.33;
            Double d2 = 33.33;
            System.out.println(d1 == d2);  //false
        }
    
  • Integer i1=40;Java 在编译的时候会直接将代码封装成Integer i1=Integer.valueOf(40);,从而使用常量池中的对象。
    Integer i1 = new Integer(40);这种情况下会创建新的对象。

      Integer i1 = 40;
      Integer i2 = new Integer(40);
      System.out.println(i1==i2);//输出false
    
      Integer i1 = 40;
      Integer i2 = 40;
      Integer i3 = 0;
      Integer i4 = new Integer(40);
      Integer i5 = new Integer(40);
      Integer i6 = new Integer(0);
      
      System.out.println("i1=i2   " + (i1 == i2));  //true
      System.out.println("i1=i2+i3   " + (i1 == i2 + i3)); //true
      System.out.println("i1=i4   " + (i1 == i4));  //false
      System.out.println("i4=i5   " + (i4 == i5));  //false
      System.out.println("i4=i5+i6   " + (i4 == i5 + i6)); //true  
      System.out.println("40=i5+i6   " + (40 == i5 + i6)); //true
    

    语句i4 == i5 + i6,因为+这个操作符不适用于Integer对象,首先i5和i6进行自动拆箱操作,进行数值相加,即i4 == 40。然后Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较。

小结:

关于String类和它的成员方法,都是被final修饰的,意味着Strin类不可被继承。String底层是采用字符数组对数据进行操作的,关于String的修改操作jdk底层都是会new一个新的String对象,在它的基础上进行操作。所以String是不可变的。

关于String,基础性的理论大致就这些,更多的还会考察字符串的一些算法,这部分也是需要攻克的一个难点!!

参考链接:www.cnblogs.com/xiaoxi/p/60…

参考链接:blog.csdn.net/yissan/arti…

参考链接:juejin.cn/post/684490…