2007年10月10日星期三

《.NET设计规范》——学习笔记(3) 命名规范

一组一致的的命名约定对框架的可用性及其重要。 名字要易于理解,同时必须传达每个元素的功能。

大小写约定

标识符的大小写规则

PascalCasing:如HtmlTag  IOStream

camelCasing:如htmlTag  ioStream

  • 要把PascalCasing用于由多个单词构成的名字空间、类型、以及成员的名字;
  • 要把camelCasing用于参数的名字。

通用命名约定

单词的选择

对框架中标识符的名字来说,很重要的一点是一目了然。

名字的意思清楚比长度短更重要。名字应该与场景、系统的逻辑组成或物理组成以及为人熟知的概念相对应,而不应该与技术或框架相对应。

  • 要为标识符选择易于阅读的名字;
  • 要更看重可读性,而不是更看重简短性;
  • 不要使用下划线、连字符以及其他任何既非字母也非数字的字符;
  • 不要使用匈牙利命名法;
  • 避免使用与广泛使用的编程语言的关键字有冲突的标识符。

使用单词缩写和首字母缩写词

一般来说,不要在标识符中使用单词缩写或首字母缩写:宁可名字长一点,也不要别人看不懂。  尤其不要使用未被广泛接受的单词缩写和首字母缩写词。

  • 不要使用缩写词或缩略词作为标识符名字的一部分
    用GetWindow  不用GetWin
  • 不要使用未被广泛接受的首字母缩写词
    多谓广泛接受:用搜索引擎在网上搜索该首字母缩写词,如果返回的前几个结果与期望相符,那么该首字母缩写词才有资格被称为众所周知。

避免使用语言特有的名字

  • 要给类型名使用语义上有意义的名字,而不要使用特有的关键字
    GetLength比GetInt要好
  • 要使用CLR的通用类型名,而不要使用语言特有的别名
  • 要使用常见的名字,比如value或item,而不要重复类型的名字

为已有API的新版本命名

当用新类型和新成员接替或取代已有的类型或成员时,如何选择名字:

  • 使用与旧API相似的名字
  • 要优先使用后缀而不是前缀来表示已有API的新版本
    这样有助于在浏览文档或使用Intellisense时发现新版本:按字母排序
  • 可以考虑使用全新但有意义的标识符
  • 要使用数字后缀来表示已有API的新版本
    有些名字(或工业标准)不宜添加后缀或改名
  • 不要在标识符中使用“Ex”“New”等类似的后缀来区分相同API的不同版本
  • 要在引入64位整数(long)而非32位整数进行操作的新版本API时使用“64”后缀,反之亦然。

程序集和DLL的命名

程序集是一个部署单元,同时还代表托管代码程序的身份。虽然程序集可以分布一个或多个文件中,但一般来说一个程序集仅与一个DLL相对应。

名字空间与DLL程序集的区别:

名字空间:一组逻辑实体

DLL和程序集:打包和部署时的一个单

  • 要为程序集和DLL选择提示性的名字,比如System.Data,这样很容易就知道它的大致功能。
  • DLL命名:<Company>.<Component>.dll

名字空间的命名

<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

  • 要用公司名称作为名字空间的前缀,不要用缩写
  • 要用稳定的与版本无关的产品名称作为名字空间的第二层
  • 不要根据公司的组织架构来决定名字空间的层次结构,因为公司内部组织的名称一般来说不会持续太长的时间
  • 要使用PascalCasing大小写风格,并用点号来分隔名字空间的各部分。
    如Microsoft.Office.PowerPoint
  • 考虑适当的时候在名字空间中使用复数形式。  首字母缩写词例外
    System.Collections
    System.IO
  • 不要用相同的名字来命名名字空间与位于该名字空间中的类型
    如:不要将名字空间命名为Debug,然后又在该名字空间中提供一个名为Debug的类。

名字空间和类型冲突

  • 不要引入太一般化的类型名,比如Element、Node、Log以及Message。

不同类型的名字空间,有不同的规范来避免类型名的冲突:

  • 应用程序模型名字空间(application model namespace)
    属于单个应用程序模型的名字空间经常一起使用,但是它们几乎不合属于其他应用程序模型的名字空间一起使用
    System.Windows*
    System.UI*
  • 基础设施名字空间(infrastructure namespace)
    此类别包含一些在开发常用应用程序时很少会导入的名字空间
  • 核心名字空间(core namespace)
    包含了所有的System名字空间,但应用程序模块名字空间和基础设施名字空间除外。  包括System、System.IO、System.Xml以及System.Net等等
  • 技术名字空间组(technology namespace group)
    此类别包含所有那些以相同的两个前缀(<Company>.<Technology>*)开始的名字空间。

类、结构和接口的命名

一般来说类型名应该是名词词组。如果无法为类型找到一个名词词组,那么应该重新考虑类型的总体设计。

另一个中重要的考虑因素:最易于识别的名字应该用于最常用的类型。

最常用的类型名应该反映出使用场景,而不是继承层次。

  • 要用名词词组来给类型命名。使用PascalCasing风格
  • 不要给类名加前缀
    只有接口才能(可以)被加前缀“I”,那是因为.NET框架受到COM及Java的影响
  • 考虑让派生类的名字以其基类结尾
    public class FileStream : Stream {...}
  • 要确保一对类/接口的名字只差一个“I”前缀,如果该类是该接口的标准实现。
    public interface IComponent {...}
    public class Component : IComponent {...}

泛型类型参数的命名

  • 要用描述性的名字来命名
  • 考虑用T来命名参数类型
  • 要给描述性的类型参数名加上T前缀
  • 考虑在类型参数中显示出施加于该类型参数上的限制

枚举类型的命名

  • 要用单数名词来命名枚举类型,除非它表示的是位域(bit field)
  • 不要给枚举类型的名字添加“Enum”后缀,也不要添加“Flag”、“Flags”等后缀
  • 不要给枚举类型值的名字添加前缀
    此规范与C++中通常所使用的恰好相反。在C++中给枚举的每个成员加上完成的限定符是很重要的,因为它们可能在枚举名的作用域之外被访问。但是在托管代码中,枚举成员只能通过枚举名的作用域来访问。

类型成员的命名

类型:方法、属性、事件、构造函数、字段

方法的命名

要尽量根据方法所对应的任务来给它们命名,而不要根据一些实现细节。

  • 要用动词或动词词组来命名方法

属性的命名

  • 要用名词、名词词组或形容词来命名属性
  • 不要让属性名看起来与“Get”方法的名字相似
  • 要用肯定性的短语(CanSeek而不是CantSeek)来命名布尔属性
  • 考虑用属性的类型名来命名属性
    public enum Color {...}
    public class Control
    {
        public Color Color
        {
            get {...}
            set {...}
        }
    }

事件的命名

  • 要用动词或动词短语来命名事件
    事件总是表示一些动作,要么正在发生,要么已经发生
  • 要用现在时和过去时来赋予事件名之前和之后的概念
    窗口关闭之前发生的close事件:Closing
    窗口关闭之后发生的close时间:Closed
  • 不要用Before和After前缀或后缀来区分前置和后置事件
  • 要在命名事件处理函数(用作事件类型的委托)时加上“EventHandler”后缀
  • 要在事件处理函数中用sender和e作为两个参数的名字
    sender:触发事件的对象,在整个.NET框架中,sender为object类型
  • 要在命名事件的参数类型时加上“EventArgs”后缀

字段的命名

  • PascalCasing风格
  • 名词或名词短语
  • 不要给字段名添加前缀

参数的命名

  • camelCasing风格
  • 要使用描述性的参数名
    参数名要具备足够的描述性,使得在大多数情况下,用户根据参数的名字和类型就能够确定它的意思
  • 考虑根据参数的意思而不是参数的类型来命名参数

资源的命名

本地化的资源就好比是属性,可以通过特定的对象来引用。因此,它的命名规范与属性的相似。

  • 要在命名属性关键字(resource key)时使用PascalCasing大小写风格
  • 要使标识符的名字具有描述性而不是使名字变短
  • 不要使用各主要CLR编程语言特有的关键字
  • 要在命名资源时使用字母、数字和下划线
  • 要用点号来给标识符清楚地划分层次
  • 要在为异常消息资源命名是遵循下面的命名约定:
    资源标识符应该是异常的类型名加一个简短的异常标识符,之间以点号分隔

2007年9月20日星期四

Adobe Flex 3最有趣的特征之一 :Web设计者和开发者的相遇

 

著:Yakov Fain 译:叶进

Adobe将于2008年二月左右发布Flex 3.它有增加了一些新的改进和特色,尤其是它将使Flash设计者和Flex开发者结合在一起。CS3可以很容易的使Flex融入到Flash IDE的发布时间线上。Flash中的容器创建在Flex中将会得到足够多的发展。为了开发Flash CS3,你必须要有个一个Flex组件工具包。这就使得Flash艺术拥护者必须去学习至少一点点Flex的基础知识,那会使他们获得在接下来的几年中获得更多的市场。

在今年的早些时候,我看到MS发布了Sliver流。我对这种可以被一个对于Web设计一窍不通的人开发的绚丽的GUI应用印象非常深刻。他(用时间线)创建了一个绚丽的画面,并且为Sillverlight开发者增加了添加他们自己写代码的地方。现在,Flash设计者也可以非常简单地就整合Flex为他们的作品添加代码了。

只要看看这段5分钟的youtube视频,你就知道我在说什么了。新一代的用户体验专家将获得更好的工具和动机去获得、学习新的技能。但是,针对艺术家的编程介绍应该更优雅一点——他们是不一样的人群,同时如果你用编程术语的话,可能会把他们吓跑。我对于这个有切身的体会,因为我的儿子,一个接受过专业培训的漫画家和漫画创作者对Flash IDE非常精通,却拒绝对于编程做任何事情。在很多场合,我都试图使他确信通过学习一些编程知识赚取更多的钱是一件好事。但是,到现在位置,我还没有成功,当然我会继续劝说他的,特别是当Flex 3发布的时候。那些认识到Web设计和编程开发是未来的人将会有一个无边的发展(People who realize that knowing of Web design and programming is the future will have an edge.)!

漫谈C#之关键字(1)

    每一种语言都有非常多的关键字,而且这些关键字也都大同小异,不过毕竟还是有些许的不一样。有些关键字大家碰到的多了,自然就熟悉了,但是有些关键字用得不大多,或者是新引入的,所以就不大熟悉了。我平常在用的时候,就是会碰到一些关键字,感觉有点生疏,平常也会把这些我不懂的关键字的用法了解一下并记录下来。想到应该也有很多跟我同样的人,所以就把我的记录跟大家分享一下。请各位tx多多指正!
