关于String面试题

来源:互联网 发布:阿里巴巴淘宝城地址 编辑:程序博客网 时间:2024/05/17 06:49

Java中String类由于其特殊性(不变类),几乎是笔试面试中的必考题,当然有些题目其实没啥意思,不过关键是要通过题目掌握原理性的东西。下面六道题目,如果您全部做对了,且明白其所以然,那么Java中的关于String的笔试面试题应该难不到你了。也许您觉得polaris说的有点过了,然而彻底明白这些题目,对理解String类还是很有好处的。

写出下面各题的打印输出的结果:

1

public static void main(String[] args) {           String a = "a1";           String b = "a" + 1;           System.out.println(a == b);       } 

2

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

3

public static void main(String[] args) {           String a = "ab";           final String bb = "b";           String b = "a" + bb;           System.out.println(a == b);       }   

4

public static void main(String[] args) {           String a = "ab";           final String bb = getBB();           String b = "a" + bb;           System.out.println(a == b);       }       private static String getBB() {           return "b";       }  

5

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

6

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

(1)通过java源码分析String

我们都知道String是不可变的(immutable),不变性的体现是:String类内部通过char数组来保存字符串,而这个char数组被声明为:final。那么为什么要将String类声明为不可变呢?了解设计模式的你应该知道有一种模式叫做“不变模式”(immutable pattern),String类的设计就是使用了不变模式,有兴趣的朋友可以看看“不变模式”讲的具体是啥东东。说完String的不可变性,需要说说String的“final性”(其实也还是不可变性决定的)。这也是有些面试官会问到的问题:我能不能写一个类继承自String?为什么?我们来看看String类的声明:

public final class String implements java.io.Serializable, Comparable, CharSequence

对于final关键字的作用不用多解释了。其实这也是“强不变模式”的一种要求(类本身声明为final或所有方法声明为final)。

(2)理解String对象的存储机制

要深入理解String必须先了解Java内存机制——运行时数据区(Runtime Data Area)。《The JavaTM Virtual Machine Specification》中将运行时数据区分为六部分(参看第三章): 1)The pc Register;2)Java Virtual Machine Stacks;3)Heap;4)Method Area;5)Runtime Constant Pool;6)Native Method Stacks;     以上数据区的具体描述可参考规范。需要注意的是,以上只是一个规范说明,并没有规定虚拟机如何实现这些数据区。

在说明String对象存储机制之前,我们需要先了解数据区的三个部分:Java 虚拟机栈(可以简称为Java栈)、堆和运行时常量池(简称常量池)。对于Java栈和堆大家应该比较熟悉,这里有一个关键点是常量池,下面就重点介绍一下与String相关的常量池。

首先大概描述一下什么是常量池:

虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用常量的一个有序集合,包括直接常量(string,integer和floating point常量)和对其他类型、字段和方法的符号引用。池中的数据项就像数组一样是通过索引访问的。因为常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以它在Java程序的动态链接中起着核心的作用。

<1> String相关常量池

在《The JavaTM Virtual Machine Specification》第四章有一节是专门讲解各种常量池的,其中有两个常量池是关于String的。1)The CONSTANT_String_info Structure对于常量池的细节此文不做过多介绍,polaris以后可能会写一序列关于Java虚拟机的文章。现在您可以查阅规范或在网上收集相关资料阅读。规范上对该常量池结构的介绍是: The CONSTANT_String_info structure is used to represent constant objects of the type String. 在该常量池结构中引用了另一个常量池结构,如2)2)The CONSTANT_Utf8_info Structure规范上的描述是:The CONSTANT_Utf8_info structure is used to represent constant string values.

根据上面的介绍可以看出,字符串字面值会存储在常量池中。下面来分析String对象的存储机制。

<2> String对象的存储

请看这样两个语句:

String x = “abc”; String y = new String(“abcd”);

现在来分析一下内存的分配情况。如图:
这里写图片描述

可以看出,x与y存在栈中,它们保存了相应对象的引用。第一条语句没有在堆中分配内存,而是将“abc”保存在常量池中。对于第二条语句,同样会在常量池中有一个“abcd”的字符串,当new时,会拷贝一份该字符串存放到堆中,于是y指向了堆中的那个“abcd”字符串。不知道polaris有没有讲明白。如果您明白了,那么做前面那六道题就没什么问题了。

