应用,一定要应用

BlogJava 首页 新随笔 联系 聚合 管理
  25 Posts :: 0 Stories :: 118 Comments :: 0 Trackbacks

#

2004-08-31

构建型模式和结构型模式强调的都是静态的类实体之间的关系,行为型模式着力解决的是类实体之间的通讯关系。希望以面向对象的方式描述一个控制流程。
以往的经历中大多是解决类实体的封装问题,对于类之间的通讯方式上,并没有刻意的去理解,所以有些行为型模式相对陌生。
还有一个原因就是行为型模式和面向过程的东西有很多近似之处,导致一定的理解的混淆。
从笔记就可以看出理解的不够,可以再专门针对行为型模式做些细节的学习。

1 chain of responsibility(职责链)-行为型对象模式
这是个“请求”处理的模式。他提供一个思路,来解决“请求”在一组具有一定结构的类之间传递时的问题,所以,我认为这只是一种思路,一种将“请求”象链条一样传导出去的方式。他的具体传递方式,除了和链的设计有关之外,最重要的是和对象组的结构有关。
当然,如果没有结构的对象组,我们也可以强行的动态构建一个链,这种方式虽然复杂和略土,但肯定更加的灵活,不仅能控制处理,还能控制链的走向。

一般来看,composite模式和这种模式经常在一起,Microsoft IE中的文档事件模型应该采用的就是这样的模式。

2 command(命令)-行为型对象模式
将请求封装为一个对象,从而可以对请求做一些统一性处理,如:排队、日志、撤销等。
适用性:
1、是回调机制的一个面向对象的替代品
2、在不同时期指定、排列、和执行请求
3、支持取消操作,但是,此处对象的状态存储是个麻烦的东西。
4、支持原语操作上构建高层操作的系统。这种在支持“事务”的系统中很常见。
理解还是不很透彻

3、Iterator(迭代器)-行为型对象模式
将聚合类的遍历算法封装为Iterator,从而封装对象的内部表示。
Iterator分为外部和内部,外部更加灵活,内部传入参数简单(只需要操作),内部Iterator自动遍历所有元素。
Iterator可以封装算法,也可以只记录状态,由聚合类封装算法,封装算法的iterator可能会破坏聚合类的封装性。

4、mediator(中介者)
mediator希望通过一个中控对象来完成对多个对象的关系的管理。
将多数可以可以重用的组件无关联的构建起来,而他们之间的关系由额外的对象来完成。
在图形化编程里,可以用此模式和listener机制(observer模式)结合使用,更加方便,进一步解除了中控对象与其他对象的耦合。

5、memento(备忘录)/token
原发器(originator)(需要保存状态的)申请一个备忘录(memento),并自己将状态保存进去,然后,将备忘录交给管理者(caretaker),当出现需要的时候,管理者将合适的备忘录交给原发器,原发器自己恢复自己的
状态。
memento模式,从originator中分离了保存客户请求状态的过程。并且memento的存储和解释都是由originator完成,保证了封装的边界。
如果备忘录的创建及其返回(给原发器)的顺序是可预测的,备忘录可以存储增量改变。

6、observer(观察者)/依赖(dependents)/发布-订阅(Publish-Subject)
suject(目标)和observer(观察者)是关键对象,suject和不固定的observer发生关系,将这种关系解耦是这个模式的主要功能,listener机制可以看作这种模式的一个实现,当然mvc也是这种模式的一个好的应用场景。
与mediator的区别主要体现在,observer是一个对关联关系的解耦过程,而mediator更加注重各个对象执行的功能。

7、state(状态)/状态对象(Objects for Status)
允许一个对象在改变其状态的时候改变它的行为,使得对象似乎修改了自己的类。
1、将状态相关的行为局部化。
2、状态转换显式化

我们可以根据当前窗口的状态来转变窗口的菜单和操作,这种方式可以用state模式来实现。

8、strategy(策略)
将算法单独的封装起来,使得它独立于其他对象的变化。
其实这种方式和面向过程里的一个算法函数封装基本是一样的,只是由于封装成为了类,会有一些接口统一的便于替换的特性。

9、visitor(访问者)
这种模式的主要目的是分离操作和对象,追求的是对象的重用和简单,以及操作的可替代性。
相对来说,strategy模式更加追求的是封装操作,追求操作的重用性。
observer、mediator追求分离对象之间的关系与对象本身。


以上都是对象级别的行为模式,以下是类级别的行为模式
1、template(模板)
定义一个算法的骨架,并将其中一些步骤延迟到子类中。
这种使用较多,不多说了。

2、interpreter(解释器)
本模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。
这种方式在正则表达式中比较明显,在以往的程序应用中,特别是上层应用中很少用到。
expression::= literal | alternation | sequence | repetition | '(' expression ')'
alternation ::= expression '|' expression
sequence ::= expression '&' expression
repetition ::= expression '*'
literal ::= 'a'|'b'|...{'a'|'b'|...}*

相关的关键参与者为:
1、abstractExpression(抽象表达式,统一的接口)
2、terminalExpression(终结符表达式)
3、nonterminalExpression(非终结符表达式)

posted @ 2006-08-24 17:10 flyffa 阅读(1159) | 评论 (0)编辑 收藏

2004-08-30

结构型模式:描述的是一系列将类和对象进行组合,以构建更大的类和对象。其强调的是结构化的方式,而不是怎样去构造这些类。