访问关键字
    base:用于派生类中访问基类的成员

  • 调用基类上已被其他方法重写的方法

    1 public override void GetInfo()
    2 {
    3 base.GetInfo(); // 调用基类上的GetInfo方法
    4 }

  • 指定创建派生类实例时应用的基类构造函数

    1 public MyDerived() : base() // 调用基类的构造函数
    2 {}

    从静态方法中使用base关键字是错误的。
转换关键字
explicit:用于声明用户定义的显式类型转换运算符

1 class MyType
2 {
3 public static explicit operator MyType(int i)
4     {
5 // 从int转换到MyType类型的代码
6     }
7 }

显式转换运算符必须通过类型转换调用

1 int i;
2 MyType x = (MyType)i; // int到MyType类型的转换需要进行类型转换

    如果转换操作可能导致异常或信息丢失,则应用explicit关键字标记它。
    implicit:用于声明用户定义的隐式转换运算符 

1 class MyType
2 {
3 public static implicit operator int(MyType m)
4     {
5 // 从MyType转换到int类型的代码
6     }
7 }

1 MyType x;
2 int i = x;  // 隐式地调用MyType的MyType到int类型的转换运算符

隐式转换可以通过消除不必要的类型转换来提高源代码的可读性。
    一般情况下,调用某一个隐式转换时,应当绝不会引发异常,并且不会造成信息丢失。否则,应将其标记为explicit。
方法参数关键字
    如果声明方法的参数时没有指明ref或out,该参数将具有与该方法相关的值。这个值在方法中能被更改,但是当程序返回到调用过程时,这种改动不会被保留。

    params:用于指定在参数数目可变时带有参数的方法参数
    在方法声明中的params关键字之后不允许引入任何其他参数,但在其前面可以有其他参数。而且在方法声明中只允许使用一个params关键字。

 1 public static void UseParams(params int[] list)
 2 {
 3 for(int i = 0; i < list.Length; i++)
 4     {
 5         Console.WriteLine(list[i]);
 6     }
 7 }
 8
 9 public static void Main()
10 {
11     UseParams(1,2,3);
12 int[] myArray = new int[3] { 10,11,12 };
13     UseParams(myArray);
14 }

    ref、out  使方法可以引用传递到该方法的那一个变量,当程序转至调用方法时,在方法中对参数所做的任何改动都将传给该变量。
    ref参数的值将被传递到ref参数,故必须首先初始化;而out参数不然,它的值不会被传递到该out参数,故不必首先初始化,但它必须在方法返回以前为out参数赋值。
    属性不是变量,不能作为ref/out参数。

2007年9月5日星期三

《.NET设计规范》——学习笔记(2.5)框架设计基础

这篇文章在一定程度上是对前面几篇文章的一个总结。

一个成功的通用框架必须是为广大具有不同的需求、技能和背景的开发人员而设计的。框架设计师面临的最大挑战是为这些多样化的用户群提供即简单又功能强大的框架。

  • 要设计即功能强大又易于使用的框架。
    80/20原则。 要把精力集中在框架中使用最为频繁的部分(20%)
  • 要明确地为具有不同编程风格、需求、技能以及使用不同编程语言的开发人员设计框架。
  • 要了解哪些使用多语言框架的广大开发人员。
    我们往往会只为自己设计API,而没有清楚地考虑用户的真正需求。

渐进框架

针对不同的使用场景,为不同的开发团体提供不同的产品,这种多框架的方法在某种程度上说是成功的,比如MS有Visual Basic程序库,有Win32程序库,也有MFC和ATL,但它也存在严重的缺点:多框架使得使用某个框架的开发人员难以将他们的知识转移到下一个技能等级或使用场景(这通常需要另一个框架)。

  • .NET框架所做的是把VB、MFC、ATL、ASP等这些模型统一起来。无论开发人员使用何种编程语言或者选择何种编程模型,可供使用的API始终都是一致的。
    一个更好的方法是提供渐进框架(Progresive framework)。  从无到有,慢慢积累知识,并应用到以后更高级的使用场景中去。
  • .NET框架就是一个渐进框架。
    渐进框架的目标是覆盖广大的开发人员,但并不是所有可能的开发人员。
    这也应了没有十全十美这句话:不可能满足每一个开发人员的需求。

框架设计的基本原则:

对用户而言,真正的开发效率来自能够轻易地创造非凡的产品,而并非来自能够轻易地创造垃圾。

  1. 场景驱动设计原则
  2. 低门栏原则
  3. 自说明对象原则
  4. 分层架构原则

《.NET设计规范》——学习笔记(2.4)分层架构原则

分层设计使得在单个框架中同时提供强大的功能和易用性成为可能。

  • 考虑对框架进行分层,使高层API能提供最佳的开发效率,低层API能提供最强大的功能和最丰富的表现力。
    通俗地讲,象我这样的菜鸟只能用高层API,太低层都不懂,而牛人们都是想用也更愿意用低层API的强大功能的(个人意见)。ps:这边的高层跟低层不是指高深的意思。而是从易用性方面考虑的!
  • 避免把低层API和高层API混在同一名字空间中,如果低层API非常复杂的话(即包含了许多类型)。
  • 要确保单个特性域中不同的层能很好的集成在一起。

2007年9月4日星期二

《.NET设计规范》——学习笔记(2.3)自说明对象原则

在简单的使用场景中,一定要让框架无需文档就能使用。

  • 要确保API是直观的,无需查阅参考文档就能用于基本场景
    你总不希望写个“Hello World”都去查阅API文档吧。
  • 要为所有的API提供优秀的文档。
    一方面,并非所有的API都能自说明。不同的人会认为不同的API是自说明的;
    另一方面,有些人想在开始使用API之前完全理解它们。

设计自说明API时最重要的一些考虑因素:

  1. 命名
    要在规范检查中重视标识符名称的选择;
    不要担心标识符的名字太冗长;
           一眼就能看出相应的方法是做什么的,类型和参数是表示什么意思。
           而且类型的名字如果足够好,那么用到这些类型的代码会更易于立即和维护。
    要在设计过程的初期就让用户教育专家参与;
    考虑把最好的名字留给最常用的类型。
  2. 异常
    要通过异常消息来清楚地告诉开发人员对框架的误用。
  3. 强类型
    很明显,调用Customer.Name要比调用Customer.Properties["Name"]容易。
    要尽可能提供强类型API。
           如果必须使用属性包,那么应该为包中最常用的属性提供相应的强类型属性。
  4. 一致性
    要确保与.NET框架以及客户可能会使用的其他框架保持一致。
  5. 限制抽象的数量
    标准面向对象设计方法会产生大量抽象。其目的是使代码易于维护。然而如果框架中存在太多的抽象,则会要求用户对框架的架构有深入的了解,不易于用户使用。
    避免在主要场景的API中使用太多的抽象。

2007年9月2日星期日

做礼拜

又是一周过去了,星期一又来了!早上,在公司里偷个空,写点东东:

昨天是星期日:基督徒做礼拜的日子!

自从呆子去年去了英国,也有一年多没见到她了。昨天我们越好见面的,还有冲哥和晓丹!昨天也是她要做礼拜的日子:她在英国受洗了!我以前也从来没有见识过基督教做礼拜的场景,所以跟呆子说,也想看看。我到富阳的时候他们的仪式已经开始了,我到的时候,上面是长老在做报告类似的,因为讲得是富阳话,我不大听得懂;不过这个很快也就结束了(大概是在我来之前已经讲了很久了:-P)。然后,他们就开始唱圣歌,唱了很多,我个人感觉那个旋律还不错,也很容易上口,也跟着唱了几首。圣歌中都是些赞颂耶稣,赞颂主的,也有一些是说希望主不要摒弃他们的,还有就是希望主收留他们。呆子,一直都在非常虔诚的唱,而且每次低头祷告的时候都非常虔诚(我作为外人,自然体会不到其中的滋味)。唱完圣歌,过后是讲了一段圣经上的内容,我们四个人,除了呆子,其他的都没有认真的在听,所以也不知道具体地讲了些什么。不过我听到一个故事印象非常深刻:有一对夫妻,妻子是基督徒。故事就讲到那个丈夫,如果打他妻子,她绝对不会还手,而且还是笑脸相迎! 这个也许就是常说的那个故事:如果有人打你左脸,你要把右脸让他打!这个礼拜好像还有圣餐,不过呆子说我们没有受洗过,不能接受的!

后来,也跟呆子聊了很多。也大致了解了基督教的一些东西。她也说她自己变了很多,在英国最重要的事就是找到了自己的主。在我看来她在英国最重要的的确是找到了自己的主,找到了自己一生的寄托。

我从来不会觉得因为有宗教信仰,呆子就是另类了。在我看来呆子还是呆子,唯独变的只是她找到了一个可以倾诉的对象!

《.NET设计规范》——学习笔记(2.2)低门栏原则

框架必须以易于使用的方式来为普通用户提供一个低门栏。

每个人在第一次接触一个新框架时,都希望其是简单而功能强大的。如果他一开始就感觉其很复杂,则会望而却步。

  • 要确保每个特性域的名字空间只包含哪些用于最常见场景的类型。应该把用于更高级的场景的类型放在子名字空间中。
    例如:System.Net命名空间提供了有关网络的主要API,而更高级的socket API则位于System.Net.Socket子命名空间。
  • 要为构造函数和方法提供简单的重载函数。参数少,且都是基本类型。
  • 不要在为主要的使用场景而设计类型中包含用于高级场景(这里我个人感觉是不常用的意思)的方法。
    通过从框架中减少(或至少不增加)特性,能够使开发人员更有效率。因为需要处理的概念减少了。CLR把多重继承排除在外就是这个原因。
  • 不要要求用户在最基本的场景中显示地实例化一个以上的类型。
  • 不要要求用户在为基本使用场景编写代码之前就进行大量的初始化。
    如果一些初始化是必需的,那么当用户未执行初始化而引起异常时,应该在异常消息中清楚的告诉用户需要做些什么。
  • 要尽可能地(用便利的重载函数)为所有的属性和参数提供合适的默认值。
    当然不要盲目采用,如果默认值会引起错误,自然不可取。
  • 要通过异常来传达API的误用。
    显示错误的原因、如何修正等信息。

