其他分享
首页 > 其他分享> > 面试中关于字符串及常量池的一些考点

面试中关于字符串及常量池的一些考点

作者:互联网

字符串及常量池在面试中很容易被问到,前2天在为公司做校招面试时,发现很多同学对相关细节不太清楚,在此梳理一下:

先回顾一下java中字符串的设计,大家都知道jvm中有所谓的"字符串常量池"设计,当String s = "xxx"时,会先检查常量池中有没有,如果没有则加入常量池(缓存起来),下次再遇到同样的String s2="xxx"赋值时,直接从池中取用,不再重复创建。

 

围绕这个,就能设计一系列问题:(以下环境都基于jdk8)

题目1:

String s1 = "123";
String s2 = "123";
System.out.println(s1==s2);

==号比较的是字符串的引用地址,根据刚才的回顾,这题很容易回答。第1次赋值放到常量池,同时返回这个字符串在池中的引用,第2次发现常量池中已经有了,直接返回引用地址,所以s1与s2的地址相同,输出true

 

题目2:

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

注意这里s2用了String的构造函数来创建,看下这个方法的签名:

    /**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    @HotSpotIntrinsicCandidate
    public String(String original) {
        this.value = original.value;
        this.coder = original.coder;
        this.hash = original.hash;
    }

方法注释里已经说明,将创建一个新实例(并非放入常量池或先从常量池检查是否存在,即:跟常量池没关系),而且这个新实例是参数字符串的副本,即然是个新的副本,那地址自然跟常量池里的不同,因此这题输出false

 

题目3:

String s1 = "123";
String s2 = String.valueOf(123);
System.out.println(s1 == s2);

这里s2用了1个新的方法valueOf,而且入参是个整数,跟踪下这个方法:

    /**
     * Returns the string representation of the {@code int} argument.
     * <p>
     * The representation is exactly the one returned by the
     * {@code Integer.toString} method of one argument.
     *
     * @param   i   an {@code int}.
     * @return  a string representation of the {@code int} argument.
     * @see     java.lang.Integer#toString(int, int)
     */
    public static String valueOf(int i) {
        return Integer.toString(i);
    }

发现调用了Integer.toString()方法,再点进去:

    /**
     * Returns a {@code String} object representing the
     * specified integer. The argument is converted to signed decimal
     * representation and returned as a string, exactly as if the
     * argument and radix 10 were given as arguments to the {@link
     * #toString(int, int)} method.
     *
     * @param   i   an integer to be converted.
     * @return  a string representation of the argument in base 10.
     */
    @HotSpotIntrinsicCandidate
    public static String toString(int i) {
        int size = stringSize(i);
        if (COMPACT_STRINGS) {
            byte[] buf = new byte[size];
            getChars(i, size, buf);
            return new String(buf, LATIN1);
        } else {
            byte[] buf = new byte[size * 2];
            StringUTF16.getChars(i, size, buf);
            return new String(buf, UTF16);
        }
    }

最终仍然是new String(),根据上一题的分析,最终s2也是一个新实例,相当于"123"的一个新副本,所以s1与s2的地址不同,输出false。

 

题目4:

String s1 = "123".intern();
String s2 = "123".intern();
System.out.println(s1 == s2);

又出现1个新方法intern,不清楚功能的话,直接看源码注释是最快的学习方法:

    /**
     * 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();

首先这是1个native方法,也就是说对于初学者,不用关心实现了,专心看注释就好。核心看中间这段:

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

翻译一下:

当intern方法被调用时,如果常量池中已经存在1个相同内容的字符串(用equals判断),将直接返回池中的对象(注:String是引用类型,即返回的就是池中的引用),否则这个字符串将加入池中,同时返回字符串的引用。

所以,回到这题,第1次调用intern时,发现池中没有,会放到池中,然后返回池中的引用,第2次再调用intern时,发现池中已有,返回池中的引用,所以s1与s2地址相同,返回true

 

题目5:

String s1 = new String("123").intern();
String s2 = "123";
System.out.println(s1 == s2);

如果理解了上一题,知道intern方法的作用后,这题其实是障眼法,s1这一行,相当于先创建"123"的1个副本,然后返回常量池中的引用地址,接下来s2发现常量池中有内容为"123"的字符串,直接返回池中的地址,所以s1与s2地址相同,返回true

 

题目6:

String s1 = new String("123");
String s2 = s1.intern();
String s3 = "123";
String s4 = String.valueOf(123);

System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1 == s4);
System.out.println(s2 == s3);
System.out.println(s2 == s4);
System.out.println(s3 == s4);

这题属于综合运用了,根据刚才的分析s1、s4都是全新实例(跟常量池没关系),只有s2与s3都是从常用池中取的,所以除了s2==s3返回true外,其它全是false

标签:code,常量,s2,s1,intern,面试,考点,123,String
来源: https://www.cnblogs.com/yjmyzz/p/string-and-pool.html