楼主精神可嘉,值得多多鼓励。 建议还可以适当更加深入一些,如 String s1 = "This is cool!"; String s2 = "This is cool!"; String s3 = new String("This is cool!"); s1 = s2; s3 = "This is cool!"; 这样的代码共有产生多少String实例,有多少对象可以被GC等等。UP~UP~
String s1 = "This is cool!"; Objnum=1; String s2 = "This is cool!"; Objnum=2; new String("This is cool!"); Objnum=3; String s3 = new String("This is cool!");Objnum=4; s1 = s2; Objnum=4; s3 = "This is cool!"; Objnum=5;Object number is 5, isn't it right?
to 大胃: “还有就是如果搞不清String s = new String("YES");和String s = "YES";的区别,容易造成new String()的滥用导致程序变慢。” 会变多慢?哪怕你new了一万个多余的String,会比一次数据库操作或者RMI慢吗?我之所以一直说这个问题没意义,就是这个意思。
To Schlemiel(维特根斯坦的扇子):呵呵。也许你是对的,可能我从很早以前做C/C++的时候就养成一个习惯,要节约资源,放在现在的硬件平台和较新的JVM上恐怕是不那么重要了,那么如果开发J2ME的东西,还能任由其new么?并不是所有的时候都会用到数据库和RMI,如果用到了,就更应该注意节约。我觉得这个习惯始终是好的,如果能不费多大力气就节省了很多资源,何乐而不为呢?把这个"重用"的思想推而广之,举一个极端点的例子,如果把一个大的类放到了循环中间来new,比如Calendar,而不是在循环外new了然后在循环中修改和使用,假定会循环上百次甚至上千次的话,你会发现代码速度的差异有好几十倍。"To new or not to new? That is the question." - Seanspeare :)To all:我可能也没有很好的表达我的意思。我觉得Java的简洁和美丽让不少人忽略了必要的节俭,如果不加控制,等做出一个大项目,恐怕不管JVM怎么先进,怎么优化,怎么JIT,你还是会抱怨Java太慢吧?我希望初学者从一开始就重视这个问题,养成好的习惯,对今后的开发很有帮助的。Java毕竟不是OTC拿来就可以吃,要懂得它的配方才是上策。
to satangf(好好学习,天天向上!) final关键字的问题我们很快会在后继的帖子里讨论的。这个问题和本系列的第二个问题也有关系。
很简单,也很容易遗忘的知识点。
建议还可以适当更加深入一些,如
String s1 = "This is cool!";
String s2 = "This is cool!";
String s3 = new String("This is cool!");
s1 = s2;
s3 = "This is cool!";
这样的代码共有产生多少String实例,有多少对象可以被GC等等。UP~UP~
String s2 = "This is cool!"; Objnum=2;
new String("This is cool!"); Objnum=3;
String s3 = new String("This is cool!");Objnum=4;
s1 = s2; Objnum=4;
s3 = "This is cool!"; Objnum=5;Object number is 5, isn't it right?
当初就曾犯过这个错误
还是看==和equals的区别时才搞懂的
期待第二个问题
楼主期望初学者弄懂的事就是:java语言中,对象传值传的是引用,而不是对象本身。这个基本概念涉及到到很多问题,例如“为什么一个对象声明为final却还可以改变”等等。个人认为这是个极重要的概念。
我还是觉得没啥用。有错没错,写几个单元测试一测不就知道了吗?
package csdn;public class Test3{
public void foo(){
String s1="s1";
String s2=s1;
String s3=new String("s3");
}
}字节码:
public class csdn.Test3 extends java.lang.Object{
public csdn.Test3();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: returnpublic void foo();
Code:
0: ldc #2; //String s1
2: astore_1
3: aload_1
4: astore_2
5: new #3; //class String
8: dup
9: ldc #4; //String s3
11: invokespecial #5; //Method java/lang/String."<init>":(Ljava/lang/Strin
g;)V
14: astore_3
15: return}foo()方法被调用时的方法桢的结构:分三部分 ,局部变量数组,桢数据区,操作数栈
每个方法的局部变量数组的大小
以及
操作数栈的大小在编译时就已经确定了,
你在java源码中声明的每一个局部变量都会对应局部变量数组中的一项(也有可能多个对应一项),
实例方法的局部变量数组的第0项总是存放this
+-----------+ <----
| this | 0
+-----------+
| s1 | 1
+-----------+ local variables 局部变量数组
| s2 | 2
+-----------+
| s3 | 3
+-----------+ <----
| |
| | frame data area 桢数据区
| |
+-----------+ <----
| |
+-----------+
| | op stack 操作数栈
+-----------+
| |
+-----------+ <----分析每一条语句:
String s1="s1";
===>
0: ldc #2; //String s1 //从常量池中取得字符串常量"s1"并压入操作数栈
//(这个字符串常量在这个类载入并解析时就已经放入到了intern string table中了)
2: astore_1 //从操作数栈弹出一项并保存到局部变量[1]中
String s2=s1;
===>
3: aload_1 //把局部变量[1]的内容压入操作数栈
4: astore_2 //从操作数栈弹出一项并保存到局部变量[2]中
String s3=new String("s3");
===>
5: new #3; //class String //为一个String对象分配内存,并把它的地址压入操作数栈
8: dup //复制操作数栈顶端的元素,并把它压入操作数栈
9: ldc #4; //String s3 //从常量池中取得字符串常量"s3"并压入操作数栈
11: invokespecial #5; //Method java/lang/String."<init>":(Ljava/lang/String;)V
//取得操作数栈顶的二项,并调用String类的构造函数
14: astore_3 //从操作数栈弹出一项并保存到局部变量[3]中
1. 你认为这个问题没什么意义
还是
2. 你认为我没有把这个问题的重要意义讲清楚。
单元测试是应该的,但是事先就排除错误总归比事后发现错误再改正要保险些。
例如“为什么一个对象声明为final却还可以改变”等等。能否详细解释一下?to zcjl(【to be forgotten..】) :
还是看==和equals的区别时才搞懂的也能否具体说说?学习,强烈学习!
盼望高手讲解!
这样就好理解了,这个引用的值不能改变,也就是只能指向第一次赋给他的对象,
但是它指向的对象的内容是可以改变的。举个例子,
final StringBuffer sb=new StringBuffer("123");
你可以改变对象的内容
sb.append("456");
但是你不能改变引用的值
StringBuffer sb1=new StringBuffer("abc");
sb=sb1;//这句出错。
String s2 = "This is cool!"; // 将同样的对象[OBJ_1]的引用赋值给s2
String s3 = new String("This is cool!"); // 新建一个字符串对象[OBJ_2],其引用赋值给s3;
s1 = s2; // 将s2保存的[OBJ_1]的引用赋值给s1,没有实际效果
s3 = "This is cool!"; // 将[OBJ_1]的引用赋值给s3,s3原先引用的[OBJ_2]不再被引用这样的代码共有产生2个String实例,有1对象可以被GC。
To Schlemiel(维特根斯坦的扇子):这些基本问题搞清楚了当然有用啦,像上面很多人已经提到的by-reference还是by-value的问题,搞不清楚的话可能有些bug很难找;还有就是如果搞不清String s = new String("YES");和String s = "YES";的区别,容易造成new String()的滥用导致程序变慢。//关于by-reference和by-value,说法还不是很统一,有人说参数传递本来就都是by-value的,因为确实我们用来代表和操作对象的变量其实是对象的引用,这个引用和对象本身是不同的。推荐大家看两本好书:Effective Java和Practical Java,很多初学者搞不清楚的问题,还有很多我们平时编码不大注意的问题都提到了。
String string = s;
------------------------------
真得很头疼,请问楼主这个过程内存是怎么分配的?
“还有就是如果搞不清String s = new String("YES");和String s = "YES";的区别,容易造成new String()的滥用导致程序变慢。”
会变多慢?哪怕你new了一万个多余的String,会比一次数据库操作或者RMI慢吗?我之所以一直说这个问题没意义,就是这个意思。
final关键字的问题我们很快会在后继的帖子里讨论的。这个问题和本系列的第二个问题也有关系。
同意,虽然我目前写的程序就是一团糟
:(
String[] s = new String[5];
当执行这一句时,在堆里开一个String数组的对象,引用为s, 这个s是在栈的。但s指向的对象是一个含有5个指向String对象的引用的对象,这些引用依次为s[0],s[1],s[2],s[3],s[4]注意它们可不在栈里,而是在数组对象的堆里。
文字说的比较饶口,图示就明了了: Stack Heap
__________ _________
|__________| ---> |__s[0]___|--
|__________| | |__s[1]___| |
|__________| | |__s[2]___| |--堆中String数组对象
|__________| | |__s[3]___| |
|_____s____|----- --|__s[4]___|--
| |_________|
| :
| :
| _________
->|__null___|--
|_________| |
|__null___| |---(堆中的String对象,由s[i]分别指向,不
|__null___| | 一定是连续空间也不一定按s[i]的顺序。
|__null___| |
|__null___|--