2007年8月29日星期三

《.NET设计规范》——学习笔记(2)框架设计的基本原则

由于我怕文章的篇幅过长会使人看了头痛,所以,我打算分几篇文章把《.NET设计规范》第二章的学习笔记写出来,这样大家看着不至于太累!大概是接下去总共五篇文章是说“框架设计基础”的......

 

用户而言,真正的开发效率来自能够轻易地创造出非凡的产品,而并非来自能够轻易地创造垃圾。

场景驱动设计的原则

框架通常包含非常大的一组API。但在开发过程中,真正用到的只是其中较小的一个子集,只会涉及一小部分常用场景。

在设计框架时,使用场景来驱动。从用户的角度,先自己编写一些对主要场景来说必不可少的代码,然后再设计对象模型(object model)来支持这些样例代码。

于功能性规范之前,先撰写一份场景驱动的API规范,应该列出一个给定的技术领域中最常用的5—10个使用场景,并列出实现这些场景的样例代码,至少用两种语言编写。

  • 要确保对任何包含公用API的特性设计来说,其核心部分都是API设计规范。
  • 要为每个主要的特性域(feature area)定义一些最常用的场景。
  • 要确保使用场景与适当的抽象层次相对应。场景应该大致与最终用户的用例相对应。
  • 先为主要的使用场景编写样例代码,然后再定义对象模型来支持这些样例代码。
  • 要用至少两种不同的编程语言来为主要场景编写样例代码。
    最好能保证所选编程语言的语法和风格差异很大。
  • 不要在设计框架的公用API时完全依赖于标准的设计方法。
    标准的设计方法(包括面向对象的方法)是为了使设计的具体实现容易维护,而不是为了使得到的API易于使用。
    以场景驱动设计为主,辅以原型制作、可用性研究以及一定数量的迭代,这种方法要比标准的设计方法好得多。
    可用性研究是为了确定开发人员真正的需求。这跟需求获取一样,设计师此时化身为一名需求分析师,而开发人员则变成了客户。需求分析师不能想当然的认为客户的真正需求是什么,一定要通过跟客户交流才行,站在客户的角度考虑问题。跟需求获取类似,可用性研究宜早不宜迟。
  • 要安排可用性研究来测试用于主要场景的API。
    如果开发人员在为主要场景编写代码时,遇到较大问题,则说明API需要重新设计。在原有API的基础上修改,开销反而大,而且是很大。

2007年8月27日星期一

《.NET设计规范》——学习笔记(1)

第一章 概述

如果框架的设计者能够站在使用者背后解释应该如何使用框架,那么就不需要同规范了。

 

精心设计的框架所具备的品质

  1. 简单性
    在设计框架时,宁可不要某个特性比较复杂的设计,也不能破坏整体的简单性。
  2. 设计代价高
    框架设计需要耗费大量的资源。框架设计应该是开发过程中明确而独立的一部分。
  3. 精心设计的框架充满利弊权衡
    没有十全十美的东西。
  4. 应该借鉴过去
    站在前人的肩膀上。
  5. 要考虑未来的发展
    考虑将来发展框架是一把“双刃剑”。一方面,它以“万一”的名义增加复杂性;另一方面,它可以避免让设计随着时间的流逝而贬值,或避免产生无法向后兼容的设计。
  6. 应具有良好的集成性
  7. 一致性
    一致性是精心设计的框架的关键特征,它是影响开发效率的最重要因素之一。一致的框架可以是开发人员从框架中已了解的部分推知不了解的部分。一致性同时还可以帮助开发人员很快地认识到,设计的哪些部分是某个特定区域所独有的,需要特别加以注意,而哪些部分仅仅是常用的既有设计模式和惯用法。

java垃圾收集算法(转)

很长的一篇文章,不过看看应该有所助益!转自:java垃圾收集算法

1.垃圾收集算法的核心思想
Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。
垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。
2.触发主GC(Garbage Collector)的条件
JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:
①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。
②Java 堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。
由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。
3.减少GC开销的措施
根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:
(1)不要显式调用System.gc()
此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。
(2)尽量减少临时对象的使用
临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。
(3)对象不用时最好显式置为Null
一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。
(4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer)
由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5= Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。
(5)能用基本类型如Int,Long,就不用Integer,Long对象
基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。
(6)尽量少用静态对象变量
静态变量属于全局变量,不会被GC回收,它们会一直占用内存。
(7)分散对象创建或删除的时间
集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主 GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。
4.gc与finalize方法
⑴gc方法请求垃圾回收
使用System.gc ()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。需要注意的是,调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。
⑵finalize方法透视垃圾收集器的运行
在JVM垃圾收集器收集一个对象之前 ,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止化该对象释放资源,这个方法就是finalize()。它的原型为:
protected void finalize() throws Throwable
在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。
因此,当对象即将被销毁时,有时需要做一些善后工作。可以把这些操作写在finalize()方法里。
protected void finalize()
{
// finalization code here
}
⑶代码示例
class Garbage
{
int index;
static int count;
Garbage()
{
count++;
System.out.println("object "+count+" construct");
setID(count);
}
void setID(int id)
{
index=id;
}
protected void finalize() //重写finalize方法
{
System.out.println("object "+index+" is reclaimed");
}
public static void main(String[] args)
{
new Garbage();
new Garbage();
new Garbage();
new Garbage();
System.gc(); //请求运行垃圾收集器
}
}
5.Java 内存泄漏
由于采用了垃圾回收机制,任何不可达对象(对象不再被引用)都可以由垃圾收集线程回收。因此通常说的Java 内存泄漏其实是指无意识的、非故意的对象引用,或者无意识的对象保持。无意识的对象引用是指代码的开发人员本来已经对对象使用完毕,却因为编码的错误而意外地保存了对该对象的引用(这个引用的存在并不是编码人员的主观意愿),从而使得该对象一直无法被垃圾回收器回收掉,这种本来以为可以释放掉的却最终未能被释放的空间可以认为是被“泄漏了”。
考虑下面的程序,在ObjStack类中,使用push和pop方法来管理堆栈中的对象。两个方法中的索引(index)用于指示堆栈中下一个可用位置。push方法存储对新对象的引用并增加索引值,而pop方法减小索引值并返回堆栈最上面的元素。在main方法中,创建了容量为64的栈,并64次调用push方法向它添加对象,此时index的值为64,随后又32次调用pop方法,则 index的值变为32,出栈意味着在堆栈中的空间应该被收集。但事实上,pop方法只是减小了索引值,堆栈仍然保持着对那些对象的引用。故32个无用对象不会被GC回收,造成了内存渗漏。
public class ObjStack {
private Object[] stack;
private int index;
ObjStack(int indexcount) {
stack = new Object[indexcount];
index = 0;
}
public void push(Object obj) {
stack[index] = obj;
index++;
}
public Object pop() {
index--;
return stack[index];
}
}
public class Pushpop {
public static void main(String[] args) {
int i = 0;
Object tempobj;
ObjStack stack1 = new ObjStack(64);//new一个ObjStack对象,并调用有参构造函数。分配stack Obj数组的空间大小为64,可以存64个对象,从0开始存储。
while (i < 64)
{
tempobj = new Object();//循环new Obj对象,把每次循环的对象一一存放在stack Obj数组中。
stack1.push(tempobj);
i++;
System.out.println("第" + i + "次进栈" + "\t");
}
while (i > 32)
{
tempobj = stack1.pop();//这里造成了空间的浪费。
//正确的pop方法可改成如下所指示,当引用被返回后,堆栈删除对他们的引用,因此垃圾收集器在以后可以回收他们。
/*
* public Object pop() {index - -;Object temp = stack [index];stack [index]=null;return temp;}
*/
i--;
System.out.println("第" + (64 - i) + "次出栈" + "\t");
}
}
}
如何消除内存泄漏
  虽然Java虚拟机(JVM)及其垃圾收集器(garbage collector,GC)负责管理大多数的内存任务,Java软件程序中还是有可能出现内存泄漏。实际上,这在大型项目中是一个常见的问题。避免内存泄漏的第一步是要弄清楚它是如何发生的。本文介绍了编写Java代码的一些常见的内存泄漏陷阱,以及编写不泄漏代码的一些最佳实践。一旦发生了内存泄漏,要指出造成泄漏的代码是非常困难的。因此本文还介绍了一种新工具,用来诊断泄漏并指出根本原因。该工具的开销非常小,因此可以使用它来寻找处于生产中的系统的内存泄漏。
垃圾收集器的作用
  虽然垃圾收集器处理了大多数内存管理问题,从而使编程人员的生活变得更轻松了,但是编程人员还是可能犯错而导致出现内存问题。简单地说,GC循环地跟踪所有来自“根”对象(堆栈对象、静态对象、JNI句柄指向的对象,诸如此类)的引用,并将所有它所能到达的对象标记为活动的。程序只可以操纵这些对象;其他的对象都被删除了。因为GC使程序不可能到达已被删除的对象,这么做就是安全的。
  虽然内存管理可以说是自动化的,但是这并不能使编程人员免受思考内存管理问题之苦。例如,分配(以及释放)内存总会有开销,虽然这种开销对编程人员来说是不可见的。创建了太多对象的程序将会比完成同样的功能而创建的对象却比较少的程序更慢一些(在其他条件相同的情况下)。
  而且,与本文更为密切相关的是,如果忘记“释放”先前分配的内存,就可能造成内存泄漏。如果程序保留对永远不再使用的对象的引用,这些对象将会占用并耗尽内存,这是因为自动化的垃圾收集器无法证明这些对象将不再使用。正如我们先前所说的,如果存在一个对对象的引用,对象就被定义为活动的,因此不能删除。为了确保能回收对象占用的内存,编程人员必须确保该对象不能到达。这通常是通过将对象字段设置为null或者从集合(collection)中移除对象而完成的。但是,注意,当局部变量不再使用时,没有必要将其显式地设置为null。对这些变量的引用将随着方法的退出而自动清除。
  概括地说,这就是内存托管语言中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。
典型泄漏
  既然我们知道了在Java中确实有可能发生内存泄漏,就让我们来看一些典型的内存泄漏及其原因。
