Archive for 09月, 2008

详解Java里的堆和栈(转载,exellent!)

星期三, 09月 24th, 2008

在C语言里堆(heap)和栈(stack)里的区别
简单的可以理解为:
heap:是由malloc之类函数分配的空间所在地。地址是由低向高增长的。
stack:是自动分配变量,以及函数调用的时候所使用的一些空间。地址是由高向低减少的。
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表
在Java语言里堆(heap)和栈(stack)里的区别

1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共 享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要 在运行时动态分配内存,存取速度较慢。
3. Java中的数据类型有两种。
一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出 后,字段值就消失了),出于追求速度的原因,就存在于栈中。
另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处 理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状 态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例, 我们定义完a与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存 放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。
4. String是一个特殊的包装类数据。即可以用String str = [...]

A Skeleton in the Cupboard.

星期一, 09月 15th, 2008

A Skeleton in the Cupboard.(橱中骷髅)是国外的谚语,我们经常在小说里读到,一个外表看起来很受人尊敬的人或家庭,多年来一直隐藏着一些不为人知的可怕的秘密。 在故事的某个戏剧性时刻,这种骇人听闻的秘密--即“橱中骷髅”让人识破了,于是便名誉扫地。
现常常指每个人或每个家庭都有不可告人的秘密。

Be thankful for what you have

星期三, 09月 10th, 2008

Be thankful for what you have and you will have more.
If you always concentrate on what you do not have you will never ever have enough.
by Oprah Winfrey
何必总是对自己得不到的耿耿于怀,其实现在身边的一切更值得珍惜。
人在大四,发现自己比以前更成熟了,也更懂得珍惜了。
有 时候自己努力回想大学以前的生活,其实和现在一样,也是一点一点的从眼皮底下、从指缝中间流走的,可是在我的脑海里留下的记忆却很少,感觉自己的从前就像 是空白一片。以前自己对一切都过于苛求,总是想着前面的事情,希望有个好的前景,却从没有回头看看走过的路,没有珍惜身边的人。
有句广告 词说得好“人生就像一场旅行,不必在乎目的,要在乎的沿途的风景和看风景的心情。” 一个人即使取得再大的成功,登到不胜寒的高处,但是如果他回首这一路行程心里却没有一点儿情感的积淀和沿途风景的轮廓,可以说这趟旅行是不成功的。人生的 确就像一趟旅行,A one way trip,没有回头的路,失去的就是失去了,永远的失去了。
前天晚上,给妈妈打了个电话。这种主动 给家里打电话的行为平时是很少在我身上发生的,以前每次要往家里打电话的时候总是感觉还有其它事情要做而最终搁浅了,甚至老妈打电话过来的时候我也总是想 早点结束,感觉“就那点儿事儿,也没什么说的”。现在的感觉不一样了,我总是想给家里打电话,想对他们说“我爱你”,记得补英语口语的时候,Sandy说 过“Say it before it’s too late”。这就是人生,Any chance could be the last chance.
对拥有的一切心存感恩,对想要拥有的好好准备、努力争取。希望能在品味成功之余,品味生活的精彩!

A great day(2008-0827)

星期三, 09月 10th, 2008

会永远记住今天的!不仅在游泳上有了很大的进步(呵呵,I‘m a rookie @_@,两位师傅走了之后,自己又游了一个来回,哈哈,对”rebuild my upper body”非常有信心了),而且了却了一桩心事。挺不错的!哈哈。
人与人之间有很多误会,我认为没有人真正了解其他人。一个人可能在某些人眼里就是个纯粹的人渣,但是谁又真正了解这个人渣呢?就连和他最亲近的人,我想,也不敢打百分之百的保票,说“我非常了解这个人”。
所以,我对自己说:“不管自己在别人眼里什么形象,做好真正的自己!为自己活着,为真正爱你的人快乐、伤心,其它的,谁管呢?”
呵呵,每天多一些笑容,去爱身边的这个世界。Time doesn’t lie to any body.
感谢身边的每一个人,珍惜自己所拥有的一切!
ps:
TOMaster JBer 和 Master B,谢谢!

最郁闷的事情(2008-08-26)

星期三, 09月 10th, 2008