结构型模式分为两种:
a、结构型类模式:通过接口或类的继承来实现。
b、结构型对象模式:不是对类或接口,而是对对象进行组合。

/*****************************************************************************************/
1、Adapter(适配器)/wrapper(包装器)
适配模式最重要的作用是去将已有的、但是接口不兼容的东西接入到系统中来。进行复杂逻辑程序的有效重用。
这个模式中adaptor,adaptee的基类是具有惟一性的。

a、类适配
类适配就是通过继承这种静态的方式来进行适配,也就是适配器会继承需要被适配的东西,
缺点是适配的范围比较小,不能满足同时适配一组对象(类及其子类)的要求。

b、对象适配
对象适配:适配器并不会静态的继承,而会是采用引用需要被适配类的方式,这样,被适配类及其子类都可以被适配了,
缺点:需要动态的指定被适配对象,而且容易引起运行期错误


注:可能是对于其他面向对象语言的不了解,我觉得用对象适配就可以了,没有必要使用类适配的模式,对于只有一个需要适配的类的情况,我们只需要将动态构造引用对象的过程加在适配器的构造函数中就可以了,在今后对其他类型的OO语言有了了解之后,可以再回顾一下此处。


/*****************************************************************************************/
2、bridge(桥接)/(Handle/boby)---结构型对象模式
桥接的模式最重要的作用是将抽象/表现与实现相分离,保持程序良好的可扩展性。
这个模式中window和windowimpl这两个接口是具有惟一性的。

一个一般的继承模式的设计
window--xwindow
 pmwindow
 iconwindow--pmiconwindow
      xiconwindow
这种树状的继承方式将抽象的表现部分iconwindow和抽象的实现部分(xwindow,pmwindow)集成到了一起。
而这两者其实负责不同的东西,一是表现(外部特征,如icon是可以拖拽的,是由矩形组成的等等),二是具体实现平台,负责在xwindow风格下的线如何画,线有多粗,从什么位置开始画等等。

于是应该有着两条线的结构:
window--iconwindow
 applicationwindow

windowimpl--xwindow
     pmwindow

中间通过window对windowimpl的引用来达到桥接的作用,也就是说,桥接的奥秘就在于此,合理的将这两者的接口进行分离,是桥接模式设计的关键。

桥接方式的退化形式就是我们只是需要实现xwindow或者pmwindow中的一种,这样windowimpl就失去意义了。(对于有些语言,windowimpl的存在可能有利于实现的改变不需要重新编译客户应用程序)  

/*****************************************************************************************/
3、composite(组合)---结构型对象模式
组合模式使得单个对象和组合对象的使用具有一致性。使得对外提供的接口变得单一,用户忽略单个对象与组合对象的差异。
组合模式着力于解决这样一个矛盾:
在一个程序组中有着层次的关系,需要表现(这是一个内部重用性和易读性的问题)
但是这个程序组中的对象有着共性,并且希望外部调用程序忽视其各异性而只是关注其共性的部分(这是一个外部接口简单化的问题)。
所以其主要元素的关系就是这样了:

composite(复合对象)-leaf(简单对象)
      composite(复合对象)-leaf(简单对象)
     leaf(简单对象)

而这些对象实现统一的接口或者抽象类:compositeInterface;任何client对任何对象实例的操作都是通过接口进行。

模式的实现:
在实现中会有一系列的问题,这只是实现的问题,和模式本身无关。
1、显示父对象引用:解决方法:建立一个稳定,模式化的父子引用关系和操作方法。
2、共享组件:这将父子之间的1:N的关系变成了N:N的模式,参考flyweight模式。
3、最大化component接口:这是保证操作透明化的必须,我们应该尽力达到这个目的。
4、声明管理子部件的操作:这个问题最大的麻烦来源于子部件和父部件最大的差异--有没有子节点,可以通过统一的,技巧性的add,remove方法来完成。
5、compositeInterface是否应该实现一个compositeInterface列表:这是一个关于存储的问题,就是对于子节点的引用对于叶子节点是无意义的内存消耗。
6、子部件排序:Iterator模式应该有一定的作用。
7、高速缓存改善性能。


/*****************************************************************************************/
4、decorator(装饰)/包装器(wrapper)---结构型对象模式
从名字我们就可以看出这个模式的基本立意:一位画家画完一幅画(实现一个组件)之后,并不需要自己去做画框(decorator),做这个画框就是这个模式需要解决的问题。
可以看到画框能为画家所用是基于一种前提的,就是画家的用纸和画框的大小是符合的,也是基于一种结果,就是画还是画,不会因为画框而改变其画的本质和内容,只是现在是一幅有框的画了。
回到模式中,我们可以看到这个模式的大概了:

component(组件接口)-concreteComponent(组件实现)
     -decorator(装饰器)-->m_cmp(对组件实现的引用)

这里我们可以看到decorator模式和adaptor模式的区别,decorator着力于装饰器部分的重用,而adaptor只是着力于组件的重用。decorator着力于封装组件的可以插件化的共性特征,其着眼点是组件级别以下的功能重用。adaptor着眼的还是组件一级的功能重用。

/*****************************************************************************************/
在前面的composite和decorator模式中,我们不停的追求将组件实现得更加的细粒度化,以便增加组件的可重用性,这基本是各种良好设计的共同特征。
但是这种设计方式也会导致各种问题,其中就包括零散、以及大量对象实例导致的资源消耗问题。
/*****************************************************************************************/
5、facade(外观)--结构型对象模式
facade模式最大的特点就是将复杂、零散的子系统进行唯一入口的封装。
这种模式在我经历的系统印象最深的就是多层分层的层层之间的接口上,一个唯一的入口,让层次非常的清晰。

