您的位置 首页 java

IT技术-基础篇 聊聊Java的String,还有那么几个面试弯弯题

前言

有人说看了你的基本类型篇,那我期望有 java 语言表达一句话还不成了是吧,我是不是得一字一顿呢?当然不是啦,Java中存在着 String ,只要使用 双引号 概括起来的那么就是一个String( 字符串 )。


设计

字符串操作那可是计算机程序设计中最常见的行为了,使用频率高的因素决定了其定位与设计,设计者将其定位为不可变,不可变的表现如下:

  • 在设计上 String类 通过final修饰了类,同时将存放内容的char数组也使用final修饰。final关键字修饰的属性为不可修改的,修饰的类是不可继承的,如此就限制了使用者对其修改。
 public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
    @Stable
     private  final  byte [] value;
    //...
}  
  • 我们可以通过String类的源码先从表象看一看,每一个看起来会修改其值的方法最终都是创建了一个新的String对象。如下图字符串的截取方法,指定起止下标,但返回时如果截取长度与字符串长度一致返回当前对象,否则就创建一个新的对象返回。
 public String substring(int beginIndex, int endIndex) {
    int length = length();
    checkBoundsBeginEnd(beginIndex, endIndex, length);
    int subLen = endIndex - beginIndex;
    if (beginIndex == 0 && endIndex == length) {
      return this;
    }
    return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
    : StringUTF16.newString(value, beginIndex, subLen);
}  

位置

  • 使用常量方式创建的字符串存放在常量池里
  • 使用new方式创建的字符串存放在堆里

说到这里就顺便了一下String提供的方法intern(),这个方法就有点意思了,根据下图源码表示,这个方法是返回一个标准化的表示方式,通过字符串的常量池进行返回,也就是说针对字符串调用此方法就会统一表示,主要是针对通过new创建在堆上的字符串,调用此方法,它会先去常量池里发现,如果存在返回它,如果不存在就会放入常量池并返回,简单理解就是将存在于堆的字符串放入常量池。

     /**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java™ Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     * @jls 3.10.5 String Literals
     */
    public native String intern();  

重载“+”

重载就是”+”操作符在应用于对应类时具有特殊意义,这里对于String而言“+”即为连接的作用,以下通过javap命令对于源码进行反编译,进行更直观的确认:

 java反编译流程(参数很多,请根据需要进行添加)
1、 javac  XXX.java
2、javap -v XXX  
 public class Test {
    public static  void  main(String[] args) {
        String a = "a";
        String s = "b" + "c" + "d" + a;
        System.out.println(s);
    }
}  
 0: ldc #2 // String a
2: astore_1
3: new #3 // class java/lang/ StringBuilder 
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String bcd
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return  

通过将源代码javap反译后可以看到指令码中,Java在处理”+”拼接的字符串时借助了StringBuilder类, append 方法即追加内容,调用 toString 方法new出来最终结果。

有人会问拼接时有四个元素进行拼接,为什么只调用了2次append方法呢,因为前3个元素均在常量池优化合并。

为了直观地看出,下面的例子,请参照如下,共计调用5次append方法:

 public class Test {
    public static void main(String[] args) {
        String a = "a";
        String s = "b" + a + "c" + a + "d";
        System.out.println(s);
    }
}  
 0: ldc #2 // String a
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String b
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String c
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: aload_1
25: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: ldc #8 // String d
30: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
36: astore_2
37: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_2
41: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: return  

弯弯绕绕面试

一、请写出以下程序的输出结果

 public class Test {
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        String ab = "ab";
        String sum = "a" + "b";
        String sum_a = a + "b";
        String sum_ab = a + b;
        System.out.println(ab == sum);      // true
        System.out.println(ab == sum_a);    // false
        System.out.println(ab == sum_ab);   // false
        System.out.println(sum == sum_a);   // false
        System.out.println(sum == sum_ab);  // false
        System.out.println(sum_a == sum_ab);// false
    }
}

根据前面我们通过反编译得出来的结论:
1、常量拼接仍在常量池;
2、有变量参与拼接, JDK 就会估化操作:
    String a = "a";
    String sum_a = a + "b";
    StringBuilder temp = new StringBuilder();
    temp.append(a);
    temp.append(b);
    temp.toString();// StringBuilder的toString()方法为new String()  

二、问以下语句共创建了几个对象

 String s = new String("abc");
以上语句等价于:
String temp = "123";		// 0~1 常量池存在,则将堆内对象引用赋值,否则在堆内创建对象,并加入常量池
String s = new String(temp); //1
所以上述语句共创建1~2个对象  

三、请问以下代码是否存在问题

 String  Lock  = Thread.currentThread().getName();
 synchronized ( lock ) {
    // DO SOMETHING
}
Lock取值为当前 线程 的名称,但线程名称非常量,存在于堆内,因此在这种情况下会导致锁失效。
需使用intern()方法将lock加入常量池。  

文章来源:智云一二三科技

文章标题:IT技术-基础篇 聊聊Java的String,还有那么几个面试弯弯题

文章地址:https://www.zhihuclub.com/183817.shtml

关于作者: 智云科技

热门文章

网站地图