String的可改变性

相信任何学习java的人,都会在书中看到“String字符串是不可变的,一旦创建就不能修改”这样的经典语句。也就是说写出String s = “aaa”; 之后再写 s = "bbb"; 还是没用,jvm会在内存中重新创建一个String对象“bbb”,而原来的“aaa”对象依然存在。 而且内存中“aaa”这个内容是不能修改的.这就是大多数人所接触的String以及对String 的解。

      不过经过我最近的研究,发现利用反射还是可以修改String对象。

      首先,研究String类源码,String类有一个 char 数组value,是final的,用来当作存储字符串的容器,也就是说String s=“aaa”;这个字符串真正是这么存储的: value[0]='a';  value[1]='a';  value[2]='a'; 而且value是final的,这就是说value在编译时就已经决定了。因此,这就是我们所说的String是不可变的。

      不过,当这一切的一切遇上java无敌的反射机制时,就好象防备森严的公主闺房下竟然有一条直通大街的地道,任何通过这条地道的人都可以一窥公主闺房。所以,通过Java的反射就可以改变String对象的内容。

      那么为什么反射就能改变String的内容呢?这是因为final是只对编译有效的,对运行无效。也就是说可以在运行是改变final的内容(当然前提是不能照着常规思路写,那样都不可能通过编译,怎么能运行呢?),所以你可以在运行时通过反射得到String的value的值,然后将新的值设置到value中,就改变了String对象。具体代码如下:

String s = "aaaa";
        System.out.println(s);
        try{
            Field field = s.getClass().getDeclaredField("value"); //String 类含有一个名为value的char数组,用于存储
            field.setAccessible(true);
            if(null != field.get("value")){
                System.out.println(field.get("value"));
            }else{
                System.out.println("no data");
            }
            field.set(s, new char[] {'b','b','b','b'});
            System.out.println(s);
        }catch(NoSuchFieldException e){
            e.printStackTrace();
        }catch(SecurityException e){
            e.printStackTrace();
        }catch(IllegalAccessException e){
            e.printStackTrace();
        }

运行结果如下:

aaaa
[C@1d057da
bbbb

只是简简单单的一句替代,运行结果就会改变,结果如下:

aa
[C@1d057da
bb
看到了吗?即使是下面new了一个大小为4的char数组,仍然只有两个‘b’存入到了s的value中。

这个好歹还没有抛出异常,要是替换成如下代码,更好的事情就来了,

String s = "aaaaaaa";

抛出异常如下:

aaaaaaa
[C@1d057da
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
	at java.lang.System.arraycopy(Native Method)
	at java.lang.String.getChars(Unknown Source)
	at java.io.BufferedWriter.write(Unknown Source)
	at java.io.Writer.write(Unknown Source)
	at java.io.PrintStream.write(Unknown Source)
	at java.io.PrintStream.print(Unknown Source)
	at java.io.PrintStream.println(Unknown Source)
	at Test.main(Test.java:21)

呵呵,爽了吧。

由此可见,通过地道进入公主闺房毕竟不是见得阳光的事情,稍微有一点点不符合要求的都会被发现,只有当第一个char数组和第二个char数组的大小严格相等的时候才会出现我们期望的结果。

注:此文来自iteye飞檐走壁的博客,原文地址:http://james23dier.iteye.com/blog/626723,与大家分享

  

  

posted @ 2013-03-01 12:28  中岛嘉兰  阅读(493)  评论(0编辑  收藏  举报