其实这种模式与adaptor有一定的相似之处,都是屏蔽两个不同的子系统,但是不同的是,facade是主动的构建,而adaptor是为了接入其他的系统而被动的构建的,可以看出,要对一个已经实现了facade模式的子系统构建adaptor远比没有没有实现facade模式的要简单的多,代价要小得多。

/*****************************************************************************************/
6、flyweight(享元)--结构型对象模式
享元设计模式着力解决的就是当内存中存在大量同样的对象实例导致的资源消耗问题。
可以几个方面来理解,
享元模式追求的是实例级别的重用。
将外部状态(上下文)和对象实例本身分离,不是在构建时传入外部状态,而是在运行期,甚至这种状态完全是过程性的,和对象实例没有关系。而内部状态只在创建时指定,而在运行期是绝对不可以碰的。

这种模式与原型模式的最大区别在于,一个共享实例级别的,一个是共享类的级别。

/*****************************************************************************************/
6、proxy(代理)/surrogate--结构型对象模式
常见使用情况
1、远程代理(remote proxy)
2、虚代理(virtual proxy):根据需要创建开销很大的对象。
3、保护代理(protection proxy):控制对原始对象的访问。
4、智能指引(smart refrence):取代简单指针,便于在访问对象前做些附加操作。
这种代理使用广泛,什么copy_on_write,lazy_loading等技术都可以考虑这种方式的实现。

proxy模式代理和被代理对象接口是一致的或者是子集,但adaptor模式则不同。
decorator模式为对象增加接口,而proxy是转发或者限制接口。

posted @ 2006-08-24 17:09 flyffa 阅读(1080) | 评论 (0)编辑 收藏

2004-05-25

每次看设计模式这本书的时候都有着相同的感觉,上次没有真的理解,这次才是理解了,但是还差那么一点点。
人其实挺奇怪的,这些模式在编程的几年之内,无论是具体的项目中还是自己的练习中,都使用了很多,只是没有作为一种模式总结出来而已,而是以经验的形式存在于脑海之中,随意的使用,随意的忘却。
可能就是必然的学习过程吧,随意使用--形成模式--突破模式灵活应用。而模式在这里起着把经验持久化的作用吧,减少忘却的可能。
所以,真正的掌握模式,除了需要理解模式的表现之外,需要理解模式着力解决的问题,理解模式的缺陷和优势,才能做到最终灵活抽取各个模式之长而灵活组合之,这可能就是我在模式领域追求的无招胜有招的境界吧。

模式的核心目的大多是两种之一:重用和解耦

言归正传,还是来说说具体的模式吧:Design patterns


模式分类:构建型模式
模式一:抽象工厂(Abstract Factory)
Factory理解为对象工厂的概念,用以生成一个系列的对象组。
而Abstract Factory就是基于Factory之上的概念,目的是把多个系列的对象组抽取共性,屏蔽不同系列之间的对象的差异。

 系列     motif       windows 
对象组
Button    motifBtn    wBtn
Bar       motifBar    wBar
Textbox   motifTB     wTB

class abstractFactory{
 Button getButton();
 Bar getBar();
 Textbox getTextbox();
}


button,bar,textbox是一组对象,而我们需要实现motif,windows等不同系列风格的对象组,Abstract Factory提供的方式下,我们对于大多应用程序而言,操作的口是abstractFactory,以及Button,Bar,Textbox这些对象组,而无需具体关心是motif风格还是windows风格的,风格系列的指定只决定于实现abstractFactory的具体工厂类。

这种方法便于这种系列的增加,但是并不有利于对象组成员的增加,比如要增加一个label对象,我们就需要增加一个抽象对象Lable,还需要修改abstractFactory以及他所有的子类。

所以使用这种方法的时候,最重要的是对象组的规划设计。


模式二:生成器(builder)
生成器的概念是提供一系列的方法生成对象的不同部分,最终返回一个完整的对象,着力于解决将构造方法和构造步骤分离的问题,builder内部封装的是构建方法而不是过程。
做了这样的分离之后,再将builder做一个抽象,从而使得不同的构建方法和构建过程做到分离。

假设一个串的存储管理:
class builder(){
 add(key,value);
 addStr(str);
}

同样的操作集合,
add(key,value),addStr(str)
对于不同的builder,可能输出的结果就会有多种:
key=value;str

另一种可能:

 value
 str

模式三:工厂方法(Factory)
其实他的另一个名字更加能够说明问题(虚构造)
也就是说通过抽象类的共同方法来避免构造时的硬编码,
如:button对象有个getBtn的方法,
就可以避免写出button btn = new motifButton();这样的代码,而将这样的代码封装到了子类自身。


模式四:原型法(prototype)
就是在应用时并不是创建一个新的对象,而是从已有的对象中拷贝一个。
这种方式我在做配置池的时候用到过,每个业务程序从我的池中去拷贝一个对象,然后才进行相关的修改和应
用。
这种方式同样是动态创建类的方法,着重于解决当对象组的成员不稳定的情况,而不是多个系列的问题。

与Factory相比,他并不是每一个原型都需要一个构建子类。
缺点:clone本身实现的难度。

