string的intern方法的使用




A String object is immutable: Its content cannot be changed once the string is created.
String 是个不可变对象。

		String a = "hello";
		String b = new String("hello");创建了两个对象。第一个是创建在字符串常量池中,一个是堆中。
		String c = new String("hello" + "world");//按理应该是创建了4个对象,前提是字符串字面量"hello","world"之前没有出现过
		
		System.out.println(a == b);//false
		System.out.println(a == b.intern());//true.b引用指向了字符串常量池中的字面量
		
		String d = "2";
		String mm = "hello2";
		System.out.println(a + d == mm);//false
		System.out.println("hello" + "2" == mm);//true。字符串拼接,全是字面量常量(字符串,数字2),直接拼接放入常量池中。
		//有一个不是字面量常量,JVM内部做了优化,会创建一个StringBuilder进行拼接,最后得到的是一个String object.
		System.out.println("hello" + 2 == mm);//true
		
		final String aa = "hello";
		final String dd= "2";
		System.out.println(aa + dd == mm);//true,加上final,变量成为了编译期的常量,相当于c#中的宏定义。使用该变量的时候直接替换值。
		
		final String aaa = getHello();
		System.out.println(aaa + dd == mm);//false 只有在编译期间确定final的值,才会有常量的效果。



  • public String intern()
    Returns a canonical representation for the string object.

    A pool of strings, initially empty, is maintained privately by the class String.

    When the intern method is invoked, if the pool already contains a string equal to thisString object as determined by theequals(Object) method, then the string from the pool is returned. Otherwise, thisString object is added to the pool and a reference to thisString object is returned.

    It follows that for any two strings s and t, s.intern() == t.intern() istrue if and only ifs.equals(t) istrue

   intern方法被触发后,检测JVM的运行时常量池中是否有和当前字符串相同内容的字符串(equal方法为true),如果有的话,返回常量池中的字符串(外部的字符串不会加入池中);
如果池中没有这样的字符串,当前字符串对象就会加入到常量池中,并且返回该字符串对象的引用。 这样做的好处就是能够节约空间
ps:如果两个字符串的equal方法比较后是true( s.equals(t) is  true),那么 s.intern() == t.intern() is  true 

Java语言规范中定义了字符串文字以及更一般的常量表达式的值的字符串是被内部化的,以便它们共享同一个实例。我们试验一下下面代码:

 String s1="你好,Java"; 
 String s2="你好,"+"Java"; 
 System.out.println(s1==s2); 
 System.out.println(s1.intern()==s2.intern()); 



打印结果为两个true,说明字符串s1,s2是共享同一个实例的。
我们再来看看这个例子
package com.zhb.jvm;

/**
 * 
 * @author zhb
 *
 */
public class RuntimeConstantPoolOOM {
	public static void main(String[] args){
		//String str1 = new StringBuilder("计算机")
		String str1 = "abc";//str1.intern == str1 为true
		System.out.println(str1.intern() == str1);
		String str2 = new String("java");//false
		System.out.println(str2.intern() == str2);
		
		String str3 =new StringBuilder("math").append("analyze").toString();
		System.out.println(str3.intern() == str3);//true
		
		String str4 =new StringBuilder("computer").append("software").toString();
		System.out.println(str4.intern() == str4);//true
		
		String str5 =new StringBuilder("jav").append("a").toString();
		System.out.println(str5.intern() == str5);//false
		
		String str6 = new StringBuilder("math").toString();
		System.out.println(str6.intern() == str6);//false
	}

}



str1.intern == str1 为true 这个比较好理解. 
str2.intern() == str2 为false 这个也比较好理解。new String("java")创建了两个对象,str2.intern() 返回的是已经存在string pool里面的字符串的引用。
str3,str4中都是为true,因为执行append后,string pool中没有"mathanalyze", 符合首次出现原则。在jdk1.7以后,intern实现不会再复制实例,只是在常量池中记录首次出现的实例的引用,所以str3和str4都是为true.而在之前的jdk1.6的时候,intern()方法会把首次遇到的字符串实例复制到永久代(方法区)中,返回的也是永久代中这个字符串实例的引用。
如果使用jdk1.6运行的话,str3和str4都是false。
str5为false,是不是感觉有点奇怪,其实执行append方法之后,产生的新字符串java,该字符串在append方法执行之前,在string pool中已经存在该字符串了。所以不符合首次出现原则,返回为false.
str6为false,其实是和str3是一样的,string pool 存在math这个字符串了。


了解这个处理机制也可以让我们在用到字符串常量的时候了解如何节省这些字符串所占用的内存。
例1:
package com.zhb.string.intern;

import javax.swing.JOptionPane;

/**
 * 
 * @author Administrator
 * intern的作用
 */