全局集合
  在大的应用程序中有某种全局的数据储存库是很常见的,例如一个JNDI树或一个会话表。在这些情况下,必须注意管理储存库的大小。必须有某种机制从储存库中移除不再需要的数据。
  这可能有多种方法,但是最常见的一种是周期性运行的某种清除任务。该任务将验证储存库中的数据,并移除任何不再需要的数据。
  另一种管理储存库的方法是使用反向链接(referrer)计数。然后集合负责统计集合中每个入口的反向链接的数目。这要求反向链接告诉集合何时会退出入口。当反向链接数目为零时,该元素就可以从集合中移除了。
缓存
  缓存是一种数据结构,用于快速查找已经执行的操作的结果。因此,如果一个操作执行起来很慢,对于常用的输入数据,就可以将操作的结果缓存,并在下次调用该操作时使用缓存的数据。
  缓存通常都是以动态方式实现的,其中新的结果是在执行时添加到缓存中的。典型的算法是:
检查结果是否在缓存中,如果在,就返回结果。
如果结果不在缓存中,就进行计算。
将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。
  该算法的问题(或者说是潜在的内存泄漏)出在最后一步。如果调用该操作时有相当多的不同输入,就将有相当多的结果存储在缓存中。很明显这不是正确的方法。
  为了预防这种具有潜在破坏性的设计,程序必须确保对于缓存所使用的内存容量有一个上限。因此,更好的算法是:
检查结果是否在缓存中,如果在,就返回结果。
如果结果不在缓存中,就进行计算。
如果缓存所占的空间过大,就移除缓存最久的结果。
将计算出来的结果添加到缓存中,以便以后对该操作的调用可以使用。
  通过始终移除缓存最久的结果,我们实际上进行了这样的假设:在将来,比起缓存最久的数据,最近输入的数据更有可能用到。这通常是一个不错的假设。
  新算法将确保缓存的容量处于预定义的内存范围之内。确切的范围可能很难计算,因为缓存中的对象在不断变化,而且它们的引用包罗万象。为缓存设置正确的大小是一项非常复杂的任务,需要将所使用的内存容量与检索数据的速度加以平衡。
  解决这个问题的另一种方法是使用java.lang.ref.SoftReference类跟踪缓存中的对象。这种方法保证这些引用能够被移除,如果虚拟机的内存用尽而需要更多堆的话。
ClassLoader
  Java ClassLoader结构的使用为内存泄漏提供了许多可乘之机。正是该结构本身的复杂性使ClassLoader在内存泄漏方面存在如此多的问题。 ClassLoader的特别之处在于它不仅涉及“常规”的对象引用,还涉及元对象引用,比如:字段、方法和类。这意味着只要有对字段、方法、类或 ClassLoader的对象的引用,ClassLoader就会驻留在JVM中。因为ClassLoader本身可以关联许多类及其静态字段,所以就有许多内存被泄漏了。
确定泄漏的位置
  通常发生内存泄漏的第一个迹象是:在应用程序中出现了 OutOfMemoryError。这通常发生在您最不愿意它发生的生产环境中,此时几乎不能进行调试。有可能是因为测试环境运行应用程序的方式与生产系统不完全相同,因而导致泄漏只出现在生产中。在这种情况下,需要使用一些开销较低的工具来监控和查找内存泄漏。还需要能够无需重启系统或修改代码就可以将这些工具连接到正在运行的系统上。可能最重要的是,当进行分析时,需要能够断开工具而保持系统不受干扰。
  虽然 OutOfMemoryError通常都是内存泄漏的信号,但是也有可能应用程序确实正在使用这么多的内存;对于后者,或者必须增加JVM可用的堆的数量,或者对应用程序进行某种更改,使它使用较少的内存。但是,在许多情况下,OutOfMemoryError都是内存泄漏的信号。一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加。如果确实如此,就可能发生了内存泄漏。

2007年8月26日星期日

接口和抽象类的定义方式举例说明(转)

本位转自中国IT实验室

原文地址:接口和抽象类的定义方式举例说明

接口定义
关于java的接口定义方式,以下三种情况下可以采用接口定义方式:
1.    接口中声明的变量全部为final 和static类型的,并且这个接口的作用在于定义一些值不能改变的变量。
举个例子:
public interface ObjectConstants{
public static final String SPACE = new String(" ");
public static final char FORMFEED = '\f';
}
2.    接口中只定义可供实现的抽象方法
EventListener.java
    public interface EventListener {
    public void handleEvent(Event evt);
    }
Runnable.java
package java.lang;
    public interface Runnable {   
    public abstract void run();
    }
3.    还有一种方式是上述两种方式的组合,如非必要一般会将这样一个接口定义拆分成两个接口定义
抽象类的定义
1.    如果一个类包含一个接口但是不完全实现接口定义的方法,那么该类必须定义成abstract型
例如InputStream.java类的定义方式:
package java.io;
public abstract class InputStream implements Closeable {
    // SKIP_BUFFER_SIZE is used to determine the size of skipBuffer
    private static final int SKIP_BUFFER_SIZE = 2048;
    // skipBuffer is initialized in skip(long), if needed.
    private static byte[] skipBuffer;   
    public abstract int read() throws IOException;
    public int read(byte b[]) throws IOException {
    return read(b, 0, b.length);
    }   
    public int read(byte b[], int off, int len) throws IOException {
    if (b == null) {
        throw new NullPointerException();
    } else if ((off < 0) || (off > b.length) || (len < 0) ||
           ((off + len) > b.length) || ((off + len) < 0)) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return 0;
    }
    int c = read();
    if (c == -1) {
        return -1;
    }
    b[off] = (byte)c;
    int i = 1;
    try {
        for (; i < len ; i++) {
        c = read();
        if (c == -1) {
            break;
        }
        if (b != null) {
            b[off + i] = (byte)c;
        }
        }
    } catch (IOException ee) {
    }
    return i;
    }
 public long skip(long n) throws IOException {
    long remaining = n;
    int nr;
    if (skipBuffer == null)
        skipBuffer = new byte[SKIP_BUFFER_SIZE];
    byte[] localSkipBuffer = skipBuffer;        
    if (n <= 0) {
        return 0;
    }
    while (remaining > 0) {
        nr = read(localSkipBuffer, 0,
              (int) Math.min(SKIP_BUFFER_SIZE, remaining));
        if (nr < 0) {
        break;
        }
   remaining -= nr;
    }    
    return n - remaining;
    } 
    public int available() throws IOException {
    return 0;
    }   
    public void close() throws IOException {}  
    public synchronized void mark(int readlimit) {}    
    public synchronized void reset() throws IOException {
    throw new IOException("mark/reset not supported");
    }
    public boolean markSupported() {
    return false;
    }
}
2.    抽象类的方法体中只定义抽象的方法,例如AbstractMethodError.java
    package java.lang;
    public class AbstractMethodError extends IncompatibleClassChangeError {
    public AbstractMethodError() {
    super();}
    public AbstractMethodError(String s) {
    super(s); }
}

2007年8月24日星期五

如何获得数据库里所有表的名字(转)

在CSDN上看到这篇文章,感觉很有用,不敢独享,转过来大家一起看看!

平时我们操作比较多的都是表里的数据,也许突然有一天会需要把所有表的名字都列出来看一看——比如,你的论坛是按每个版块一个表来管理的,这时候你要在首页列出各版块的名字。应该怎么办呢?

肯定得用SELECT吧……但我们平时使用SELECT操作的数据都是表里的数据,表的名字并不是表的数据,这可怎么办呢?

......

原文是用C#实现的,不过我感觉用其他的语言(比如Java)也是很简单的。 

下面是这篇文章的链接:

如何获得数据库里所有表的名字

Java中堆和栈的区别(转)

     本文摘自天极网     作者:dave     原文出处:Java中堆和栈的区别

      栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
     Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
     栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
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=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
      要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。
String是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";
      两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。
比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。
     因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。
      另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。
由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

2007年8月21日星期二

规范?!

在学校里,一直都知道,做事情(开发)要有规范,应该先怎样,然后怎样,最后做什么,这些都是知道的,而且还是学的重点。可是,真真到了岗位上,真真要自己动手的时候,什么都抛掉了!

只图方便,只图快捷,根本就不管什么可读性,什么可维护性,连注释都没有,更不用说什么文档的。昨晚也想到要好好的写写文档,可是今天到了公司,又不愿意写了。其实,也是另有内情:我实在是不知道,到底该如何下手。

所以,我也请晓鸣能够把他以前写的文档,借我看看,让我好有个参考,看看到底大致该怎么写,应该写些什么内容。

当然,这次,我也暂且先不写,一方面,考虑到,目前为在做的只是一个试用版,日后一定会再做过的;另一方面,钱好像急着想用它,就先完成它再说吧(也许完成后,再来写文档也好,我知道,这个不可取,可总比没有好)。

刚刚,我也想到,等这个完成,给钱之后,让他先去跑跑看;然后,我再抛掉所有的东西,重头做过。到时候要好好的做!

可是,我把这个想法跟晓鸣说了之后,问他我有没有可能重新做过,他却说不大可能。郁闷......看不起我!呵呵......也以此为鉴吧!希望我日后,不要如晓鸣所言,而且,这个算是我的第一个项目,一定要好好做才行。

写此文以记之,望日后自省,望同仁提点!

 

PS:我也深刻认识到,没有写文档所带来的麻烦:尽管大致的结构、功能,心理都清楚,可是很多小地方,细节的东西,都是到了临时才会想起来,这样的开发,不仅没有质量保证,而且效率也不高!很多时候,还要仔细地想清楚,到底有没有什么细节没有考虑进去。

2007年8月18日星期六

Core Java2 6th 摘要(8)

每个组件都有三要素:

  • 内容,例如,按钮的状态(是否按下)或者文本框中的文本
  • 外观显示(颜色、尺寸等)
  • 行为(对事件的反应)

 

模型——视图——控制器(MVC)模式实现三个独立的类:

  • 模型——存储内容
  • 视图——显示内容
  • 控制器——处理用户输入

模型——视图——控制器模式的一点优点是一个模型可以有多个视图,其中每个视图可以显示完整内容的不同部分或不同方面。

 

使用setColumns方法改变了一个文本框的大小以后,以需要调用包容该文本框的容器的validate方法。

textField.setColumns(10);

panel.validate();

validate()方法会重新计算容器内所有组件的大小,并且对它们重新布局。使用完validate()方法以后,布局管理器会重新绘制容器,然后就可以看到改变尺寸后的文本框。

 

通常,不可能通过过滤器避免所有的无效字符串。 

过滤器的另外一个用途就是把字符串中的所有字符变成大写。这样的过滤器很容易编写,在过滤器的insertString和replace方法中,把要被插入的字符串转换为大写的,然后调用超类方法。

 