模式五:单件(singleton)
这种模式使用无数,就不再多说了。用于保证实例的唯一性。

posted @ 2006-08-24 17:08 flyffa 阅读(904) | 评论 (0)编辑 收藏

2004-10-25

第八章线程和锁
这章描述的是非常底层的jvm运作的时候的概念上的部分模型,与前部分的编译不属于一个层次。
本章提到的变量概念是指类实例,类变量,和类数组。而参数和局部变量并不强行要求到这个变量概念之中。
8。1术语和框架
jvm中有主寄存器,当一个线程被启动时,会创建自己的工作寄存器,当线程运行,会对主寄存器中的数据进行拷贝,存入自己的工作寄存器中。
主寄存器包含每个变量的主拷贝。
主寄存器也包含锁,每个对象有个与其相关的锁。线程可以竞争获得一个锁。
相关术语:
use,assign,load,store,lock,unlock为线程可以执行的动作。
read,write,lock,unlock为主存储器子系统可以执行的动作。
use,assign是线程执行引擎与工作存储之间的紧密耦合交互作用。
lock,unlock是线程执行引擎与主存储之间的紧密耦合交互作用。
主存储与工作存储之间却是松散耦合的。
分别对应的动作就是:load,store和lock,unlock

注意有三个实体在协同工作:主存储器,工作存储器,线程运行引擎

8。2执行顺序和一致性。
1。同线程执行动作排序
2、主存储执行的、对同一变量的动作排序
3、主存储执行的、对同一锁的动作排序
4、不允许一个动作跟在其自身后面。

第4点特殊说明:虽然看起来无关紧要,但必须显式说明,以保证完整性。因为这种危险确实存在。

线程的动作和主存储的动作间的约束:
1、每个lock和unlock动作必须由一些线程和主存储共同执行。
2、每个load动作,唯一的与一个write动作配对,这样write动作跟在store动作后。

8。3有关变量的规则

线程T对变量V的操作约束:
1、T执行过程中只有需要相应使用V时才调用use,或者assign动作。
2、T执行的assign动作必须插入到T对V的load或者store和后继的由T执行的对V的store之间。(保证主存储内值的正确,同时保证assign动作之前工作存储内存在V)
3、在T对V执行use和store之前,必须执行assign或load操作(保证工作存储器中有V,并且已经初始化)
4、在V创建后,每个T在use或store之前,必须执行assign或load操作。

对于第4点:volatile(易变)变量有更严格的约束

由主存储器执行的read和right操作的约束:
1、T对V的load,必须先有对应的read
2、T对V的store,必须后继对应的write
3、动作A,B是T对V的load或store动作,P,Q为对应的read或write,如果A优先于B,则P必须有些于Q
 
8.4Double和long变量的非原子处理
由于32位机器的效率问题,有些jvm实现对double和long这种64位值操作是非原子的,导致一些同步问题(如程序员总是需要显式指定同步于对double和long上的操作),其实这只是一种软件设计对硬件的妥协而已。

8。5有关锁的规则
线程为T,锁为L,T执行的关于L的操作约束如下:
1、T对L的lock操作,必须对于每个不是T的线程S,S对L的unlock操作数等于之前的S对L的lock操作数
2、T对L的unlock操作,必须要求先前T对L的unlock操作数,小于先前T对L的lock操作数(不解不拥有的锁)

8。6有关锁和变量交互作用的规则
线程T,锁L,变量V,T执行的关于L和V的操作约束如下:
1、在t对V的assign和随后的unlock之间,必须插入store操作。对应于该store的write操作必须先于unlock操作(保证L对于V的有效性)。
2、在lock和随后执行的use或store之间,必须插入assign或load操作。

8。7有关volatile(易变)变量的规则
线程T,易变变量V和W
1、load和use操作必须成对出现,紧挨在一起,并且load操作在前
2、assign和store操作必须成对出现,紧挨在一起,并且assign操作在前
3、A、B为T对V的use或assign,F、G为对应的load或store,P、Q为对应的read或write,如果A先于B,则P必须优先于Q。

8。8先见存储操作
如果变量没有声明为violate,则store操作可以提前于assign,store操作将将要赋给V的值替代V实际的值进行store操作,只要满足:
1、如果发生store,必然发生assign。
2、重定位的store和assign间未插入锁定操作。
3、重定位的store和assign间未插入对V的load
4、重定位的store和assign间未插入其他对V的store
5、store操作将assign操作要放到线程T的Z作为存储器中的值传到主存储器。

8。9讨论

8。10可能的交换
一个关于同步和锁的小例子。

8。11范例:无序写
另一个例子

8。12线程
线程由Thread和ThreadGroup类创建并管理。创建Thread对象就创建一个线程,而且是创建线程的唯一方式。当线程被创建时,它还不是活跃的,当其start方法被调用时,开始运行。

8。13锁和同步
每个对象都有与其关联的锁。
当synchronized方法被调用时,它自动执行锁定操作;如果该方法是实例方法,它锁定同实例相关联的锁,如果方法是static的,它锁定同Class对象相关联的锁

8。13等待集和通知
每个对象除了相关的锁外,还有相关的等待集,即一组线程,首次创建的对象,等待集为空。
讲述了wait,notify,notifyall几个方法,wait方法往等待集中增加内容,而notify或notifyall方法从等待集中删除内容。
但不能完全读懂内容,可细研究。

