当前位置: 代码迷 >> 综合 >> String.inter()深入理解
  详细解决方案

String.inter()深入理解

热度:106   发布时间:2023-11-23 23:58:11.0

        代码中字符串的使用对于每个程序员来说都司空见惯,大家用起来也得心应手,所以很少会花时间深入研究。但是在笔试面试中,却常常被提问,而且往往是看似熟悉的程序,却无法说出自己坚信的答案。而且今天的重点是String.inter()。今天花时间整理一下,一来自己做一个记录,二来希望帮助更多的猿类!Hahaha~

先热热身做俩道题:

Q:下列程序的输出结果: 
String s1 = “abc”; 
String s2 = “abc”; 
System.out.println(s1 == s2); 

A:true

分析一下:程序执行,常量池中生成一个字符串,并将栈中的引用指向 s1,s2 先去查询常量池中是否有已经存在,存在,则返回常量池中的引用,所以s1 = s2。

Q:下列程序的输出结果: 
String s1 = new String(“abc”); 
String s2 = new String(“abc”); 
System.out.println(s1 == s2); 

A:false,两个引用指向堆中的不同对象。

分析一下:第一步、常量池中生成一个字符串“abc”,并在堆中生成一个s1引用指向的对象,第二步、先去查询常量池中是否有已经存在“abc”,因为已经存在,所以不在生成,但堆中仍然会生成一个s2引用指向的对象,所以s1 , s2引用不一样。

Q:下列程序的输出结果: 
String s1 = “abc”; 
String s2 = “a”; 
String s3 = “bc”; 
String s4 = s2 + s3; 
System.out.println(s1 == s4); 

A:false,因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。

分析一下:程序执行,s1,s2,s3在常量池中分别生成字符串,并将栈中的引用指向s1,s2,s3,而s4在编译的时候,其方法内部创建StringBuilder来拼接对象,在堆中生成了新的对象。

引号声明的字符串都是会直接在字符串常量池中生成的,而 new 出来的 String 对象是放在堆空间中的。

接下来好戏开始:

public static void main(String[] args) {
        
    String str1 = "abc";
        String str2 = new String("abc");
        String str3 = str2.intern();
        System.out.println(str1==str2);//#1
        System.out.print (str1==str3);//#2   
}    
输出结果为: false    true
#1:str1指向常量池中的对象引用,str2指向堆中的对象引用,所以返回false;

#2:str2.intern()指向str1的在常量池中的对象引用,所以str1==str3。

public static void main(String[] args) {
        
    String abc = "abc";
        final String abcFinal = "abc";
 
        String str1 = "abc01";
        String str2 = "abc"+"01";
        String str3 = abc + "01";
        String str4 = abcFinal+"01";
        String str5 = new String("abc01").intern();
 
        System.out.println(str1 == str2);//#3
        System.out.println(str1 == str3);//#4
        System.out.println(str1 == str4);//#5
        System.out.println(str1 == str5);//#6 
}
输出的结果为:true    false    true    true
#3:str1指向常量池中的对象引用,而str2在编译期间,+号会自动合并为一个字符串,因为常量池中已经生成了str1的对象,所以str2不在生成,直接将常量池对象的引用赋值给栈中的str2。

#4:str4实际上是使用StringBuilder.append来完成,会生成不同的对象。

#5:final修饰的变量,在编译期间会自动进行常量替换,这是与#4不同。所以不在生成新的对象,直接将常量池对象的引用str1赋值给栈中的str4。

#6:都是指向常量池中的对象引用。

public static void main(String[] args) {
        
    String str2 = new String("abc")+new String("01");
    str2.intern();
    String str1 = "abc01";
    System.out.println(str2==str1);//#7
}
输出的结果为:true 
#7:str2在常量池中分别生成了俩个对象,“abc”和“01”,堆中则存的是abc01的引用,并赋值给了str2,str2.intern()在常量池中生成了一个abc01的引用,并赋值给了str2,str1在生成对象之前先是查找,找到并直接返回str2在常量池中生成的引用。
public static void main(String[] args) {
        
    String str1 = "abc01";
    String str2 = new String("abc")+new String("01");
    str2.intern();
    System.out.println(str2 == str1);//#8
}
输出的结果为:false
#8:str1指向常量池中的对象引用,而str2指向堆中的对象引用,并在常量池生成俩个对象,“abc”和“01”,接着str2.intern()返回str1指向常量池中的对象引用,但是对于str2没有影响,结果为false,如果是str2 = str2.intern();则最终结果将是true。


上面讲了一些原理,那么为什么要使用String的intern()方法呢,甚至是非用不可??
        假设有这样的一个问题,我们需要读取一个大的文件(文件中有重复的一些信息),并且要处理这些读取出来的字符串,这时候我们应该怎样编写程序才能使程序高效执行呢?

Tips:是不是首先想到用Set,HashSet来存这些字符串,回忆一下Set集合的特点,无序,不可重复。在仔细一想,无序:基于链表的数据结构,查询效率低,不方便我们处理大量的字符串;不可重复:在set值的时候,先先生成HashCode,再迭代查询是否有重复的值。。。那么有没有更好的方法呢?

intern()方法设计的初衷,就是重用String对象,以节省内存消耗。

点击打开链接

但是调用intern()方法开销也不小所以灵活运用。

  相关解决方案