原文出自:http://cmsblogs.com/?p=863。尊重作者的成果,转载请注明出处!
个人站点:http://cmsblogs.com
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 今天朋友问我String的内容是真的不可变吗?我肯定告诉他是的?因为在我的主观意识里String就是一个不可变的对象。于是他给我发了这段程序:
public class StringTest {
public static void main(String[] args) throws Exception {
String a = "chenssy";
System.out.println("a = " + a);
Field a_ = String.class.getDeclaredField("value");
a_.setAccessible(true);
char[] value=(char[])a_.get(a);
value[4]='_'; //修改a所指向的值
System.out.println("a = " + a);
}
}
看到这个简单的程序,我笑了,你这不是从底层来修改String的值么?从这里来理解String的值肯定是可以改变的啦(我们应该始终相信String的不可变性)!接着他再给我一段程序:
public class StringTest {
public static void main(String[] args) throws Exception {
String a = "chenssy";
String b = "chenssy";
String c = new String("chenssy");
System.out.println("--------------修改前值-------------------");
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
//修改String的值
Field a_ = String.class.getDeclaredField("value");
a_.setAccessible(true);
char[] value=(char[])a_.get(a);
value[4]='_'; //修改a所指向的值
System.out.println("--------------修改后值-------------------");
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("chenssy");
System.out.println("c = " + c);
}
}
乍看这程序是异常的简单,无非就是赋值、改值、输出嘛!可能你现在就会毫不犹豫的说太简单了结果就是……。但是!!你的毫不犹豫会害死你,而且你的结果很可能错误。那么运行结果是什么呢?
--------------修改前值-------------------
a = chenssy
b = chenssy
c = chenssy
--------------修改后值-------------------
a = chen_sy
b = chen_sy
chen_sy
c = chen_ssy
修改前值很容易理解,但是修改后值呢?是不是有点儿不理解呢?你可能会问:为什么System.out.println("chenssy");的结果会是chen_ssy,System.out.println("c = " + c);也是chen_ssy呢?
要明白这个其实也比较简单,掌握一个知识点:字符串常量池。
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串(这点对理解上面至关重要)。
我们再来理解上面的程序。
String a = "chenssy";
String b = "chenssy";
a、b和字面上的chenssy都是指向JVM字符串常量池中的”chenssy”对象,他们指向同一个对象。
String c = new String("chenssy");
new关键字一定会产生一个对象chenssy(注意这个chenssy和上面的chenssy不同),同时这个对象是存储在堆中。所以上面应该产生了两个对象:保存在栈中的c和保存堆中chenssy。但是在Java中根本就不存在两个完全一模一样的字符串对象。故堆中的chenssy应该是引用字符串常量池中chenssy。所以c、chenssy、池chenssy的关系应该是:c--->chenssy--->池chenssy。整个关系如下:
通过上面的图我们可以非常清晰的认识他们之间的关系。所以我们修改内存中的值,他变化的是所有。
总结:虽然a、b、c、chenssy是不同的对象,但是从String的内部结构我们是可以理解上面的。String c = new String("chenssy");虽然c的内容是创建在堆中,但是他的内部value还是指向JVM常量池的chenssy,它构造chenssy时所用的参数依然是chenssy字符串常量。
为了让各位充分理解常量池,特意准备了如下一个简单的题目:
String a = "chen";
String b = a + new String("ssy");
创建了几个String对象??
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
原文出自:http://cmsblogs.com/?p=863。尊重作者的成果,转载请注明出处!
个人站点:http://cmsblogs.com
分享到:
相关推荐
使用过protobuf的同学肯定对pb的DebugString相关方法再熟悉不过了,其作用就是打印输出pb对象。有些时候我们可能会有根据这个输出还原原始pb对象的需求。仔细观察发现输出并不是标准json格式,也就是说基于json→pb...
网上很多描述java解压中文乱码的问题,...public static void main(String args[]) { new ZipUtil().unZip("E:\\aaaa\\中文.zip","E:\\aaaa\\中文","GBK"); } 实例: 将E:\\aaaa\\中文.zip解压到E:\\aaaa\\中文目录下
一些常用的java工具类:Date和String类型互转,获取想要格式的String类型的日期时间、java导出数据到Excel、http文件下载、HMAC-MD5加密、3DES加密、MD5加密、读写txt文件、zip解压缩文件、文件夹等
Java实现压缩与解压缩ZIP import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream;...
JAVA文件压缩与解压缩实践报告 主函数 gzip压缩模块代码 压缩模块要完成的就是将文件读入以后进行压缩,再将压缩后的数据写入一个新的文件,其部分代码如下: public class gzip { public static void main(String...
Java 语 言 能 在 执 行 码 (二 进 制 码 )上 兼 容 ,这 样 以 前 所开 发 的软 件 就 能 运行 在 不 同 的 机 器 上 ,只 要 所 用 的 机 器 能 提供 Java 语 言 解 释 器 即可 。 Java 语 言 将 对 未 来 软 件 ...
通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从...
通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从...
java实现对文件的基本操作,如创建,删除,读取,写入,解压缩,复制,移动等操作。{@link #createFile(String)} * * {@link #readFile(String, String)} * * {@link #moveFile(String, String)} * {@link #...
下载以后解压缩,然后从lib文件夹下导入Spire.Barcode.jar包到你的Java应用程序中即可。 支持的条码类型 Spire.Barcode for JAVA支持的条码类型多达38种,包括常见的QR Code、Code 25、 CodeBar、Code 39、Code 93...
解压缩之后,将chat.sql导入你的mysql数据库中; 将chat文件夹工程导入eclipse编辑器中; 更改 DButil.java 里面的mysql服务器Ip 改成你的IP地址; 更改Constans.java 里面public final static String CON_...
1、Java允许创建不规则数组,即Java多维数组中各行的列数可以不同。 ( ) 2、接口和类一样也可以有继承关系,而且都只能支持单继承。 ( ) 3、所有类至少有一个构造器,构造器用来初始化类的新对象,构造器与类...
一个很强大的工具, 能将c#代码片段、文件甚至工程直接转换成java代码,并能彻底解决外部引用的DLL问题,最强的是支持c#工程的直接转换,生成的Java代码质量也很不错。软件已破解,去除了未注册版最多只能转换1000行的...
编写Java应用程序,求解一元二次方程,要求求出复数解。 2011-10-02 21:21:49| 分类: 程序设计|字号 订阅 对话框输出:import javax.swing.JOptionPane; public class quadratic_Equation { public ...
String verTime = new StringBuilder("-").append(new java.text.SimpleDateFormat("yyMMdd").format(cal.getTime())).append("0").toString(); String type = "YE3MP-"; String need = new StringBuilder...
二、实验内容或题目 1、编写一个Java Application字符界面程序,实现数论中的某个基本算法:最大公约数和最小公倍数的求 解。 2、编写一个Java Applet程序,利用图形界面输入一个数据,判断是否是回文数,并将结果...
4.3.1 java语言中输入/输出流 4.3.2 通过系统缓冲流类提高i/o操作效率 4.3.3 通过自定制缓冲区提高i/o操作效率 4.3.4 通过压缩流提高i/o操作效率 4.3.5 通过非阻塞i/o优化应用性能 4.4 其他 104 4.4.1 数据格式化与...
Java.Bug模式详解 第1章 混乱环境下的灵活方法 1.1 软件设计、实现和维护的趋势 1.1.1 对于稳定、安全 系统的需求增加 1.1.2 传统软件工程技 术的局限性 1.1.3 开放源代码的软 件项目的可利用性 1.1.4 对于...
lzstring4j-Java的LZString lzstring4j是Java的的易于使用的实现。 这个想法是受启发的。 特征: 基于JavaScript LZString库(版本1.3.3) UTF-16压缩 Base64压缩 通过这篇通过UTF-16压缩方法(Java Server-> ...