public class StringIntern1 {
	public static void main(String[] args){
		String   s1,s2,s3,s4,output;   
        s1=new   String("hello");   
        s2=new   String("hello");   

        if(s1==s2)   
                output="s1   and   s2   are   the   same   object   in   memory";   
        else   
                output="s1   and   s2   are   not   the   same   object   in   memory";   

        if(s1.equals(s2))   
                output+="\ns1   and   s2   are   equal"   ;   
        else   
                output+="\ns1   and   s2   are   not   equal"   ;   

        s3=s1.intern();   
        s4=s2.intern();   

        if(s3==s4)   
                output+="\ns3   and   s4   are   the   same   object   in   memory"   ;   
        else   
                output+="\ns3   and   s4   are   not   the   same   object   in   memory"   ;   

        if(s1==s3)   
                output+="\ns1   and   s3   are   the   same   object   in   memory";   
        else   
                output+="\ns1   and   s3   are   not   the   same   object   in   memory";   

        if(s2==s4)   
                output+="\ns2   and   s4   are   the   same   object   in   memory";   
        else   
                output+="\ns2   and   s4   are   not   the   same   object   in   memory";   

        if(s1==s4)   
                output+="\ns1   and   s4   are   the   same   object   in   memory";   
        else   
                output+="\ns1   and   s4   are   not   the   same   object   in   memory";   

       
        System.out.println(output);
	}
}

输出结果是:

s1   and   s2   are   not   the   same   object   in   memory
s1   and   s2   are   equal
s3   and   s4   are   the   same   object   in   memory
s1   and   s3   are   not   the   same   object   in   memory
s2   and   s4   are   not   the   same   object   in   memory
s1   and   s4   are   not   the   same   object   in   memory


例2:

package com.zhb.string.intern;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.zhb.util.DBHandler;

/**
 * 
 * @author Administrator
 * intern作用2
 */
public class StringIntern2 {
	static String get() {
		  return "123456789123456789123456789";
	}
	public static void main(String[] args) throws Exception{
	       //test1();//测试结果:The busy memory is: 3260176
		//test2();//测试结果:The busy memory is: 2156624
		test3();//测试结果:The busy memory is: 2152072
		/*
		 * 从上面可以发现
		 * 1.test2使用intern后比test1占用的内存少了很多
		 * 2.test3比test2少了一点,因为test3中将rs.getString(1)换成了get()
		 * get()中返回的是一个和数据记录相同的字符串,但是这个字符串是存在类的静态方法中
		 * 所以该字符串不是放在方法区中的运行时常量池中,而放在虚拟机栈中的局部变量表中
		 * 所以test3比test2少的正是字符串在常量池中的那点内存。
		 */
		long total = Runtime.getRuntime().totalMemory();
		long free = Runtime.getRuntime().freeMemory();
		System.out.println("The busy memory is: " + (total - free));
		
	}
	public static void test1() throws Exception{
		List<Pojo> list = new ArrayList<Pojo>();
		Connection conn = DBHandler.getServerConnection();
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("select name from intern");
	    while(rs.next()){
		    String s = rs.getString(1);;
		    Pojo p = new Pojo();
		    p.setName(s);
		    list.add(p);
		    s = null;
		    p = null;
	    }
	    rs.close();
	    stmt.close();
	    conn.close();
	    System.gc();
	}
	
	public static void test2() throws Exception{
		List<Pojo> list = new ArrayList<Pojo>();
		Connection conn = DBHandler.getServerConnection();
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("select name from intern");
	    while(rs.next()){
	    	String s = rs.getString(1);
		    Pojo p = new Pojo();
		    p.setName(s.intern());
		    list.add(p);
		    s = null;
		    p = null;
	    }
	    rs.close();
	    stmt.close();
	    conn.close();
	    System.gc();
	}
	
	public static void test3() throws Exception{
		List<Pojo> list = new ArrayList<Pojo>();
		Connection conn = DBHandler.getServerConnection();
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery("select name from intern");
	    while(rs.next()){
		    String s = get();
		    Pojo p = new Pojo();
		    p.setName(s);
		    list.add(p);
		    s = null;
		    p = null;
	    }
	    rs.close();
	    stmt.close();
	    conn.close();
	    System.gc();
	}
}

                /*
		 * 从上面可以发现
		 * 1.test2使用intern后比test1占用的内存少了很多
		 * 2.test3比test2少了一点,因为test3中将rs.getString(1)换成了get()
		 * get()中返回的是一个和数据记录相同的字符串,但是这个字符串是存在类的静态方法中
		 * 所以该字符串不是放在方法区中的运行时常量池中,而放在虚拟机栈中的局部变量表中
		 * 所以test3比test2少的正是字符串在常量池中的那点内存。
		 */

总结:
Intern方法可以节约内存占用,建议从数据库中查询出来的数据可以使用intern方法。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值