发发牢骚。
其实心里一直不舒服,我想纵使心再大的人,我想遇到这种情况可能也会和我同感。
在两个男人之间发生了冷战……
以前关系很好,现在却如同路人,真让人伤心!
The key point is I just DON’T KNOW WHY! Give a reason.

About Java数组创建

星期一, 09月 8th, 2008

String s = new String(”abc”);创建了几个String对象?

引用变量与对象的区别;
字符串文字”abc”是一个String对象;
文字池(pool of literal strings)和堆(heap)中的字符串对象。
一、引用变量与对象:除了一些早期的Java书籍和现在的垃圾书籍,人们都可以从中比较清楚地学习到两者的区别。
A aa;
这个语句声明一个类A的引用变量aa[我们常常称之为句柄],而对象一般通过new创建。所以题目中s仅仅是一个引用变量,它不是对象。
二、Java中所有的字符串文字[字符串常量]都是一个String的对象。有人[特别是C程序员]在一些场合喜欢把字符串”当作/看成”字符数组,这也没有办法,因为字符串与字符数组存在一些内在的联系。事实上,它与字符数组是两种完全不同的对象。
System.out.println(”Hello”.length());
char[] cc={’H’,’i’};
System.out.println(cc.length);
三、字符串对象的创建:
由于字符串对象的大量使用(它是一个对象,一般而言对象总是在heap分配内存),Java中为了节省内存空间和运行时间(如比较字符串时,==比equals()快),在编译阶段就把所有的字符串文字放到一个文字池(pool of literal strings)中,而运行时文字池成为常量池的一部分。文字池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。
我们知道,对两个引用变量,使用==判断它们的值(引用)是否相等,即指向同一个对象:
String s1 = “abc” ;
String s2 = “abc” ;
if( s1 == s2 ) System.out.println(”s1,s2 refer to the same object”);
else System.out.println(”trouble”);

这里的输出显示,两个字符串文字保存为一个对象。就是说,上面的代码只在pool中创建了一个String对象。
现在看String s = new String(”abc”);语句,这里”abc”本身就是pool中的一个对象,而在运行时执行new String()时,
将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有。ok,这条语句就创建了2个String对象。

String s1 = new String(”abc”) ;
String s2 = new String(”abc”) ;
if( s1 == s2 ){ //不会执行的语句}

这时用==判断就可知,虽然两个对象的”内容”相同(equals()判断),但两个引用变量所持有的引用不同,
BTW:上面的代码创建了几个String Object? (三个,pool中一个,heap中2个。)

关于 JAVA Exception

星期六, 09月 6th, 2008

异常的概念  
       任何的异常都是Throwable类(为何不是接口??),并且在它之下包含两个字类Error / Exception,而Error仅在当在Java虚拟机中发生动态连接失败或其它的定位失败的时候,Java虚拟机抛出一个Error对象。典型的简易程序不捕获或抛出Errors对象,你可能永远不会遇到需要实例化Error的应用,那就让我们关心一下Exception。
       Exception中比较重要的就是RuntimeException(运行时异常)-可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在 throws 子句中进行声明,也就是说你的应用应该不去“关心”(说不关心是不服责任的,但只是你不应该试图实例化它的字类)。  RuntimeException,就如同你不应该关心Error的产生与处理一样!RuntimeException描述的是程序的错误引起来的,因该由程序负担这个责任!(从责任这个角度看Error属于JVM需要负担的责任;RuntimeException是程序应该负担的责任;checked exception 是具体应用负担的责任)
       除了Error与RuntimeException,其他剩下的异常都是你需要关心的,而这些异常类统称为Checked Exception,至于Error与RuntimeException则被统称为Unchecked Exception.

         关于 Java 中引入的 Checked Exceptions,目前存在着很多反对意见。正方的观点是引入 Checked Exceptions,可以增加程度的鲁棒性。反方的观点是 Checked Exceptions 很少被开发人员正确使用过,并且降低了程序开发的生产率和代码的执行效率。

Java 中定义了两类异常: 
1) Checked exception: 这类异常都是Exception的子类 。异常的向上抛出机制进行处理,如果子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。C#中就没有使用这种异常机制。
2) Unchecked exception: 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是特殊的,它们不能通过client code来试图解决,所以称为Unchecked exception 。  
   
