最近看到帖子上有几篇关于字符串传递的问题,下面就总结帖子所说的再详细讲述一下,有不对的地方希望指正!所用学过java的程序员们可能都知道对象的参数传递都是引用传递(所谓引用传递就是把对象的引用地址传进函数,而不是像值传递那样产生一个副本),对于字符串对象来说,虽然在参数传递的时候也是引用传递,但是java虚拟机在函数内部对字符串对象进行了特殊处理--视String对象为常量(final) 所以对传进来的引用地址所引用的string对象比能直接进行修改,而是产生一个副本对其进行操作,对其进行的操作不会影响原来的值。下面我们看一例子(sun认证试题)来证明这一点:1. public class X {
2. public static void main (String[]args) {
3. string s = new string (“Hello”);
4. modify(s);
5. System.out.printIn(s);
6. }
7.
8. public static void modify (String s) {
9. s += “world!”;
10. }
11. }你说它会打印出什么结果?是Helloworld?可能大部分人会说是这个答案吧,哈哈结果却是Hello,为什么呢?再仔细分析一下上边所说的吧-----“视String对象为常量(final)” 这是问题的关键!在modify()方法中传进了string对象,虽然是引用传递,上边我们说了视String对象为常量(final),它不会修改原来地址所引用的值,而是生成一个副本对其进行操作,所以打印结果应该为Hello!下面再讲一讲java中对常量(final)是怎样处理优化的,以便更深入的理解上面的问题:java中的final变量,java编译器是进行了优化的。每个使用了final修饰的变量的地方都不会通过连接而进行访问。比如说Test类中使用了Data类中一个final的int数字fNumber=77,这时候,java编译器会将77这个常数编译到Test类的指令码或者常量池中。这样,每次Test类用到fNumber的时候,不会通过引用连接到Data类中进行读取,而是直接使我们再来举个例子说明这一点:public class TestFinal(){public static void main(string s[]){System.out.println(FinalData.date);}public class FinalData(){public static final data=8;}}运行上面的TestFinal类结果为:8但是你把FinalData类中的data=88,编译后再运行TestFinal类结果还是8,这是因为编译器把data的副本保存TestFinal类中,所以在重新编译TestFinal类的前,TestFinal类一直把data认为是8而不是88,是不是证明了上面所讲述的每个使用了final修饰的变量的地方都不会通过连接而进行访问!结合这个例子,再深入的想一想第一个问题是不是就理解的更加清晰了
2. public static void main (String[]args) {
3. string s = new string (“Hello”);
4. modify(s);
5. System.out.printIn(s);
6. }
7.
8. public static void modify (String s) {
9. s += “world!”;
10. }
11. }你说它会打印出什么结果?是Helloworld?可能大部分人会说是这个答案吧,哈哈结果却是Hello,为什么呢?再仔细分析一下上边所说的吧-----“视String对象为常量(final)” 这是问题的关键!在modify()方法中传进了string对象,虽然是引用传递,上边我们说了视String对象为常量(final),它不会修改原来地址所引用的值,而是生成一个副本对其进行操作,所以打印结果应该为Hello!下面再讲一讲java中对常量(final)是怎样处理优化的,以便更深入的理解上面的问题:java中的final变量,java编译器是进行了优化的。每个使用了final修饰的变量的地方都不会通过连接而进行访问。比如说Test类中使用了Data类中一个final的int数字fNumber=77,这时候,java编译器会将77这个常数编译到Test类的指令码或者常量池中。这样,每次Test类用到fNumber的时候,不会通过引用连接到Data类中进行读取,而是直接使我们再来举个例子说明这一点:public class TestFinal(){public static void main(string s[]){System.out.println(FinalData.date);}public class FinalData(){public static final data=8;}}运行上面的TestFinal类结果为:8但是你把FinalData类中的data=88,编译后再运行TestFinal类结果还是8,这是因为编译器把data的副本保存TestFinal类中,所以在重新编译TestFinal类的前,TestFinal类一直把data认为是8而不是88,是不是证明了上面所讲述的每个使用了final修饰的变量的地方都不会通过连接而进行访问!结合这个例子,再深入的想一想第一个问题是不是就理解的更加清晰了
解决方案 »
- 有个java小程序,不懂里面的代码,麻烦哪位大虾指教
- java程序的环境变量的改变
- 请问如何得到Oracle SQLException的errorCode, 用SQLException.getErrorcode() 总是返回0
- 这段程序的语法错在哪里呀?????
- 菜鸟问题!
- 关于通过接口获得运行时多态性的问题~~谢谢~~
- 请教一个问题:如何让ButtonGroup的边框线显示出来;
- 新手上路问问程序里面的几个语句是什么意思
- 各位高手:
- 高手请务必请来,该问题困扰我三天了....我也不知道到底和什么有关,swing&socket&threads
- JAVA练习题,能做多少就做多少。
- 急!请问为什么使用定时器时,java.util.TimerTask中有获得数据库连接的任务不执行且无异常?
这跟final根本一点关系没有!别在这误人子弟了!就算不是String、final也是这个结果!至于为什么我已经在原问题的帖子里解释的很清楚了!你看换一个Integer对象也是一样的!
final 不final完全是另外的话题!!
public static void main(String[] args)
{
Integer i=new Integer(4);
modify(i);
System.out.println(i);
} public static void modify(Integer i)
{
Integer t=new Integer(5);
i=t;
}
结果也会输出4!
可能你误解我的意思了,在java虚拟机内部string对象是一final的形式存在的你这个是不可否认的吧,我是想通过java对final的处理说明上面的结果。
String对象在jvm怎么会以final的形式保存呢?你可能把intern和final的概念弄混了把。
String s=new String("hello");或者String s="hello"这种情形,jvm的确是将hello保存在
Constant Pool中。但是象final String s=aFileInputStream.readLine(),肯定不会把s做为常量保存的……
String对象跟一般对象的区别是有些String对象可能被intern()。
比如String s="a",和String x="a",s==x会返回true
那么Integer类也是final
public static void main (String[]args) {
string s = new string (“Hello”);
modify(s); //这里确实传递了s地址
System.out.printIn(s);//s指向了原对象
}
public static void modify (String s) {
s += “world!”; //这里是创建了一个新对象而已,s指向了创建后的新对象//上面的S+="world"在编译时的处理应该是这样的
//new java/lang/StringBuffer;创建一个StringBuffer对象用于处理+号操作
//dup 复制StringBuffer对象产生StringBuffer的副本并至于操作数栈顶
//aload 1 把s对象从局部变量区域加载到操作数栈
//invokestatic java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;//转换s对象为String类型
//invokespecial java/lang/StringBuffer.<init>(Ljava/lang/String;)V;初始化StringBuffer对象
// ldc "world" 加载字符串"world"到操作数栈
//invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;//调用StringBuffer的append方法追加字符串"world"
//inovkevirtual java/lang/StringBuffer.toString()Ljava/lang/String;这里调用StringBuffer对象的toString()方法把构建好的字符序列包装成一个字符串对象
//astore 1将StringBuffer的toString()方法创建的String对象赋给变量s
}
}