你可以给任何JComponent附加检验器。如果组件失去焦点,那么就询问检验器。如果检验器报告说组件的内容无效,该组件立即重新获得焦点,用户在提供其他输入前强迫修改内容。

 

在Swing中,组件增加滚动条的通用机制是:把组件添加进一个滚动窗格中。

 

标签是容纳文本的组件。这种组件没有修饰(例如,没有边界),它们也不响应用户输入,你可以使用一个标签来标识组件。给组件设置标签的方法如下:

  1. 使用正确的文本构造一个JLabel组件。
  2. 把该标签组件放置到同需要标识的组件足够近的地方,这样用户能看到该标签标识了哪个组件。

从J2SE1.3开始,按钮、标签以及菜单项中不仅可以无格式的文本,还可以使用HTML文本。注意,第一个使用HTML标签的组件需要延迟一段时间才能显示出来,这是因为必须加载相当复杂的HTML翻译代码。

2007年8月17日星期五

Core Java2 6th 摘要(7)

任何支持GUI的操作都会不断地监视敲打键盘、单击鼠标等事件,操作环境会把这些事件报告给正在运行的程序,程序会决定如何响应这些事件。

 

事件源拥有自己的方法,允许我们向其注册事件监听器。当事件源产生某个事件时,事件源会向注册在那个事件上的所有事件监听器对象发送通知。

在像Java这样的面向对象语言中,关于事件的信息被封装在一个事件对象中。所有的事件对象都是从Java.util.EventObject派生出来的。

 

AWT中事件处理机制的概览:

  • 一个监听器对象是一个实现了专门的监听器接口的类的实例。
  • 一个事件源是一个能够注册监听器对象并向它们发送事件对象的对象。
  • 事件发生时,事件源会把事件对象发生给所有的注册监听器。
  • 监听器对象随后会使用事件对象中的信息来决定对事件的反应。

 

对于ActionListener接口,它可用于多种情况:

  •  当通过双击来选择列表框的一项时。
  • 当选择一菜单项时。
  • 当在文本域中按下ENTER键时。
  • 对于Timer组件来说,当指定时间达到时。

使用ActionListener接口的方法在所有的情况下都一样:actionPerformed方法(ActionListener接口中唯一的方法)只有一个参数,是一个ActionEvent类型的对象。该事件对象给出了所发生事件的详细信息。

 

在Java中,类实现一个接口意味着它要实现该接口中所有方法。

每个具有不止一个方法的AWT监听器接口都有一个实现其所有方法,但方法中什么也不做的适配器类。

 

一个想接收某一事件的类必须实现一个监听器接口。这个类要在事件源中注册自己,然后,接收所需的事件对象。并通过监听器接口中的方法做出相应的处理。

 

AWT明确区分语义事件和底层事件。  语义事件是用于表达用户动作(如“点击按钮”)的事件;底层事件是使这些成为可能的事件。类似地,调整滚动条是个语义事件,而拖动鼠标则是一个底层事件。

 

事件源是用户界面组件、窗口和菜单。操作系统会把用户的动作(如鼠标移动和击键操作)通知给感兴趣的事件源。事件源在一个事件对象中描述事件的特征。事件源还保持有一组监听器——当事件发生时需要调用的对象。当事件发生时,事件源调用监听器接口中的适当方法把事件信息传递给多个监听器。事件源是通过把适当的事件对象传递给监听器类的方法来实现这一点的。监听器通过分析事件对象以找出关于这个事件更多的信息。

 

Swing包提供了一个非常有用的机制,用来封装命令,并把它们连接到多个事件源。这种机制就是Action接口,一个动作是一个对象。它封装了:

  • 命令的说明(用字符串或可选图标表示)。
  • 执行命令需要的参数。

 

所有的AWT事件源都支持一种对应监听器的多点传递模型。这意味着同一事件被传递到不止一个监听器对象。

API不对向事件源注册的一组监听器传递事件的次序作任何保证。因此,不要编写依赖传递次序的逻辑。

 

当操作环境响应用户动作(如鼠标点击)生成事件时,同操作环境通信的AWT部分会收到一个通知,并把它转化为一个AWT事件。接着,AWT把这个事件添加到一个事件队列中。AWT中把事件分派到监听器的部分会:

  • 从事件队列中提取事件。
  • 为事件定位监听器对象。
  • 为事件调用正确的监听器过程。

2007年8月16日星期四

挫折?鞭策!

昨天,有提到关于餐馆的事,今天写了一下概要设计(以前好像从来没有写过),还有就是确定了数据的表。在做概要设计的时候(我是按照国标写的),发现,很多项都很烦的样子。主要是我都看不懂这些条目到底要写些什么,所以都搁在那边,不高兴写了。

我知道这是因为我以前都没有做过项目,我也提过了,原来课程上的那些项目只要写些文档就搞定了,而且还是很多人一起写文档,所以并不能真真的理解这个文档的作用,当然也不知道如何下手喽!我也知道文档的重要性,至少学校里的老师和书都是这么说的......呵呵!

下午,草草地写完了概要设计(其实很多条目还空着),然后完成了数据库表的设计,还有点时间多,我就继续了下前几天写的访问数据库的操作。

因为我是一个超级菜鸟,而且是那种没有飞过的菜鸟,所以所有的东西都是从头开始的。关于那个数据库操作,也是前几天我请晓鸣教我的,那天晚上是顺利的去出了menu表中的数据。今天,我想自己依着那天的思路,写一个函数:getDishByName。居然出了个错,于是我马上就僵死在那边了,郁闷啊!只能回头再请教晓鸣了!一遍又在感慨自己的没用......

不过,我还是感觉自己收获颇丰的,特别是那天晓鸣教我具体的数据库操作的时候。因为,我也知道,其实这些都不难,平常在看代码的时候也基本上能懂,但自己却不知道怎么下手写自己的数据库操作函数.......呵呵。可是,那天晓鸣教了下后,感觉豁然开朗......不过今天马上受挫!无奈......

看来,还得不断的努力啊!  呵呵,没有急功近利的意思,只是鞭策一下自己!

各位,多给我提提意见啊,希望知无不言,言无不尽!  谢谢......

Core Java2 6th 摘要(6)

内部类是定义在其他类内部的类。使用内部类的原因有以下四个:

  • 内部类对象能够访问创建它的对象的实现——包括那些私有数据;
  • 内部类能够隐藏起来,不为同一包中的其他类所见;
  • 匿名内部类可以方便地定义运行时回调;
  • 使用内部类在编写事件驱动的程序时用起来很方便。

局部类不会使用访问指示符(如public或者private)来声明。它们的范围总是限定在声明它们的程序快中。

 

你可以在运行时使用代理创建实现了一组给定接口的新类。只有在编译时无法确定要实现哪些接口时,才有必要使用代理。

 

Java中的顶层窗口(即那些没有包含在其他窗口中的窗口)被称为框架。

框架是一个容器。这意味着一个框架可以容纳按钮、文本域等其他用户界面组件。

 

JFrame类中几个可能最为重要的方法:

  • dispose方法:关闭窗口并收回用于创建窗口的任何系统资源;
  • setIconImage方法:当窗口最小化(在Java中常常称作图标化)时,把一个Image对象作为图标;
  • setTitle方法:改变标题栏中的文字;
  • setResizable方法:使用boolean参数来决定框架大小是否能被用户改变。

2007年8月15日星期三

My first Mashup!

昨天,在MS上申请了一个Popfly帐户。今天,可以用了。

作为第一次使用Popfly,我选择了创建我的第一个Mashup,是根据它的向导来做的,做得不好,还请大家多多包涵。不过,我把它看作是一个起点......

新手入门:写Java程序的三十个基本规则

<摘自天极网>