(JAVA视线论坛robbin’s view,个人觉得用来做业务流程控制违背了Exception设计的初衷,但可以借鉴一下)
     在使用UseCase来描述一个场景的时候,有一个主事件流和n个异常流。异常流可能发生在主事件流的过程,而try语句里面实现的是主事件流,而catch里面实现的是异常流,在这里Exception不代表程序出现了异常或者错误,Exception只是面向对象化的业务逻辑控制方法。如果没有明白这一点,那么我认为并没有真正明白应该怎么使用Java来正确的编程。 
      而我自己写的程序,会自定义大量的Exception类,所有这些Exception类都不意味着程序出现了异常或者错误,只是代表非主事件流的发生的,用来进行那些分支流程的流程控制的。例如你往权限系统中增加一个用户,应该定义1个异常类,UserExistedException,抛出这个异常不代表你插入动作失败,只说明你碰到一个分支流程,留待后面的catch中来处理这个分支流程。传统的程序员会写一个if else来处理,而一个合格的OOP程序员应该有意识的使用try catch 方式来区分主事件流和n个分支流程的处理,通过try catch,而不是if else来从代码上把不同的事件流隔离开来进行分别的代码撰写。

(另外一种观点,不同于robbin,个人赞同,并引用于此)
1。什么时候抛出异常–涉及到服务类
2。抛出checked还是unchecked的异常–涉及到客户类
       对第一个问题来说,我想异常本身这个字解释了某些东西,异常就是我们认为在正常情况下不可能发生的问题,并且服务代码不知道如何去处理。譬如说我做一个监控程序,需要用压缩卡提供的API去初始化所有的板卡,API提供的是boolean型的返回值,但我把这个API变成抛出一个异常,因为除非特殊原因,我不认为会发生初始化失败的情况,当然更不知道怎样去处理这个问题。又譬如Hibernate里面的LoadObject使用没有发现这个对象存在,那Hibernate也是认为不可能的,除非其他代码直接删除了数据库里面的记录,那么也需要抛出异常。当然Hibernate本身也不知道如何处理这种情况。
       但是如果发生的情况是可以预期的,那我不认为应该抛出例外。象上面这个userExist的情况,我认为应该在前面已经分流,应该首先判断这个用户是否存在,if(userExists()),然后进行处理,而不应当抛出例外。以及login应当返回true或者false。也就是说,这些属于程序的正常流程,而不是例外,不是异常。把例外作为正常程序流程的控制机制,只不过是把服务代码中的if转移到客户代码去,没有减少任何需要处理的代码,反而增加了系统的负担(生成例外栈)。
       还有抛出异常的情况是违反方法的先决条件,每一个方法都有自己的先决条件和后置条件,方法只有在正确的前提下才能执行达到一个正确的后果,(所谓类的不变量)。譬如你去存取一个数组的某一个元素,这个存取方法有一个前提条件,就是你的索引应当落入它的最大下标和最小下标之间,不然就应当抛出一个例外。
         对于第二个问题,端视于客户代码是否能够根据这个例外进行合理的处理。如果客户代码根本就不知道如何处理这个例外,应当把它作为一个unchecked例外,例如上面下标的问题,客户代码用一个不合法的下标来存取数组,那么抛出一个checked例外以后,客户代码是+1还是-1?显然根本就不可能做出“合理的”处理,客户既然不能处理,还要强制它去处理,那么就是捕获,打印了事,没有增加任何价值。但是如果是客户可以处理的,或者可以选择不同的方式处理的,那么就可能需要用checked,但我发现很少有这样的情况。对于类似于RemoteException或者SQLException这些Exception,我一般都转换为具体的业务Exception,而我所有的业务Exception都是RuntimeException.
         所以我的观点是,是否抛出例外就是服务代码是否进行合理的处理,抛出什么类型的例外就是客户代码是否能够合理的处理。

保护封装性的代码实例:
      不要让你要抛出的checked [...]

Recent Posts:
  1. I'm still fighting for a good future
  2. C++内存分配方式(转载)
  3. 详解Java里的堆和栈(转载,exellent!)
  4. A Skeleton in the Cupboard.
  5. Be thankful for what you have