第九章优化
本章描述jvm中Sun版本中实现的优化。
在此优化中,编译的jvm代码在运行期修改,利用运行期获得的信息,做比源指令更少的工作,以获得更好的性能。

9。1通过重写动态链接
对重写的指令,指令的每个实例在其第一次执行时被替换为_quick伪指令,该指令实例随后执行的总是_quick变体。
其余是对_quick伪指令的描述,可用于查阅,因为这是Sun的jdk在运行的时候的真正指令状态。

第十章 操作码的操作码助记符
这章主要是个附录的功能,可供查阅。


×××××××××××××××××××××××××××××××××××
在延期了将近一个月了之后,终于算是看过了一遍这本书,虽然有很多没有看的非常明白的地方,但是比我预期的效果要好了许多了,进一步的细致研究可以安排在后面。

posted @ 2006-08-24 17:07 flyffa 阅读(1307) | 评论 (0)编辑 收藏

2004-10-24

i=0;i=i++为什么等于0这个问题困扰了我好长的一段时间,结果前段时间还试图从虚拟机那个层面进行解释,但无论是线程还是方法调用都不能解释其现象,发现方向性错误,这只是一个语言的特性而已。在java lang spec中提到:
1、java运算符的优先级++符是大于=的。
2、The result of the postfix increment expression is not a variable, but a value.后++符表达式的结果是个值而不是一个变量。

也就是说后++符先将自己的值存储起来,然后对变量进行++;
再进行赋值操作,也就是将先存储起来的值赋给变量i,这样的操作就导致了i值被置为0了


对于C和C++来说不一样,在讲到m=i++操作时,C语言是先将i的值赋给了m,然后将i值++,这样i=i++的结果自然就是1了,c的实现中是不存在那个中间的值的存储的。


由于java和c不同的语言特性,导致了i=i++的不同之处,前面的笔记中已经提到,由于java lang spec中的一些细微规定,导致其运行结果的不同,我们可以用个例子来看i=i++在jvm中实际的运行过程。
源程序test.java:
public class test {
  public test() {
  }
  public static void main(String[] args) {
    int i=0;
    i=i++;
  }

}
我们用javap来看其实际的虚拟机指令集:
C:\JBuilderX\jdk1.4\bin>javap -c  -classpath "d:/" test
Compiled from "test.java"
public class test extends java.lang.Object{
public test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   nop
   5:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0 //常数0入栈
   1:   istore_1 //i赋值,常数值出栈
 //至此完成i=0;
   2:   iload_1  //装载变量i,0入栈
 //第2步是特殊的一步,这步将i值先行保存,以备赋值使用
   3:   iinc    1, 1 //变量值增加,栈内值不变
 //至此完成i++
   6:   istore_1 //i赋值,0出栈。
 //至此完成i=i++
   7:   nop  //donothing
   8:   return

}

对比而言,对于i++而言,i=i++指令多了两步,2和6
其实这两步是赋值符号引起的,有意思的是第二步出现的时机,是在iinc之前,这就是因为java lang spec中规定的。

posted @ 2006-08-24 17:05 flyffa 阅读(948) | 评论 (0)编辑 收藏

2004-10-22