2007-08-13 09:09 作者: Senton 出处: 天极Yesky软件频道 责任编辑:方舟

 

  (1) 类名首字母应该大写。字段、方法以及对象(句柄)的首字母应小写。对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字母。例如:

  ThisIsAClassName

  thisIsMethodOrFieldName

  若在定义中出现了常数初始化字符,则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。

  Java包(Package)属于一种特殊情况:它们全都是小写字母,即便中间的单词亦是如此。对于域名扩展名称,如com,org,net或者edu等,全部都应小写(这也是Java 1.1和Java 1.2的区别之一)。

  (2) 为了常规用途而创建一个类时,请采取“经典形式”,并包含对下述元素的定义:

  equals()

  hashCode()

  toString()

  clone()(implement Cloneable)

  implement Serializable

  (3) 对于自己创建的每一个类,都考虑置入一个main(),其中包含了用于测试那个类的代码。为使用一个项目中的类,我们没必要删除测试代码。若进行了任何形式的改动,可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。

  (4) 应将方法设计成简要的、功能性单元,用它描述和实现一个不连续的类接口部分。理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做同样的一件事情)。 (5) 设计一个类时,请设身处地为客户程序员考虑一下(类的使用方法应该是非常明确的)。然后,再设身处地为管理代码的人考虑一下(预计有可能进行哪些形式的修改,想想用什么方法可把它们变得更简单)。

  (6) 使类尽可能短小精悍,而且只解决一个特定的问题。下面是对类设计的一些建议:

  ■一个复杂的开关语句:考虑采用“多形”机制

  ■数量众多的方法涉及到类型差别极大的操作:考虑用几个类来分别实现

  ■许多成员变量在特征上有很大的差别:考虑使用几个类 。

  (7) 让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”(一个方法、类或者一个字段等等),就永远不能把它拿出。若强行拿出,就可能破坏其他人现有的代码,使他们不得不重新编写和设计。若只公布自己必须公布的,就可放心大胆地改变其他任何东西。在多线程环境中,隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。

  (8) 谨惕“巨大对象综合症”。对一些习惯于顺序编程思维、且初涉OOP领域的新手,往往喜欢先写一个顺序执行的程序,再把它嵌入一个或两个巨大的对象里。根据编程原理,对象表达的应该是应用程序的概念,而非应用程序本身。

  (9) 若不得已进行一些不太雅观的编程,至少应该把那些代码置于一个类的内部。

  (10) 任何时候只要发现类与类之间结合得非常紧密,就需要考虑是否采用内部类,从而改善编码及维护工作(参见第14章14.1.2小节的“用内部类改进代码”)。

  (11) 尽可能细致地加上注释,并用javadoc注释文档语法生成自己的程序文档。

  (12) 避免使用“魔术数字”,这些数字很难与代码很好地配合。如以后需要修改它,无疑会成为一场噩梦,因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以,我们应创建一个常数,并为其使用具有说服力的描述性名称,并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。

  (13) 涉及构建器和异常的时候,通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来,调用者就不会以为那个对象已正确地创建,从而盲目地继续。

  (14) 当客户程序员用完对象以后,若你的类要求进行任何清除工作,可考虑将清除代码置于一个良好定义的方法里,采用类似于cleanup()这样的名字,明确表明自己的用途。除此以外,可在类内放置一个boolean(布尔)标记,指出对象是否已被清除。在类的finalize()方法里,请确定对象已被清除,并已丢弃了从RuntimeException继承的一个类(如果还没有的话),从而指出一个编程错误。在采取象这样的方案之前,请确定finalize ()能够在自己的系统中工作(可能需要调用System.runFinalizersonExit(true),从而确保这一行为)。

  (15) 在一个特定的作用域内,若一个对象必须清除(非由垃圾收集机制处理),请采用下述方法:初始化对象;若成功,则立即进入一个含有finally从句的try块,开始清除工作。

  (16) 若在初始化过程中需要覆盖(取消)finalize(),请记住调用super.finalize()(若Object属于我们的直接超类,则无此必要)。在对finalize()进行覆盖的过程中,对super.finalize()的调用应属于最后一个行动,而不应是第一个行动,这样可确保在需要基础类组件的时候它们依然有效。

  (17) 创建大小固定的对象集合时,请将它们传输至一个数组(若准备从一个方法里返回这个集合,更应如此操作)。这样一来,我们就可享受到数组在编译期进行类型检查的好处。此外,为使用它们,数组的接收者也许并不需要将对象“造型”到数组里。

  (18) 尽量使用interfaces,不要使用abstract类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个interface(接口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。

  (19) 在构建器内部,只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法,因为那些方法可能被其他人覆盖或取消,从而在构建过程中产生不可预知的结果(参见第7章的详细说明)。

  (20) 对象不应只是简单地容纳一些数据;它们的行为也应得到良好的定义。

  (21) 在现成类的基础上创建新类时,请首先选择“新建”或“创作”。只有自己的设计要求必须继承时,才应考虑这方面的问题。若在本来允许新建的场合使用了继承,则整个设计会变得没有必要地复杂。

  (22) 用继承及方法覆盖来表示行为间的差异,而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色,这是绝对应该避免的:应直接使用一个“颜色”字段。

  (23) 为避免编程时遇到麻烦,请保证在自己类路径指到的任何地方,每个名字都仅对应一个类。否则,编译器可能先找到同名的另一个类,并报告出错消息。若怀疑自己碰到了类路径问题,请试试在类路径的每一个起点,搜索一下同名的.class文件。

  (24) 在Java 1.1 AWT中使用事件“适配器”时,特别容易碰到一个陷阱。若覆盖了某个适配器方法,同时拼写方法没有特别讲究,最后的结果就是新添加一个方法,而不是覆盖现成方法。然而,由于这样做是完全合法的,所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。

  (25) 用合理的设计方案消除“伪功能”。也就是说,假若只需要创建类的一个对象,就不要提前限制自己使用应用程序,并加上一条“只生成其中一个”注释。请考虑将其封装成一个“独生子”的形式。若在主程序里有大量散乱的代码,用于创建自己的对象,请考虑采纳一种创造性的方案,将些代码封装起来。

  (26) 警惕“分析瘫痪”。请记住,无论如何都要提前了解整个项目的状况,再去考察其中的细节。由于把握了全局,可快速认识自己未知的一些因素,防止在考察细节的时候陷入“死逻辑”中。

  (27) 警惕“过早优化”。首先让它运行起来,再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候,才应进行优化。除非用专门的工具分析瓶颈,否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解,而且难于维护。

  (28) 请记住,阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序,但注释、细致的解释以及一些示例往往具有不可估量的价值。无论对你自己,还是对后来的人,它们都是相当重要的。如对此仍有怀疑,那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折,这样或许能将你说服。

  (29) 如认为自己已进行了良好的分析、设计或者实施,那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家,但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作,看看是否能找出你一度熟视无睹的问题。采取这种方式,往往能在最适合修改的阶段找出一些关键性的问题,避免产品发行后再解决问题而造成的金钱及精力方面的损失。

  (30) 良好的设计能带来最大的回报。简言之,对于一个特定的问题,通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法,以后的工作就轻松多了,再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报(甚至无可估量)。而且由于自己倾注了大量心血,最终获得一个出色的设计方案,成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失。

Core Java2 6th 摘要(5)

如同一个Employee对象描述一个特定员工的属性一样,一个Class类描述一个特定类的属性。Class类中最常用的方法可能就是getName,它能返回类的名称。

 

能够分析类的能力的程序称为反射器。

  • 在运行时分析类的能力;
  • 在运行时探查对象.比如,只写一个toString方法供所有的类使用;
  • 实现通用数组操作代码;
  • 利用method对象.这个对象就像C++中的函数指针.

设计继承的建议:

  1. 把通用字段和操作放到超类中;
  2. 不要使用受保护字段;
  3. 使用继承来模型化"is-a"关系;
  4. 除非所有继承的方法都有意义,否则不要使用继承;
  5. 使用多态,而非类型信息;
  6. 不要滥用反射.

2007年8月14日星期二

Windows编程——Windows编程基础(3)

续。。。。。。。。

4. TextBox控件

文本框的主要用途是让用户输入文本,用户可以输入任何字符,也可以限制用户只输入数值。

.NET Framework内置了两个基本控件来提取用户输入的文本:TextBox和RichTextBox。这两个控件都派生于TextBoxBase,而TextBoxBase又派生于Control。

TextBoxBase提供了在文本框中处理文本的基本能力,例如选择文本、剪切和从剪切板上粘帖,以及许多事情。

4.1 TextBox控件的属性

常用属性:

  • CausesValidation:当控件的这个属性为true,且该控件获得了焦点时,会引发两个事件:Validating和Validated。可以处理这些事件,以便验证失去焦点的控件中数据的有效性。
  • CharacterCasing:这个值表示TextBox是否会改变输入的文本的大小写。可能的值有:
    1. Lower:文本框中输入的所有文本都转换为小写;
    2. Normal:不变;
    3. Upper:都转换为大写。
  • MaxLength:这个值指定输入到TextBox中的文本的最大字符长度。如果设为0,表示最大字符长度仅受限于可用的内存。
  • Multiline:表示该控件是否是一个多行控件。多行控件可以显示多行文本。
  • PasswordChar:指定是否用密码字符替换在单行文本框中输入的字符。如果Multiline属性为true,这个属性就不起作用。
  • ReadOnly:这个Boolean值表示文本是否为只读。
  • ScrollBars:指定多行文本框是否显示滚动条。
  • SelectedText:在文本框中选择的文本。
  • SelectionLength:在文本中选择的字符数。如果这个值设置得比文本中的字符数大,则控件会把它重新设置为字符总数减去SelectionStart的值。
  • SelectionStart:文本框中被选中文本的开头。
  • WordWrap:指定在多行文本框中,如果一行的宽度超出了控件的宽度,其文本是否应自动换行。

4.2 TextBox控件的事件

TextBox提供了以下所示的事件:

  • Enter、Leave、Validating、Validated:这4个事件按照列出的顺序引发。它们统称为焦点事件,当控件的焦点发生改变时引发,但有两个例外:Validating和Validated仅在控件接收了焦点,且其CausesValidation属性设置为true时引发。接收焦点的控件引发事件的原因是有时即使焦点改变了,我们也不希望验证控件的有效性。
  • KeyDown、KeyPress、KeyUp:这三个事件称为“键事件”。它们可以监视和改变输入到控件中的内容。KeyDown和KeyUp接收与所按下键对应的键码,这样就可以确定是否按下了特殊的键Shift或Control和F1。另一方面,KeyPress接收与键对应的字符。这表示字母a的值与A的值不同。
  • Change:只要文本框的文本发生了改变,就会引发该事件。

Come ON!

钱一直在提关于餐馆的计划,昨天,提到了关于界面的设计问题,我也提出了我自己的一些看法,感觉应该如何如何。

对于这个,郭老师,好像也很支持的样子,感觉他也是有兴趣做这一块的事情的。我也认识到,我现在确实也没什么事情在做(或者说是能做),所以,我也主动提出由我来做做看(当时,好像是指界面)。

今天,在提起来的时候,我也感觉我非常想做这个系统,毕竟可以是我的一个切入口,学习和工作的切入口。我要好好的努力,抓住这个机会。况且,郭老师,也提过我可以做做这个系统,毕竟可以(要)慢慢地锻炼起来。

另一方面,我也认识到,其实这个系统不难,而且郭老师和晓鸣最近也很忙,所以我也是应该是主要的参与人员,或者说,基本上要由我来完成,我也想挑战一下。当然,期间会得到很多来自郭老师、晓鸣还有钱的帮助。

今天算是计划启动吧,初步确定启动。

定一下明天的任务:确定界面的选择(不过按照钱的意思,好像是三种方案都要做出来 ......然后,还有就是写一下需求文档,再有就是稍微确定下详细设计)。

 

Come on!

2007年8月12日星期日

Windows编程——Windows编程基础(2)

续:

2. Button控件

按钮主要用于执行3类任务:

  • 用某种状态关闭对话框(如OK和Cancel按钮)。
  • 给对话框上输入的数据执行操作(如,输入一些搜索条件后,单击Search)。
  • 打开另一个对话框或应用程序(如Help按钮)。

2.1 Button控件的属性:

Buttom类最常用的属性,它们都是在ButtonBase基类中定义的。

  • FlatStyle:改变按钮的样式。
  • Enabled:把Enabled设置为false,则该按钮就会灰显,单击它,不会起任何作用。此属性派生于Control。
  • Image:可以指定一个在按钮上显示的图像(位图、图标等)。
  • ImageAlign:按钮上图像的显示位置。

2.2 Button控件的事件:

private void button1_Click(object sender, EventArgs e)

{...}

对于Click事件,第一个参数Object sender包含被单击的控件。另一个参数EventArgs e包含所发生事件的信息。

3. Label和LinkLabel控件

Label控件(标签)是一个简单的控件,其用途只有一个:在窗体上显示文本。

.NET Framework包含两个标签控件,它们可以用两种截然不同的方式显示:

  • Label是标准的Windows标签。
  • LinkLabel以Internet链接的方式显示(超链接)。

尽管Label通常不需要添加任何事件处理代码,但它也支持事件。

Label控件常用的属性:

  • BorderStyle:指定标签边框的样式。默认为无边框。
  • FlatStyle:控制显示控件的方式。
  • Image:指定要在标签上显示的图像(位图、图标等)。
  • ImageAlign:图像显示在标签的什么位置。
  • LinkArea:(只用于LinkLabel)文本中显示为链接的部分。
  • LinkColor:(只用于LinkLabel)链接的颜色。
  • Links:(只用于LinkLabel)LinkLabel可以包含多个链接。利用这个属性可以查找需要的链接。控件会跟踪显示文本中的链接,不能在设计期间使用。
  • LinkVisited:(只用于LinkLabel)把它设置为true,单击控件,链接就会显示为另一种颜色。
  • TextAlign:文本显示在控件中的什么位置
  • VisitedLinkColor:(只用于LinkLabel)用户单击LinkLabel后控件的颜色

Windows编程——Windows编程基础(1)

1. 控件

在使用Windows窗体时,就是在使用System.Windows.Forms命名空间。

.NET中的大多数控件都派生于System.Windows.Form.Control类。这个类定义了控件的基本功能。还有一些控件称为定制或用户控件,派生于另一个类System.Windows.Forms.UserControl。这个类本身派生于Control类,提供了创建控件所需要的功能。另外,用于设计Web用户界面的控件派生于另一个类System.Web.UI.Control。

1.1 属性

所有控件都有许多属性,用于处理控件的操作。

下面是Control类最常见的属性:

Anchor:指定当控件的容器的大小发生变化时,该控件如何响应。

BackColor:背景色

Bottom:指定控件的底部距离窗口的顶部有多远。这与指定控件的高度不同

Dock:可以使控件停靠在窗口的边界上

Enabled:把Enable设为true通常表示该控件可以接收用户的输入。把Enable设置为false通常表示不能接收用户的输入

ForeColor:前景色

Height:控件从底部到顶部的距离(是控件本身的距离)

Left:控件的左边界到窗口左边界的距离

Name:控件的名称,这个名称可以在代码中用于引用该控件

Parent:控件的父控件

Right:控件的右边界到窗口右边界的距离

TabIndex:控件在容器中的标签顺序号

TabStop:指定控件是否可以用Tab键访问

Tag:这个值通常不由控件本身使用,而是在控件中存储该控件的信息。当通过Windows Form设计器给这个属性赋值时,就只能给它赋一个字符串值

Top:控件的顶部距离窗口顶部的距离

Visible:指定控件是否在运行期间可见

Width:控件的宽度

另外,.NET的所有内部控件都使用Text属性来设置显示的文本。

1.2 控件的定位、停靠和对齐

在Visual Studio 2005中,窗体设计器默认改为使用栅格状的界面,并使用捕捉线来定位控件,使控件整齐地排列在界面上。

在窗体设计中,Anchor和Dock属性特别有用。

Anchor属性可以指定如果控件重新设置了大小,就根据控件的边界锁定它,或者其大小不变,当根据窗口的边界来锚定它的位置。

Dock属性用于指定控件应停放在容器的边框上。如果用户重新设置了窗口的大小,该控件将继续停放在窗口的边框上。

1.3 事件

下面所列的是Control类所定义的最常见的事件:

Click:在单击控件时引发。在某些情况下,这个事件也会在用户按下Enter键时引发。

DoubleClick:在双击控件时引发。处理某些控件上的Click事件,例如Button控件,表示永远不会调用DoubleClick事件。

DragDrop:在完成拖放操作时引发。换言之,当一个对象被拖到控件上,然后用户释放鼠标按钮后,引发该事件。

DragEnter:在被拖动的对象进入控件的边界时引发。

DragLeave:在被拖动的对象移出控件的边界时引发。

DragOver:在被拖动的对象放在控件上时引发。

KeyDown:当控件有焦点时,按下一个键时引发该事件,这个事件总是在KeyPress和KeyUp之前发生。

KeyPress:当控件有焦点时,按下一个键发生该事件,这个事件总是在KeyDown之后、KeyUp之前引发。KeyDown和KeyPress的区别是KeyDown传送被按下的键的键盘码,而KeyPress传送被按下键的char值。

KeyUp:当控件有焦点时,释放一个键时发生该事件,这个事件总是在KeyDown和KeyPress之后引发。

GotFocus:在控件接收焦点时引发。不要用这个事件执行控件的有效性验证,而应使用Validating和Validated。

LostFocus:在控件丢失焦点时引发。不要用这个事件执行控件的有效性验证,而应使用Validating和Validated。

MouseDown:在鼠标指针指向一个控件,且鼠标按钮被按下时引发。这与Click事件不同,因为在按钮被按下之后,且未被释放之前引发MouseDown。

MouseMove:在鼠标划过控件时引发。

MouseUp:在鼠标指针位于控件上,且鼠标按钮被释放时引发。

Paint:绘制控件时引发。

Validated:当控件的CausesValidation属性设置为true,且该控件获得焦点时,引发该事件。它在Validating事件之后发生,表示有效性验证已经完成。

Validating:当控件的CausesValidation属性设置为true,且该控件获得焦点时,引发该事件。需要注意的是,被验证有效性的控件是失去焦点的控件,而不是获得焦点的控件。

 

 

待续.................................

2007年8月11日星期六

 
Posted by Picasa

J2EE相关名词解释

(转自赛迪网)

 

容器:充当中间件的角色

WEB容器:给处于其中的应用程序组件(JSP,SERVLET)提供一个环境,使 JSP,SERVLET直接跟容器中的环境变量接口交互,不必关注其它系统问题。主要由WEB服务器来实现。例如:TOMCAT,WEBLOGIC, WEBSPHERE等。该容器提供的接口严格遵守J2EE规范中的 WEB APPLICATION 标准。我们把遵守以上标准的WEB服务器就叫做J2EE中的WEB容器。

EJB容器:Enterprise java bean容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。只要满足J2EE规范的EJB放入该容器,马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。

WEB容器和EJB容器在原理上是大体相同的,更多的区别是被隔离的外界环境。 WEB容器更多的是跟基于HTTP的请求打交道。而EJB容器不是。它是更多的跟数据库、其它服务打交道。但他们都是把与外界的交互实现从而减轻应用程序的负担。例如SERVLET不用关心HTTP的细节,直接引用环境变量 session,request,response就行、EJB不用关心数据库连接速度、各种事务控制,直接由容器来完成。

RMI/IIOP:远程方法调用/internet对象请求中介协议,他们主要用于通过远程调用服务。例如,远程有一台计算机上运行一个程序,它提供股票分析服务,我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。

JNDI:JAVA命名目录服务。主要提供的功能是:提供一个目录系统,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。

JMS:JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。

JAVAMAIL:JAVA邮件服务。提供邮件的存储、传输功能。它是JAVA编程中实现邮件功能的核心。相当MS中的EXCHANGE开发包。

JTA:JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。

JAF:JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。

EAI:企业应用集成。是一种概念,从而牵涉到好多技术。J2EE技术是一种很好的集成实现。

Java程序员要掌握的十个JSP中的标签库

(转自赛迪网)

 

Standard

这个库必不可少,因为它结合了对普通JSP的大量核心改进。它的一些特性包括:XML操作库、SQL库、特殊国际化功能、以及对迭代器和其它程序控制机制的支持。在需要高级流程控制结构、国际化功能或XML支持时使用这个库。

DateTime

这个库提供各种操作日期和时间的工具,包括恢复当前日期和时间、生成日期和月份列表、格式化日期和时间、时区转换。使用这个库可以方便地把日期和时间操作程序加入到你的应用程序中。

Mailer

这个库通过一个用户定义的SMTP服务器支持邮件发送,为你的JSP应用程序增加电子邮件发送功能。它还支持多位收件人、定制标题、MIME附件和错误处理。当需要在JSP页面中创建和发送电子邮件消息时使用这个库。

Cache

这个库为一个网站提供一个简单的缓冲框架。它将缓冲页面转译给客户端,允许你缓冲部分页面,缩短响应时间。如果你的网站接收许多流量,使用这个库不时为客户端提供页面缓冲快照而非“实况”页面,从而减轻服务器负载和页面处理时间。

XTages

这个库提供程序简化用XSLT解析和转换XML文档的过程。它可用于将一个XSLT转换应用于XML文档中、增加或删除XML节点、评估Xpath表达式和定义模板规则。当你需要高级XML/XSLT处理功能使用这个库。

Regexp

这个库允许你在JSP应用程序中使用与Perl5兼容的常规表达式。这个库支持Perl的匹配(m)、替换(s)和分解操作符。当你需要模式匹配和替代的常规表达式支持时使用这个库。

JSP Controls

这个库允许你建立单个页面组件,如登录表格或进度条,它们可以进行单独更新。这个库对AJAX应用程序特别有用,虽然它也可用在非AJAX模式中。在建立AJAX类型的JSP应用程序时使用这个库。

Pagers

这个库为数据集实施一个分页系统,简化在大型数据集(如数据库搜索结果)中来回移动的操作过程。它还包括各种分页样式,支持可浏览的结果目录。当你的应用程序需要一个粗劣的分页系统时使用这个库。

JDBForms

这个库可以在数据库连接和表单元素之间建立链接,建立定制行动迅速简化数据库驱动的Web应用程序的开发。它还支持分类、搜索、过滤和样式功能,并使用一个基于SWING的界面。要迅速开发数据库支持的JSP应用程序时使用这个库。

Google Tag Library

这个库可以执行和处理Google搜索,检查拼写并且访问Google.com缓存中的页面。使用这个库给你的网站增加Google搜索功能,或创建一个使用Google.com数据库的搜索引擎。

2007年8月10日星期五

Windows Live Writer

刚刚下了这个Windows Live Writer,一款博客的客户端管理工具,微软出品。  还不知道好不好用,先用来做个测试吧,如果好用的话,以后就继续,并且推荐给各位;如果不好的话,呵呵,自然是......

 

ps:这个是beta版的。

Core Java2 6th 摘要(4)

方法参数共有两种:
基本类型(数字、布尔值)
对象引用:方法得到对对象引用的一个拷贝,原来的对象和这个拷贝所指向的是同一个对象。
方法不能改变基本类型的参数,但对于对象参数来说,情况有所不同。

Java程序设计语言中利用方法参数可以做到和做不到的几种情况:
方法不能修改基本类型的参数;
方法可以修改对象参数的状态;
方法不能让对象参数指向新的对象。

如果一个类声明为final,只有它的方法(而不包括字段)会被自动设为final。

在类型转换时,Java在运行时,系统会检查类型转换能否进行。若不能,则抛出异常。如果没有捕捉这个异常,程序就会终止。因此,要养成在类型转换之前先判断它是否能成功的编程习惯,这个判断只需要简单地使用instanceof操作符。

具有一个或多个抽象方法的类本身也必须声明为abstract的。

抽象类不能被实例化,即如果一个类被声明为abstract,就不能构建这个类的任何对象。但仍然可以创建抽象类的对象变量,只是这个变量必须指向它的非抽象之类的对象。
在C++中,抽象方法称为纯虚函数,而且在结尾处标记上“=0”,只要定义了一个纯虚函数,那么C++类就是抽象的,在C++中,没有专门的关键字来定义纯虚函数。

Object类中的equals方法用于测试某个对象是否同另一个对象相等。它在Object类中的实现是判断两个对象是否指向同一块内存区域。这种测试没有太大用处。如果想测试对象是否相等,就需要覆盖equals方法,进行更有意义的比较。

编写完美equals方法的建议:
1. 显示参数命名为otherObject
2. 测试this同otherObject是否是同一个对象:
if(this == otherObject) return ture;
3. 测试otherObject是否为null。如果是,就返回false。这个测试是必需的:if(otherObject == null) return false;
4. 测试this和otherObject是否属于同一个类。这项测试是“对称性规则”所要求的。 if(getClass() != otherObject.getClass()) return false;
5. 把otherObject的类型转换为你的类型所属的类型。
ClassName other = (ClassName)otherObject;
6. 最后比较所有字段。使用==比较基本类型字段,使用equals比较对象字段。

包装器内包含的信息不可更改。

2007年8月9日星期四

JSP基础1:创建动态页面实现数据表示(2)

在JSP页面中可以嵌入采用另一种编程语言编写的代码,这称为脚本(scripting)。
有3中不同类型的脚本元素:
声明(declaration)
小脚本(scriptlet)
表达式(expression)
声明是用于声明变量和方法的Java代码。 形式如下:
<%!.....Java声明放在这里......%>
......Java声明放在这里......
小脚本是任意的Java代码段。 形式如下:
<% Java代码 %>
......Java代码......
表达式是能够生成一个结果值的Java表达式。执行JSP时,这个值会转换为一个文本串,并显示在脚本元素所在的位置。 形式如下:
<%=......Java表达式......%>
......Java表达式......

要向一个URL请求附加参数,第一种方法称为GET方法,参数数据会与URL一同发送,参数实际上是URL的一部分:有两个限制:
参数值的长度不能超过URL的长度上限。
某些字符有特殊的含义,如&和等号(=),如果这些字符要作为URL的一部分,就必须先进行编码(调整为另一种表示)。
第二种方法:POST方法。 POST方法使用HTTP消息体来发送参数消息。
优点:所发参数值的长度不再受限。
参数不作为可见的URL的一部分发送,因此不太容易受到恶意用户的攻击。
GET要用于发送可以任意重发而不会对系统带来改变的数据。
POST动作可以用于发送可能带来改变的数据。

JSTL(JSP标准标记库)是一组专门设计的标准标记,用于在JSP中完成一些最常用的Web应用编程任务。这组标记涉及条件流程控制、循环、数据输出、国际化以及XML文档和数据库的使用。

EL表达式形式如下:
${......表达式......}
EL表达式总是在运行时计算,这说明它们在JSP实际执行时才运行(处理一个收到的具体请求时),而不是在JSP容器处理JSP时运行。

Core Java2 6th 摘要(3)

继承在Java和C++中很相似.Java使用extends关键字代替了C++中的":"符号.Java中的任何继承都是公有继承;而没有和C++中的私有和受保护继承相似的概念.

有人认为super和this引用类似.这种类比是不准确的:super不是一个对对象的引用(比如你不能把值super赋给另一个对象变量),而是指示编译器调用超类方法的专用关键字.
Java中使用关键字super调用超类中方法.在C++中,相应的调用规则是在超类名字后加上"::"运算符.
this关键字有两个意思:一是表示对隐式参数的引用;一是调用同一类的其他构造器.同样super关键字也有两个意思:一是调用超类方法;一是调用超类构造器.当被用来调用构造器时,这两个关键字十分类似.在一个构造器中,对其他构造器的调用只能出现在第一行语句中.构造器参数既可以传递给同一个类的其他构造器(使用this),也可以传递给超类的构造器(使用super).

如果之类构造器没有显示调用超类构造器,那么超类会使用默认(无参数)构造器.如果超类没有默认构造器,而且之类构造器又没有调用其他超类构造器,那么Java编译器会报告错误.

Core Java2 6th 摘要(2)

Java中的构造器其工作方式和C++中的不一样,不过,要记住,所有的Java对象都是在堆中被构造的,而且构造器必须和new一起使用。
Employee number007("adai"); // 适用于C++,不适用于Java

注意不要返回指向可变对象的引用的访问方法。
如果需要返回一个指向可变对象的引用,我们首先需要克隆(clone)它。一个克隆是一个对象放在新位置的精确拷贝。

静态方法是不向对象施加操作的方法,所以不能用一个静态方法来访问实例字段,但是静态方法可以访问自身类中的静态字段。
public static int getID() {......}
对静态方法的调用需要使用类的名字。
用static定义的变量、方法属于类,而不属于此类的特定对象。

只有在类中没有其他构造器时,系统才会提供默认构造器。如果你编写的类中有一个自己编写的构造器,而且又希望使用你的类的用户通过调用new ClassName()来创建类的实例,那你就必须提供一个默认构造器(没有参数)。当然,如果你喜欢把所有字段设置为默认值的话,你只用简单的提供 public ClassName() {}

构造器执行之前,赋值会先被执行。

构造器调用其他构造器:
如果构造器的第一个语句具有形式this(...),那么这个构造器将调用同类中的其他构造器。

如果需要在资源使用完毕后立即关闭资源,那就需要对它进行手工管理。对需要清除的资源使用dispose方法。重要的是,如果你使用的类含有 dispose方法,你需要在对象操作完后调用这个方法。尤其是,如果类的某个实例字段含有dispose方法,要提供一个释放此实例字段的 dispose方法。

只能使用*符合引入一个包。不能使用import java.*或是import java.*.*来引入所有以java为前缀的包。
只能引入类,不能引入对象。

类设计的技巧:
1. 一定要让数据私有。
2. 一定要初始化数据。
3. 不要在类中过多使用基本类型。
4. 并非所有字段都需要独自的字段访问方法和更改方法。
5. 为类定义使用标准格式。
6. 分解职责太多的类。
7. 让类和方法的名字反映它们的职责。

Core Java2 6th 摘要(1)

布尔类型:其值只能是false或true(而不是0或1)
Java中布尔值和整数不能相互转换

Java在变量命名中区分大小写。如Box和box是两个不同的名字

Java中使用final来表示常量。
一个常量在某个类的多个方法中都是可用的,这种常量通常称作类常量(class constants)

位操作符: &(与) |(或) ^(异或) ~(非)
<<(左移位)比如 3 << 1 表示11(二进制)左移1位, 变为110(二进制)
>>(右移位)

要测试两个字符串是否相等,可以使用equals方法。如果字符串s和t相同,那么表达式s.equals(t)将返回true,否则返回false。
不要使用==操作符来检测两个字符相等与否!它只能判断两个字符串是否存储在同一位置。

带标签的break语句。标签必须在你要跳出的最外层循环的前面,并且标签后必须有一个冒号。
continue语句把控制转移到它所在的最内层循环的开始。

与C++不同,Java中不能重载操作符。

拷贝数组:可以把一个数组变量拷贝给另一个,这时两个变量都指向相同的数组。 如:
int[] a = b;
a[5] = 12; // 现在b[5]的值也为12

Java中可以创建不同行具有不同长度的不规则数组(ragged array) 二维如:
int[][] adds = new int[NMAX+1][];
for(n = 0; n <= NMAX; n++)
adds[n] = new int[n+1];

JSP基础1:创建动态页面实现数据表示(1)

构成一个JSP页面的可见元素可以包含下面这些:
指令元素 directive element
模板元素 template data
动作 action
脚本元素 scripting element

指令并不直接用来生成输出,相反,指令要用于控制JSP页面的某些特征。可以使用指令向JSP容器做一些特殊的指示,告诉JSP容器在翻译页面时要做些什么。
<%@ 指令 %>
page指令
taglib指令
include指令

page指令 定义JSP文件中的全局属性。
<%@page %>指令用于整个JSP页面,同样包括静态的包含文件。但是page指令不能作用于动态的包含文件,比如
可以在一个页面中用上多个<%page %>指令,但是其中的属性只能用一次。不过也有个例外,那就是import属性。
无论page指令放在JSP文件的哪个地方,它的作用范围都是整个JSP页面。

taglib指令 定义一个标签库以及其自定义标签的前缀。
语法:<%@taglib uri = "URIToTagLibrary" prefix = "tagPrefix"%>
<@taglib >指令声明此JSP使用了自定义标签,同时引用标签库,也指定了他们的标签的前缀。
这里自定义的标签含有标签和元素之分。
标签只不过是一个在意义上被抬高了点的标记。是JSP元素的一部分。JSP元素是JSP语法的一部分,和XML一样有开始标记和结束标记。元素也可以包含其他的文本、标记、元素。

指令(例如一个taglib指令)在JSP中通常以如下形式出现:
<%@ taglib....%>
这个元素也可以如下形式出现在JSP中:
这是一种表示JSP指令的XML兼容语法

模板数据就是静态文本。静态会直接通过JSP容器传递,而不做任何处理。
模板文本用于指定页面的静态部分,其他JSP元素则用于生成页面的动态部分。

动作(action)元素是在请求处理中直接涉及的JSP元素
利用动作元素,可以在生成动态输出时访问数据以及管理或转换数据。

动作元素可以是标准(standard)动作也可以是定制(custom)动作。标准动作在每一个遵循JSP2.0标准的JSP容器肯定都可以用。例如都是标准动作。
定制动作是使用JSP标记扩展机制创建的动作。这种机制运行开发人员自己创建的一组动作来管理数据或生成JSP页面中的动态输出。