相信绝大多数的人不会去用String类的intern方法,打开String类的源码发现这是一个本地方法,定义如下:
public native String intern();
文档告诉我们该方法返回一个字符串对象的内部化引用。
众所周知:String类维护一个初始为空的字符串的对象池,当intern方法被调用时,如果对象池中已经包含这一个相等的字符串对象则返回对象池中的实例,否则添加字符串到对象池并返回该字符串的引用。
从程序的角度上怎么来看这个方法呢,我们假设有两个字符串s1,s2,当s1.equals(s2)时,s1.intern()==s2.intern(),也就是说这两个字符串在内存中使用的是同一个实例。
Java语言规范中定义了字符串文字以及更一般的常量表达式的值的字符串是被内部化的,以便它们共享同一个实例。我们试验一下下面代码:
String s1="你好,Java";
String s2="你好,"+"Java";
System.out.println(s1==s2);
System.out.println(s1.intern()==s2.intern());
这段代码将打印两个true,也就是说字符串s1和s2是共享同一个实例。不过前提是尽管使用了表达式,但是表达式中必须都是常量。
了解这个处理机制也可以让我们在用到字符串常量的时候了解如何节省这些字符串所占用的内存。
下面两个例子可以帮你:
其一:
import javax.swing.*; public class StringIntern { 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"; JOptionPane.showMessageDialog(null, output, "Demonstrating String Method intern", JOptionPane.INFORMATION_MESSAGE); System.exit(0); } }
运行结果:
其二:
假定要加载许多数据库数据到内存,这些数据有很多是重复的。在反复测试之后,发现intern() 省了好多内存。
以下是表信息:
mysql> select count(*) from t1;
+------------+
| count(*) |
+------------+
| 8000 |
+------------+
1 row in set (0.01 sec)
mysql> select name From t1 limit 0,1;
+--------------------------------------+
| name |
+--------------------------------------+
| 123456789123456789123456789 |
+--------------------------------------+
1 row in set (0.00 sec)
总共8000行 ,每行的数据都是 "123456789123456789123456789"
下面是类 (连接异常忽略)
public static void a1() throws SQLException {
List list = new ArrayList();
Connection con = DB.getCon();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name FROM t1");
while (rs.next()) {
String s = rs.getString(1);
Po o = new Po();
//o.setName(s); //注释掉
o.setName(s.intern());
list.add(o);
s=null;
o = null;
}
rs.close();
stmt.close();
con.close();
}
public static void a2() throws SQLException {
List list = new ArrayList();
Connection con = DB.getCon();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name FROM t1");
while (rs.next()) {
String s = rs.getString(1);
Po o = new Po();
o.setName(s);
list.add(o);
s=null;
o = null;
}
rs.close();
stmt.close();
con.close();
}
注意:
a1 方法测试 使用内存: 2046544
a2 方法测试 使用内存: 3475000
如果在函数的结尾加上 System.gc(); 去除connection 的影响
a1 方法测试 使用内存: 668856
a2 方法测试 使用内存:2262232
然后再把 写函数 a3() ,把rs.getString(1) 换为 get()
static String get() {
return "123456789123456789123456789123456789123456789";
}
public static void a3() throws SQLException {
List list = new ArrayList();
Connection con = DB.getCon();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name FROM t1");
while (rs.next()) {
String s = get();
Po o = new Po();
o.setName(s);
list.add(o);
s=null;
o = null;
}
rs.close();
stmt.close();
con.close();
}
a3 方法测试 使用内存:666536
a2 (668856)比a3 的内存使用量多了一点点,这个估计就是常量池那一点内存
8000条数据 差别这么大, 数据量再大的话,优势更加明显.建议大家在连接数据库查询出来的数据,用intern();
注:内存使用 用 Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory() 计算
转自: http://www.java3z.com/cwbwebhome/article/article2/21063.html?id=1833 谢谢原作者。