第四章:class文件格式
java.io.DataInput和java.io.Output输入输出的都是以高端字节序输出字节。
与C结构的域不同,class文件中连续的项顺序存储,不进行填充或者对齐。
4.1classFile
一个class文件包含一个单独的classFile结构,包含:
一个四字节的幻数表明class类型。具有值0XCAFEBABE;
access_flag用于表明是类还是接口,是abstract,final还是其他
另外分别有表存储:常量、属性、方法、域。
目前属性只有sourceFile属性。
4。2完全限定类名称的内部形式
出现在classFile文件结构中的类都以完全限定类名称的方式出现,并且不是类似于java.lang.xxx,而是变为java/lang/xxx
4.3描述符
域类型有:根类型,对象类型,数组类型
根类型有:B,C,D,F,I,J,S,Z
对象类型:L
数组类型:[
多维数组double[][][] d的表示方式:[[[D
方法返回描述符
V表示void

object mymethod(int i,double j,Thread t)的方法描述符为:
(IDLjava/lang/Thread;)Ljava/lang/Object;
java的方法不管是static还是实例方法描述符都是如此,区别在于jvm运行时给实例方法隐式的传递当前对象的指针this

4.4常数池
常数池由一组类似于cp_info的结构构成
cp_info{
 u1 tag;
 u1 info[];
}

4.5域
每个域由一个变长的field_info构成
field_info{
 u2 access_flags;
 u2 name_index;
 u2 descriptor_index;
 u2 attribute_count;
 attribute_info attributes[attributes_count];
}

4.6方法
每个方法由变长结构method_info构成
method_info{
 u2 access_flags;
 u2 name_index;
 u2 descriptor_index;
 u2 attribute_count;
 attribute_info attributes[attributes_count];
}

4.7属性
属性用于field_info,method_info,class_file结构之中,结构一般如下:
attribute_info{
 u2 attribute_name_index;
 u4 attribute_length;
 u1 info[attribute_length];
}

某些属性被预定义作为class文件规范的一部分,这些属性是:sourceFile,ConstantValue,code,exception,lineNumberTable和localVariableTable属性。

4。8对java虚拟机代码的约束

4。9class文件的检验

4。10java虚拟机和class文件格式的限制

posted @ 2006-08-24 17:03 flyffa 阅读(1268) | 评论 (0)编辑 收藏

2004-09-30

第六章:jvm虚拟指令集
6.1假定:“必须”的含义
对于jvm指令的一些“必须”的要求,在运行期要求javaclass的结构是满足约束的,对于不满足约束的情况,jvm的行为是未定义的。
6.2保留操作码
在java class文件中使用的指令操作码,有三个操作码是保留的,供java虚拟机内部使用。
254(0xfe)和255(0xff),分别有助记符impdep1和impdep2,目的是在软件或者硬件实现的特定功能提供“后门”或陷阱。
202(0xca),有助记符breakpoint,目的是由调试程序使用来实现断点。

6.3虚拟机错误
当内部错误或者资源限制导致java语言语义不能实现时,jvm抛出一个VirtualMachineError类的子类的实例。jvm规范不规定和语言抛出的时间和地点。

6.4jvm指令集
我不一一例举各种指令的操作码和用法,需要时去查就行了。

第七章 为jvm编译
7.1范例格式
java编译成为class文件之后,以类汇编的jvm操作指令方式存在,jdk自带的javap程序具有这样的功能,将class文件翻译为这样的指令集。
下面是我测试的一个实例:
源文件为test.java
public class test {
    public static String a = "";
    public int i =0;
    public test() {
        int i=0;
        String a = "xyz";
    }

}
编译完成为test.class,用javap生成之后输出结果为:
C:\JBuilderX\jdk1.4\bin>javap -c  -classpath "c:/" test
Compiled from "test.java"
public class test extends java.lang.Object{
public static java.lang.String a;

public int i;

static {};
  Code:
   0:   ldc     #4; //String
   2:   putstatic       #5; //Field a:Ljava/lang/String;
   5:   nop
   6:   return

public test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   aload_0
   5:   iconst_0
   6:   putfield        #2; //Field i:I
   9:   iconst_0
   10:  istore_1
   11:  ldc     #3; //String xyz
   13:  astore_2
   14:  nop
   15:  return

}

7.2常数、局部变量和控制结构的使用
jvm是面向栈的,大多数操作都是从当前框架的操作数栈中取得操作数。

7。3运算
jvm一般在操作数栈上进行运算,即从栈中取操作数,并将结果存入操作数栈中。

7。4访问常数池
byte,char,short,以及小int值都可以通过立即数的方式编译在method的code中,但是int,long,float,double,以及string实例的引用,就需要对常数池进行访问了。使用ldc,ldc_w,ldc2_w指令管理。

7.5更多控制范例
可供查阅

7。6接收参数
如果向java实例方法传递了n个参数,它们被接收,按约定,框架的编号1到n的局部变量为新的方法调用创建,顺序为接收的顺序。
7。7调用方法
对java实例方法的调用,是在对象的运行期类型上调度的。用invokevirtual实现,将其参数取做对常数池表项的索引,给出对象的类类型的完整限定符名称,再调用方法的名称和方法的描述符。

对java静态(类)方法的调用,无需传递类实例,其余和实例方法相似,用invokespecial方法实现。

invokespecail指令用于调用实例初始化方法。超类方法和调用private方法。

7。8处理类实例
构建一个类实例的过程,在建立对象及其实例域之后,用invokespecial调用相应构造函数对应的方法来初始化对象。
对对象域的访问用getfield和putfield指令完成。

7。9数组
主要讲的是数组操作的相关jvm指令,如:newarray,过一遍,可以查阅。

7。10编译开关
对于java的switch指令,jvm有对应的两种指令:tableswitch,lookupswitch.
tableswitch指定取值范围,而lookupswitch并不指定取值范围,两者如何选择完全由效率选择决定。

7。11对操作数栈的操作
jvm有大量的指令补充,将操作数栈的内容作为无类型字或成对的无类型字操纵。便于jvm虚拟机指令的灵活组成。
如dup,dup_x2等,都是对字或者双字进行操作。

7。12抛出或者处理异常
jvm中try...catch块的处理对于jvm指令处理是透明的,辅助控制是由异常表来完成的,由异常表来决定到哪里去调用处理,哪些部分的异常是受控的。

7。13编译finally
try.....finally语句与try-catch相似,只是其辅助控制是由指令jsr(子例程调用)显式的表示,在jsr调用之前,将下一指令的地址压入栈中。而异常表只控制try块的范围。

7。14同步
jvm用monitorenter和monitorexit指令对同步提供显式支持。而java常用sychronized方法。
sychronized“方法”通常不是用monitorenter和monitorexit指令实现的。往往是由“方法调用指令”检查常数池里的ACC_SYCHRONIZED标志
但monitorenter和monitorexit指令是为了支持sychronized“语句”而存在的。
注意这里的方法和语句的区别。

语句实例如下:test.java
public class test {
  public test() {
  }
  public static void main(String[] args) {
    synchronized(new Object()){
        int i = 0;
    }
  }

}
编译完的结果:
C:\JBuilderX\bin>javap -c  -classpath "d:/epm40/classes" test
Compiled from "test.java"
public class test extends java.lang.Object{
public test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   nop
   5:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #2; //class Object
   3:   dup
   4:   invokespecial   #1; //Method java/lang/Object."":()V
   7:   dup
   8:   astore_1
   9:   monitorenter
   10:  iconst_0
   11:  istore_2
   12:  nop
   13:  aload_1
   14:  monitorexit
   15:  goto    23
   18:  astore_3
   19:  aload_1
   20:  monitorexit
   21:  aload_3
   22:  athrow
   23:  nop
   24:  return
  Exception table:
   from   to  target type
    10    15    18   any
    18    21    18   any

}

而synchronized方法编译没有特殊之处,只是在方法名上加了synchronzied字样。

posted @ 2006-08-24 17:02 flyffa 阅读(2792) | 评论 (0)编辑 收藏

2004-09-27

第三章:jvm结构
3.1数据类型
基本类型和引用类型
基本值和引用值

数据不需要做标记或者可被检查以确定类型。也就导致jvm的指令集是针对特定类型的值的。

jvm包含对对象的显式支持(引用类型)

3.2基本类型和值
基本类型--returnAddress类型(jvm指令的操作码的指针,不是java类型)
 --数值类型--整型--byte
   --short
   --int
   --long
    --浮点型--float
     --double
   --char

jvm中没有boolean类型,java中的boolean类型的操作被翻译为int类型进行操作。


问题:在数值类型中提及的NaN值对应于java里的什么情况

3.3引用类型和值
有三种引用类型:类类型,接口类型,数组类型

3.4字
jvm中没有指定数据类型的存储器大小,只是指定了一个“字”的概念,一个字足以持有byte,int,char,short,float,returnAddress,refrence的值,两个字足够大持有double,long的值。

一般来说,一个字的大小是主机平台的一个指针的大小,32位机上,字是32位,64位机上,字是64位的,但这是实现决定的,而不是jvm规范决定的。

3.5运行期数据
pc(程序计数器)寄存器:
每个jvm线程有自己的pc寄存器,在任何点,每个jvm线程执行一个单个方法的 代码,这个方法被称为那个线程的当前方法。如果方法是native,则pc寄存器的值没有定义,如果不是,则pc寄存器中存放当前正在执行的jvm指令的地址。
pc寄存器占一个字宽。

栈:
每个jvm线程都有私有的栈。等价于传统语言的栈,它持有局部变量和部分结果。并参与部分方法的调用和返回。(由于java框架是可以堆分配的,所以java的栈的存储空间可以是不连续的)
java栈可以是固定大小或者是动态的。jvm实现可以向程序员提供对java栈的初始大小以及动态情况下的最大和最小值的控制。

如果固定大小而且线程需要的栈大于栈的大小,则出现stackoverflowError
如果动态大小但存储器没有足够空间,则出现outOfMemoryError

Sun的jdk1.0.2版jvm实现中,java栈是不连续、动态的,不收缩,在线程消亡时被回收。java栈的大小的限制可以在jvm启动时用“-oss”标志设置。

堆:
java有一个所有线程共享的堆。堆是用于分配所有类实例和数组的运行期数据区。
堆在jvm启动时创建,由garbage collector回收。
堆可以是固定的,也可以动态扩展,并且支持自动收缩,存储器无需连续。
jvm实现可以向程序员提供堆初始大小以及动态情况下的最大和最小值的控制。

如果要求的堆比系统能够拥有的堆大,则出现OutOfMemoryError

Sun的jdk1.0.2中,堆是动态的,从不收缩它的堆,它的初始值和最大值在启动时用“-ms”和“-mx”指定。

方法区:
方法区类似于传统语言中编译后代码的存储区,存储每个类结构例如:常数池、域、方法数据。
方法区是虚拟机建立的时候启动的,逻辑上是垃圾回收实现的一部分,但可以不实现。
方法区可以固定大小,可以动态,可以收缩,无需连续存储器。
jvm实现可以向程序员提供方法区初始大小以及动态情况下的最大和最小值的控制。
outofmemory异常

sun的jdk1.0.2中,仿佛去是动态的,不收缩的,不提供程序员对其最大最小值的控制。

常数池:
常数池是每个类或接口的constant_pool的运行期表示。功能类似于传统语言的符号表,但含更宽的数据范围。(详细见第五章)

自身方法栈(估计应该是native method stack)
其管理和普通栈类似,每个线程一个,线程创建时创建,只是使用非java的native语言(如C)写成,以支持native方法。

Sun的jdk1.0.2版jvm实现中,java栈是固定大小。java栈的大小的限制可以在jvm启动时用“-oss”标志设置。

3.6框架
jvm frame用于存储数据和部分结果,以及动态链接,返回方法的值,和调度异常。

每次java方法调用时创建一个新的框架,当方法结束的时候,框架撤销。框架从创建它的线程的栈里分配,每个框架有自己的局部变量集和操作数栈(这些可以一次分配,因为都是编译期可知的)。
对于一个给定线程的任何点,只有一个活跃框架,称为当前框架,局部变量和操作数栈上的操作总是引用当前框架。

局部变量:
每个jvm frame包含一组局部变量,局部变量总是一个字宽,long型,double型存为两个局部变量。

操作数栈:
每个jvm frame包含一个操作数栈,绝大多数java操作从当前操作数栈取值。

动态连接:
jvm frame通过包含一个对当前类的常数池的引用来达到动态链接的目的,java 的class文件仍然可以使用符号引用访问变量或方法。


java中的i=i++从这一章来理解应该是和框架(jvm frame)这个概念有关,也就是++操作符被实现成为了一个方法,而不是一个虚拟机指令,这样就可以解释了,但是目前还没有看到有++操作符应该被实现为一个方法的说明,另外java的方法调用是值传参的,这种情况应该也不会出现值回写的情况.
看至3。6结束。

3.7对象的表示
jvm不对对象表示要求任何特殊的内部结构。
在sun公司的jdk实现中,对象实例就是指向一个句柄的指针,而这个句柄本身又包括两个指针:1、一个指向包含该对象方法而代表该对象类型的class对象的指针,2、另一个指向在java堆中为该对象实例分配的存储区域。
别的jvm实现可以采用诸如:直接插入高速缓存技术,等。

3.8特殊的初始化方法
实例初始化:构造函数作为具有特殊名字《init》的实例初始化方法出现,方法的名字由编译器提供,这个方法由jvm在内部由invokespecial指令调用,只能使用于未初始化的实例上,不能被java程序员使用。
类和接口的初始化:类和接口的初始化具有特殊的名字《cinit》,方法的名称由编译器提供,该方法由jvm显示调用,从不直接被java代码和jvm指令调用,只是作为类初始化进程的一部分被间接的调用。

3.9异常
异常一层层向上抛,丢弃当前层的操作数栈和局部变量,知道遇到catch为止,如果到顶层还没有catch,当前线程将被结束。

3.10class文件格式
class文件是平台无关格式的二进制文件,class文件格式精确定义了文件的内容。

3.11指令集概述
一个jvm指令由一个字节的操作码后跟0个或多个操作数构成。操作数的数目由操作码决定。
当操作数多于一个字节的时候,以高位字节在前的方式存储。
字节码指令流只是单字节对齐的(除了tableswitch,和lookupswitch的特殊指令对操作数的特殊要求),放弃了数据对齐,反映了对数据紧凑性的偏好,而排除了可能提高jvm仿真器性能的某些实现技术。
jvm指令中绝大多数是类型相关的,也就是作用于特定类型的操作数的。并在该指令的助记符中显示的标识出来。
具体的指令集后面细说。

3.12公共设计、私有实现
公共概念:class文件格式和jvm指令集

posted @ 2006-08-24 17:01 flyffa 阅读(1216) | 评论 (0)编辑 收藏

2004-09-27

学习java虚拟机规范之前,已经有了心理的准备,像我这种从应用中开始真正了解计算机的人,可能会遇到许许多多的问题,很多关于底层的,硬件级别的概念可能会无法理解,但是只要能开始,就是进步,不是吗。

第一章:前言

************************************************************************************************

java虚拟机假定任何实现技术或主机平台,它并不非得是解释型的,它也可以像传统的程序设计语言一样,通过把它的指令集编译成一个实际的CPU指令集来实现。它也可以直接用微代码或者直接用芯片来实现。

 

第二章:Java概念
(这个部分是对Java语言的介绍,这里罗列出一些比较细节的概念)
1、Java语言使用Unicode1.1.5版编写。
2、除了注释、字符、字符串、标识符之外,Java程序的所有输入元素都是ascii码形式。
   测试证明变量定义可以使用中文。
3、字符串文字以及更一般的常数表达式的值的字符串,被用方法String.inter内部化以共享惟一的实例。
4、整型和引用类型可以转化为boolean型,非零和非null都为true。(以标准C++的方式x!=0和obj!=null)
5、三种引用类型:类引用,接口引用,数组引用,所有对象包括数组都支持Object类的方法。
6、执行顺序:装载类---链接一个类型或者类(检验,准备和可选的解析)---初始化(这个初始化可能引起相关父类的初始化动作)
7、类实例退出的时候自动的调用finalize()方法
8、类卸载的时候自动调用classFinalize()方法。
9、虚拟机退出的条件:1、所有非守护线程中止。2、显式调用了Runtime或者System的exit方法

************************************************************************************************
6、变量是一种存储位置,有相应的类型,称为编译期类型,可以是引用类型,也可以是基本类型。
   问题:变量本身是怎样的一个存在,它的结构如何。
7、java语言中多次提到基本数值类型的特殊值NaN,但是不知道其表现和使用。


2004-6-3凌晨1点,读至2、16 执行,31页。
2004-6-3下午五点:读至34页
2004-6-6晚上8:30-10:30,读至第三章:java虚拟机结构:41页

为了了解i=i++的奥秘,我提前阅读了第八章:线程和锁,好像能够理解,结果又被全盘推翻了。(通查网上的说法:i=i++的特殊之处在于i++,或者说是++操作符的实现,i++操作的方式是1:将i值拷贝一份到另一内存区域,2、对原i值执行++操作,3、将i++的结果放入新分配的区域内,而对于i=i++,就多了第四步:4、将i++值回写到i的存储区域中),但java为什么这么实现不是很清楚

posted @ 2006-08-24 16:59 flyffa 阅读(945) | 评论 (0)编辑 收藏

20060607
1.2版本,下载地址
http://www.blogjava.net/Files/flyffa/javaSms1.2.rar
1、对联通手机号码进行支持,主要的处理就是在短信中心的控制上
2、对容错能力进行提高,对于发送失败的信息,加以提示.
3、解决以手机做为gsm模块时不能正常接收短信的问题,从而达到聊天的效果。

如果需要了解其他更多,访问:
http://www.blogjava.net/flyffa/archive/2006/05/15/46177.html
posted @ 2006-06-07 13:11 flyffa 阅读(544) | 评论 (1)编辑 收藏

仅列出标题
共3页: 上一页 1 2 3 下一页