3、六道题答案详解

1)true

要说明一点:当两个字符串字面值连接时(相加),得到的新字符串依然是字符串字面值,保存在常量池中。

2)false

当字符串字面值与String类型变量连接时,得到的新字符串不再保存在常量池中,而是在堆中新建一个String对象来存放。很明显常量池中要求的存放的是常量,有String类型变量当然不能存在常量池中了。

3)true

注意此题与上一题的区别,此处是字符串字面值与String类型常量连接,得到的新字符串依然保存在常量池中。

4)false

此题中第条语句:final String bb = getBB();其实与final String bb = new String(“b”);是一样的。也就是说return “b”会在堆中创建一个String对象保存”b”,虽然bb被定义成了final。可见并非定义为final的就保存在常量池中,很明显此处bb常量引用的String对象保存在堆中,因为getBB()得到的String已经保存在堆中了,final的String引用并不会改变String已经保存在堆中这个事实。

5)false,true

可能很多人对intern()这个函数不了解。JDK API文档中对intern()方法的描述是:返回字符串对象的规范化表示形式。一个初始为空的字符串池,它由类 String 私有地维护。当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。

上面字符串池即为字符串常量池。明白该题结果的原因了吧。

6)false,false,true

第五题看明白后,第六题就没什么好讲的了。

面试题:

Java代码

String s = new String("abc");   String s1 = "abc";   String s2 = new String("abc");   System.out.println(s == s1);   System.out.println(s == s2);   System.out.println(s1 == s2);  

请问以上程序执行结果是什么?

第一句执行后内存中有两个 对象,而不是一个。一个由new String(“abc”)中的”abc”在String Pool里生成一个值为”abc”的对象;第二个由new在堆里产生一个值为”abc”的对象,该对象完全是String Pool里的”abc”的一个拷贝。变量s最后指向堆中产生的”abc”对象;

第二句执行时,s1先去String Pool找是否有值为”abc”的对象,很显然在上一步中java已经在String Pool里生成一个”abc”对象了,所以s1直接指向String Pool中的这个”abc”;

第三句中又有一个new,在java中凡遇到new时,都会在堆里产生一个新的对象。因此,该句执行后堆里又多了一个”abc”对象,这与执行第一句后生成的”abc”是不同的两个对象,s2最后指向这个新生成的对象。
因此,执行后面的打印语句的结果是三个false

问题2:
Java代码

System.out.println(s == s.intern());   System.out.println(s1 == s1.intern());   System.out.println(s1.intern() == s2.intern());  

请问以上程序执行结果是什么?

设 s为String类型的变量,当执行s.intern()时,java先在String Pool里找与字符串变量s相等(用equals()方法)的字符串,若有则将其引用返回;若没有则在String Pool里创建一个与s的值相等的字符串对象,并将其引用返回。从中我们可以总结出intern()方法无论如何都将返回String Pool里的字符串对象的引用。
因此,以上程序执行的结果是false,true,true
PS:设s和t为两个字符串变量,若有s.equals(t),必有s.intern() == t.intern();
PS:”==”永远比较的是两边对象的地址是否相等。

问题3:
Java代码

String hello = "hello";   String hel = "hel";   String lo = "lo";   System.out.println(hello == "hel" + "lo");   System.out.println(hello == "hel" + lo);  

请问以上程序执行结果是什么?

前三句在String Pool里分别产生“hello”、“hel”、“lo”三个常量字符串对象
当做第一个加法连接时,+号两边都是常量字符串,java就会将两者拼起来后到String Pool里找与之相等(用equals)的字符串,若存在则将其地址返回;不存在则在String Pool里新建一个常量对象,其值等于拼接后的字符串,并将其地址返回。

第二个+号两边有一个是变量,此时,java会在堆里新建一个对象,其值是两字符串拼接后的值,此时返回的地址是堆中新对象的地址。所以,第一句做+连接后返回String Pool中“hello”的地址,显然与变量hello的地址相等;第二句返回的是堆中地址,显然与变量hello的地址不等;

原创粉丝点击