关于作者

用户名:myvalley
笔名:缘客石
地区: 黑龙江-哈尔滨
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



友情blog

本站公告

本站公告


欢迎大家的光临!!!
本站域名:

http://myvalley.bokee.com
http://myvalley.blogchina.com
免责声明

本站资料大部分来自于互联网,其版权归原作者或其他合法者所有.如内容涉及或侵犯了您的权益,请通知本人,我将尽快处理!

欢迎您的光临

感谢您的大力支持!如果您对本站有好的意见和建议,请到留言板留言或用以下方式和我联系:
myvalley@bokee.com

访问统计:
文章个数:162
评论个数:401
留言条数:61




Powered by BlogDriver 2.1

青山翠谷

 

         

文章

AOP是什么?

AOP是什么?

为什么要区分J2EE容器和J2EE应用系统?

  我们知道,J2EE应用系统只有部署在J2EE容器中才能运行,那么为什么划分为J2EE容器和J2EE应用系统? 通过对J2EE容器运行机制的分析(见我的电子教材“EJB实用原理”),我们可以发现:实际上J2EE容器分离了一般应用系统的一些通用功能,例如事务机制、安全机制以及对象池或线程池等性能优化机制。

  这些功能机制是每个应用系统几乎都需要的,因此可以从具体应用系统中分离出来,形成一个通用的框架平台,而且,这些功能机制的设计开发有一定难度,同时运行的稳定性和快速性都非常重要,必须经过长时间调试和运行经验积累而成,因此,形成了专门的J2EE容器服务器产品,如Tomcat JBoss、Websphere、WebLogic等。

  从J2EE系统划分为J2EE容器和J2EE应用系统两个方面,我们已经看到一种分散关注的思路(separation of concerns)。

分散关注

  将通用需求功能从不相关类之中分离出来;同时,能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以。

   AOP就是这种实现分散关注的编程方法,它将“关注”封装在“方面”中。

AOP是什么?

  AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

  举例:假设有在一个应用系统中,有一个共享的数据必须被并发同时访问,首先,将这个数据封装在数据对象中,称为Data Class,同时,将有多个访问类,专门用于在同一时刻访问这同一个数据对象。

  为了完成上述并发访问同一资源的功能,需要引入锁Lock的概念,也就是说,某个时刻,当有一个访问类访问这个数据对象时,这个数据对象必须上锁Locked,用完后就立即解锁unLocked,再供其它访问类访问。

  使用传统的编程习惯,我们会创建一个抽象类,所有的访问类继承这个抽象父类,如下:

abstract class Worker{

  abstract void locked();
  abstract void accessDataObject();
  abstract void unlocked();

}


  缺点:
  • accessDataObject()方法需要有“锁”状态之类的相关代码。
  • Java只提供了单继承,因此具体访问类只能继承这个父类,如果具体访问类还要继承其它父类,比如另外一个如Worker的父类,将无法方便实现。
  • 重用被打折扣,具体访问类因为也包含“锁”状态之类的相关代码,只能被重用在相关有“锁”的场合,重用范围很窄。

    仔细研究这个应用的“锁”,它其实有下列特性:
  • “锁”功能不是具体访问类的首要或主要功能,访问类主要功能是访问数据对象,例如读取数据或更改动作。
  • “锁”行为其实是和具体访问类的主要功能可以独立、区分开来的。
  • “锁”功能其实是这个系统的一个纵向切面,涉及许多类、许多类的方法。如下图:

  因此,一个新的程序结构应该是关注系统的纵向切面,例如这个应用的“锁”功能,这个新的程序结构就是aspect(方面)

  在这个应用中,“锁”方面(aspect)应该有以下职责:

  提供一些必备的功能,对被访问对象实现加锁或解锁功能。以保证所有在修改数据对象的操作之前能够调用lock()加锁,在它使用完成后,调用unlock()解锁。

AOP应用范围

  很明显,AOP非常适合开发J2EE容器服务器,目前JBoss 4.0正是使用AOP框架进行开发。
  具体功能如下:
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

AOP有必要吗?

  当然,上述应用范例在没有使用AOP情况下,也得到了解决,例如JBoss 3.XXX也提供了上述应用功能,但是没有使用AOP。

  但是,使用AOP可以让我们从一个更高的抽象概念来理解软件系统,AOP也许提供一种有价值的工具。可以这么说:因为使用AOP结构,现在JBoss 4.0的源码要比JBoss 3.X容易理解多了,这对于一个大型复杂系统来说是非常重要的。

  从另外一个方面说,好像不是所有的人都需要关心AOP,它可能是一种架构设计的选择,如果选择J2EE系统,AOP关注的上述通用方面都已经被J2EE容器实现了,J2EE应系统开发者可能需要更多地关注行业应用方面aspect。


AOP具体实现

  AOP是一个概念,并没有设定具体语言的实现,它能克服那些只有单继承特性语言的缺点(如Java),目前AOP具体实现有以下几个项目:

  AspectJ (TM): 创建于Xerox PARC. 有近十年历史,成熟
  缺点:过于复杂;破坏封装;需要专门的Java编译器。

  动态AOP:使用JDK的动态代理API或字节码Bytecode处理技术。

  基于动态代理API的具体项目有:
  JBoss 4.0 JBoss 4.0服务器
  nanning 这是以中国南宁命名的一个项目,搞不清楚为什么和中国相关?是中国人发起的?

  基于字节码的项目有:
  aspectwerkz 
  spring ?

在以后其它文章中,我将继续对AOP概念进行分析,和大家一起学习进步

- 作者: 缘客石 2006年03月14日, 星期二 17:09  回复(2) |  引用(0) 加入博采

j2EE基础概念
j2EE基础概念
Java基础方面:

1,作用域public,protected,private,以及不写时的区别

2,ArrayList和Vector的区别,HashMap和Hashtable的区别

3,char型变量能不能定义为一个中文?为什么?

4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么?

5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?

6,内部类的实现方式?

7,垃圾回收机制,如何优化程序?

8,float型float f=3.4是否正确?

Jsp方面

1,jsp有哪些内置对象?作用分别是什么?

2,jsp有哪些动作?作用分别是什么?

3,include的两种实现方式的区别?

4,两种跳转方式分别是什么?有什么区别?

Servlet方面

1,说一说Servlet的生命周期?

2,Servlet版本间(忘了问的是哪两个版本了)的不同?

Jdbc,Jdo方面

1,可能会让你写一段Jdbc连Oracle的程序.

2,Class.forName的作用?为什么要用?

3,Jdo是什么?

Xml方面

1,xml有哪些解析技术?区别是什么?

2,你在项目中用到了xml技术的哪些方面?如何实现的?

3,用jdom解析xml文件时如何解决中文问题?如何解析?

EJB方面

1,EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别?

MVC方面

1,MVC的各个部分都有那些技术来实现?如何实现?

设计模式方面:

1,开发中都用到了那些设计模式?用在什么场合?

JavaScript方面

1,如何校验数字型?

CORBA

1,CORBA是什么?用途是什么?


谁来做出解答阿!
-------------------------------------------------------------
回答一部分。
1,作用域public,protected,private,以及不写时的区别
public 在其他的包中的类也可以引用,protected只限于同一个包内的类,private只有自己可以使用。不写的时候和protected一样。
2,ArrayList和Vector的区别,HashMap和Hashtable的区别
ArrayList需要预先定义大小,Vector不用。HashMap和Hashtable的默认初始化容量(default initial capacity)不同 HashMap是16,Hashtable为11。
3,char型变量能不能定义为一个中文?为什么?
可以定义。因为中文也是16bit的。
4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么?
查看jdk文档。
5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
这个具体的去看。
6,内部类的实现方式?
内部类”是在另一个类的内部声明的类。从Java 1.1开始,你可在一个类中声明另一个类,这与声明字段和方法非常相似。
7,垃圾回收机制,如何优化程序?
在变量不使用的时候将其赋值为null。
8,float型float f=3.4是否正确?
不行。类型不匹配。改为float f=3.4f。

Servlet方面

1,说一说Servlet的生命周期?
Servlet的生命周期是当服务器装载运行servlets,接收来自客户端的多个请求并且返回数据给客户端,然后再删除移开servlets的时间。
2,Servlet版本间(忘了问的是哪两个版本了)的不同?
我个人认为这个问题没有什么实际意义。
Jdbc,Jdo方面

1,可能会让你写一段Jdbc连Oracle的程序.
有通式。Connection conn = null;
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@xxx:1521:xxx";
String user = "xxx";
String password = "xxx";
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection(url, user, password);
}
catch (ClassNotFoundException e)
{
System.err.print("ClassNotFoundException: Load jdbc-driver failure!");
System.err.println(e.getMessage());
}
catch (SQLException e)
{
e.printStackTrace();
try
{
if (conn != null)
{
conn.close();
conn = null;
}
}
catch (SQLException se)
{
}
}
2,Class.forName的作用?为什么要用?
返回一个指定名称的class对象。用它是为了load你指定名称的class。
3,Jdo是什么?
全称Java Data Objects。提供了在事务处理数据库中Java对象模型的明显的持久性,直接支持了Java类的实例,应用程序不用处理任何其它的数据模型。

Xml方面

1,xml有哪些解析技术?区别是什么?
1) CSS是Cascading Style Sheet的缩写,即“层叠样式表”,在1997年W3C颁布HTML4标准的同时也公布了有关样式单的第一个标准CSS1。
2)XSL(eXtensible Style Language,可扩展的样式语言)是最强大和灵活的样式语言,是特别为应用XML而设计的,它完全遵循XML规则,进一步完善了XML本身。
3)Data-Island。还有几个,但是主要是第二个,其他的我认为不是主流。
2,你在项目中用到了xml技术的哪些方面?如何实现的?
xslt,fo,html解析,xml数据封装和解析。使用第三方软件包实现。
3,用jdom解析xml文件时如何解决中文问题?如何解析?
没用过,不太清楚。我使用的castor。(jbuilder9中有)

JavaScript方面

1,如何校验数字型?
isNaN(parseFloat(xxx))如果为true,则不是数字。
---
做点力所能及的补充:
(1)Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而
ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比
Vector好。
(2)当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而
ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
(3)Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一
个实现。
(4)性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而
HashMap的不是。
(5)只有HashMap可以让你将空作为一个表的条目的key或value
---
1,xml有哪些解析技术?区别是什么?

有两种:DOM和SAX.

DOM:可以得到一个包含文档中所有元素的树结构.
SAX:在文档的不同治点产生事件,应用程序可以决定如何处理这些事件以从解析器中得到信息.
---
Java基础方面:(未写的是已经有人回答或我自己也未知或不确定)

4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么?
:::
我所知道的有两种实现方法:
一种是将方法同步:public synchronized methodName(...){....}
另一种是将对象同步(此对象不能是null值): synchronized (object) { ..... }

5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
继承时候类的执行顺序是:
父类中被static关键字定义的部分是按所定义的顺序而最先被初始化的;
父类构造函数(按调用顺序);

子类中被static关键字定义的部分是按所定义的顺序而最先被初始化的;
子类构造函数(按调用顺序);
其他则按方法的调用顺序.

6,内部类的实现方式?
public class XXXXX{ // 只有一个public类

...

class YYYYY{ // 可以有的访问类型是[ protected | private | 无 ]
...
}

}

8,float型float f=3.4是否正确?
我也不知道是否正确.
我一般是这样:float f=3.4F; 或 float f=3.4f;// 后面跟一个英文大写或小写字母F
---
JavaScript方面

1,如何校验数字型?
var v = document.all.Telephone.value;
if(isNan(v)){
alert("全是数字");
}
---
yangtaylor的解释有一半以上是错误的,大家不要作标准答案背哦
noisysilence的解释很不错,说明你已经是高手了,不过说明一点,Vector的精妙设置是在于第二个参数的设置,其余的都基本正确。

关于“作用域public,protected,private,以及不写时的区别”的说明---
前三个常用,就不用说了,关于不写时的情况我来说明一下:
JAVA的作用域其实有5种,除了上面的3种外还有:private protected,default
public--------------不说了
protected-----------除了所有的子类可访问外,同包的非子类也可以访问
private-------------也不说了
private protected---只有子类可以访问(这才是我们理解意义上的protected)
default-------------只有同包的类可以访问,即使是子类但不同包仍不能访问

我也是看了很多参考书才最终得到的结果,在此献给大家了。
---
ArrayList和Vector的区别上面已说,我说说它们会带来得影响吧。
同为对象集合,ArrayList可由编译器检查而Vector则不会,所以如果用Vector返回对象集合,编译器是无法查错得,只有在运行时才能才能发现。例如:
********采用Vector*************
server side:
public Vector getCustomDataSet(int num)
{
Vector v = new Vector();
for(int i=0; i<num; i++)
{
CustomData customData = new CustomData(i);
v.add(customData) //此处加入自定义的数据结构CustomData
}
return v;
}
client side:

Vector v = getCustomDataSet(5);
for(int i=0; i<v.size(); i++)
{
OtherData data = (OtherData)v.get(i);//此处取出的是customData却转换为OtherData,编译器通过
}

********采用ArrayList*************
server side:
public CustomData[] getCustomDataSet(int num)
{
CustomData[] customData = new CustomData[5];
for(int i=0; i<num; i++)
{
customData[i] = new CustomData(i);
}
return customData;
}
client side:

CustomData[] datas = getCustomDataSet(5);
for(int i=0; i<datas.length; i++)
{
OtherData data = datas[i];//编译不通过,类型不匹配
}

---
下边是以前我找的关于Hashtable和HashMap的不同之处。


Hashtable和HashMap
Hashtable和HashMap类有三个重要的不同之处。第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。
也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多处理费用。
第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key,但任意数量的条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回null。如果有必要,用containKey()方法来区别这两种情况。
一些资料建议,当需要同步时,用Hashtable,反之用HashMap。但是,因为在需要时,HashMap可以被同步,HashMap的功能比Hashtable的功能更多,而且它不是基于一个陈旧的类的,所以有人认为,在各种情况下,HashMap都优先于Hashtable。
---
XML 的编程接口: DOM SAX JDOM JAXP

文档对象模型(通常称为 DOM)为 XML 文档的已解析版本定义了一组接口。解析器读入整个文档,然后构建一个驻留内存的树结构,然后您的代码就可以使用 DOM 接口来操作这个树结构。您可以遍历树以了解原始文档包含了什么,您可以删除树的几个部分,还可以重新排列树和添加新的分支
DOM 提供了一组丰富的功能,您可以用这些功能来解释和操作 XML 文档,但使用它们是有代价的。
DOM 构建整个文档驻留内存的树。如果文档很大,就会要求有极大的内存。
DOM 创建表示原始文档中每个东西的对象,包括元素、文本、属性和空格。如果您只需关注原始文档的一小部分,那么创建那些永远不被使用的对象是极其浪费的。
DOM 解析器必须在您的代码取得控制权之前读取整个文档。对于非常大的文档,这会引起显著的延迟


为了解决 DOM 问题,XML-DEV 参与者们(由 David Megginson 领导)创建了 SAX 接口。SAX 的几个特征解决了 DOM 的问题:

SAX 解析器向您的代码发送事件。当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,它会告诉您。您可以决定什么事件对您重要,而且可以决定要创建什么类型的数据结构以保存来自这些事件的数据。如果您没有显式地保存来自某个事件的数据,它就被丢弃。
SAX 解析器根本不创建任何对象,它只是将事件传递给您的应用程序。如果希望基于那些事件创建对象,这将由您来完成。
SAX 解析器在解析开始的时候就开始发送事件。当解析器发现文档开始、元素开始和文本等时,代码会收到一个事件。您的应用程序可以立即开始生成结果;您不必一直等到整个文档被解析完毕。更妙的是,如果您只查找文档中某些内容,代码一旦找到所要找的东西就可以抛出一个异常。该异常会停止 SAX 解析器,然后代码用它找到的数据做它需要做的任何事。
SAX 解析器也有些问题引人关注:

SAX 事件是无状态的。当 SAX 解析器在 XML 文档中发现文本时,它就向您的代码发送一个事件。该事件仅仅给您发现的文本;它不告诉您什么元素包含那个文本。如果您想知道这一点,则必须自己编写状态管理代码。
SAX 事件不是持久的。如果应用程序需要一个数据结构来对 XML 文档建模,则必须自己编写那样的代码。如果您需要从 SAX 事件访问数据,并且没有把那个数据存储在代码中,那么您不得不再次解析该文档。

JDOM 是基于 Java 技术的开放源码项目,它试图遵循 80/20 规则:用 DOM 和 SAX 20% 的功能来满足 80% 的用户需求。JDOM 使用 SAX 和 DOM 解析器,因此它是作为一组相对较小的 Java 类被实现的。

JDOM 的主要特性是它极大地减少了您必须编写的代码数量。尽管本篇介绍性教程并不深入讨论编程主题,但 JDOM 应用程序的长度通常是 DOM 应用程序的三分之一,大约是 SAX 应用程序的一半。(当然,坚持使用 DOM 的纯粹主义者会建议说:从长远来看,学习和使用 DOM 终会有所回报)。JDOM 并不做所有的事,但对于大多数您要做的解析,它可能正好适合您。

尽管 DOM、SAX 和 JDOM 为大多数常见任务提供了标准接口,但仍有些事情是它们不能解决的。例如,在 Java 程序中创建 DOMParser 对象的过程因 DOM 解析器的不同而不同。为了修正这个问题,Sun 发布了 JAXP(用于 XML 解析的 Java API,Java API for XML Parsing)。该 API 为使用 DOM、SAX 和 XSLT 处理 XML 文档提供了公共接口。

JAXP 提供的诸如 DocumentBuilderFactory 和 DocumentBuilder 之类的接口为不同的解析器提供了一个标准接口。还有一些方法可以允许您控制底层的解析器是否可以识别名称空间以及是否使用 DTD 或模式来验证 XML 文档。
为了确定哪种接口适合您,您需要理解所有接口的设计要点,而且需要理解应用程序用您将要处理的 XML 文档来做什么。考虑下面的问题将有助于您找到正确的方法。

要用 Java 编写应用程序吗?JAXP 使用 DOM、SAX 和 JDOM;如果您用 Java 编写代码,那么您应使用 JAXP 将您的代码与各种解析器实现的细节隔离。
应用程序将如何部署?如果您的应用程序将要作为 Java applet 部署,那么您会希望使要载的代码数量最小,别忘了 SAX 解析器比 DOM 解析器小。还要知道使用 JDOM 时,除了 SAX 或 DOM 解析器之外还要求编写少量的代码。
一旦解析了 XML 文档,还需要多次访问那些数据吗?如果您需要回过头来访问 XML 文件的已解析版本,DOM 可能是正确的选择。而 SAX 事件被触发时,如果您以后需要它,则由您(开发人员)自己决定以某种方式保存它。如果您需要访问不曾保存的事件,则必须再次解析该文件。而 DOM 自动保存所有的数据。
只需要 XML 源文件的少量内容吗?如果您只需要 XML 源文件的少量内容,那么 SAX 可能是正确的选择。SAX 不会为源文件中的每个东西创建对象;您要确定什么是重要的。使用 SAX,您要检查每个事件以了解它是否与您的需要有关,然后相应地处理它。更妙的是,一旦找到您正在寻找的东西,您的代码就会抛出一个异常来完全停止 SAX 解析器。
您正在一台内存很少的机器上工作吗?若是的话,不管您可能考虑到的其它因素是什么,SAX 是您的最佳选择。

- 作者: 缘客石 2006年03月14日, 星期二 17:05  回复(2) |  引用(0) 加入博采

Java虚拟机(JVM)对象创建浅析
 
Java虚拟机(JVM)对象创建浅析
你认为java虚拟机什么时候会创建一个对象?如果能回答出下面的问题,你就可以不用继续向下看了。
  
  class A{
  static final int a = 10;
  
  static{
  System.out.println( "initializing A" );
  }
  
  }
  
  class B{
  public static void main(String[] args){
  System.out.println(A.a);
  }
  
  static{
  System.out.println( "initializing B" );
  }
  }
  
  我们调用B的main函数,会得到什么输出?
  
  经过考虑后,你是不是认为应该得到:
  
  "initializing B"
  "initializing A"
  10
  
  这个结果?不对,其实结果是:
  
  "initializing B"
  10
  
  嗯?A没有被实例化?
  
  其实,java虚拟机会在6种情况下创建一个对象,分别是:
  
  ·创建类的新实例
  
  ·调用类中声明的静态方法
  
  ·操作类或接口中声明的非常量静态字段
  
  ·调用Java API中特定的反射方法
  
  ·初始化一个类的子类
  
  ·制定一个类作为Java虚拟机启动时的初始化类
  
  java虚拟机只有在第一次主动使用一个类的情况下才会初始化该类。在以上6种情况下,虚拟机认为正在主动使用该类,所以会对其进行初始化,记住这6条,SCJP可能会出现(只是如果,可能考试不会涉及到虚拟机一层)
  
  顺便说一下,任何一个类的初始化都要求它的所有祖先类(非祖先接口)预先被初始化,而接口的初始化则不需要祖先接口预先被初始化。

- 作者: 缘客石 2006年03月14日, 星期二 17:03  回复(1) |  引用(0) 加入博采

Java中Static、this、super、final用法
Java中Static、this、super、final用法
一、Static

请先看下面这段程序:

TER> public class Hello
{
    public static void main(String[] args)
 {
 //(1)
      System.out.println("Hello,world!");
   //(2)
    }
  }
 
看过这段程序,对于大多数学过Java的从来说,都不陌生。即使没有学过Java,而学过其它的高级语言,例如C,那你也应该能看懂这段代码的意思。它只是简单的输出“Hello,world”,一点别的用处都没有,然而,它却展示了static关键字的主要用法。

在1处,我们定义了一个静态的方法名为main,这就意味着告诉Java编译器,我这个方法不需要创建一个此类的对象即可使用。你还得你是怎么运行这个程序吗?一般,我们都是在命令行下,打入如下的命令(加下划线为手动输入):

javac Hello.java
java Hello
Hello,world!
 
这就是你运行的过程,第一行用来编译Hello.java这个文件,执行完后,如果你查看当前,会发现多了一个Hello.class文件,那就是第一行产生的Java二进制字节码。第二行就是执行一个Java程序的最普遍做法。

执行结果如你所料。在2中,你可能会想,为什么要这样才能输出。好,我们来分解一下这条语句。(如果没有安装Java文档,请到Sun的官方网站浏览J2SE API)首先,System是位于java.lang包中的一个核心类,如果你查看它的定义,你会发现有这样一行:

public static final PrintStream out;
 
接着在进一步,点击PrintStream这个超链接,在METHOD页面,你会看到大量定义的方法,查找println,会有这样一行:

public void println(String x)
 
好了,现在你应该明白为什么我们要那样调用了,out是System的一个静态变量,所以可以直接使用,而out所属的类有一个println方法。

静态方法

通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法。如下所示:

class Simple
{
   static void go()
   {
     System.out.println("Go...");
   }
}
public class Cal
{
  public static void main(String[] args)
  {
    Simple.go();
  }
}
 
调用一个静态方法就是“类名.方法名”,静态法的使用很简单如上所示。一般来说,静态方法常常为应用程序中的其它类提供一些实用工具所用,在Java的类库中大量的静态方法正是出于此目的而定义的。

静态变量

静态变量与静态方法类似。所有此类实例共享此静态变量,也就是说在类装载时,只分配一块存储空间,所有此类的对象都可以操控此块存储空间,当然对于final则另当别论了。看下面这段代码:

class Value
{
  static int c=0;
  static void inc()
  {
    c++;
  }
}
class Count
{
  public static void prt(String s)
  {
    System.out.println(s);
  }
  public static void main(String[] args)
  {
    Value v1,v2;
    v1=new Value();
    v2=new Value();
    prt("v1.c="+v1.c+"  v2.c="+v2.c);
    v1.inc();
    prt("v1.c="+v1.c+"  v2.c="+v2.c); 
  }
}
 
结果如下:

v1.c=0  v2.c=0
v1.c=1  v2.c=1
 
由此可以证明它们共享一块存储区。static变量有点类似于C中的全局变量的概念。值得探讨的是静态变量的初始化问题。我们修改上面的程序:


class Value{
  static int c=0;
  Value(){
    c=15;
  }
  Value(int i){
    c=i;
  }
  static void inc(){
    c++;
  }
}
class Count{
  public static void prt(String s){
    System.out.println(s);
  }
    Value v=new Value(10);
    static Value v1,v2;
    static{
      prt("v1.c="+v1.c+"
   v2.c="+v2.c);
      v1=new Value(27);
      prt("v1.c="+v1.c+"
   v2.c="+v2.c);
      v2=new Value(15);
      prt("v1.c="+v1.c+"
   v2.c="+v2.c);
    }

  public static void main
  (String[] args)
  {
    Count ct=new Count();
    prt("ct.c="+ct.v.c);
    prt("v1.c="+v1.c+"  v2.c="+v2.c);
    v1.inc();
    prt("v1.c="+v1.c+"  v2.c="+v2.c);
    prt("ct.c="+ct.v.c);
  }
}
 
运行结果如下:

v1.c=0  v2.c=0
v1.c=27  v2.c=27
v1.c=15  v2.c=15
ct.c=10
v1.c=10  v2.c=10
v1.c=11  v2.c=11
ct.c=11
 
这个程序展示了静态初始化的各种特性。如果你初次接触Java,结果可能令你吃惊。可能会对static后加大括号感到困惑。首先要告诉你的是,static定义的变量会优先于任何其它非static变量,不论其出现的顺序如何。

正如在程序中所表现的,虽然v出现在v1和v2的前面,但是结果却是v1和v2的初始化在v的前面。在static{后面跟着一段代码,这是用来进行显式的静态变量初始化,这段代码只会初始化一次,且在类被第一次装载时。

如果你能读懂并理解这段代码,会帮助你对static关键字的认识。在涉及到继承的时候,会先初始化父类的static变量,然后是子类的,依次类推。非静态变量不是本文的主题,在此不做详细讨论,请参考Think in Java中的讲解。

静态类

通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。如下代码所示:

public class StaticCls
{
  public static void
  main(String[] args)
  {
    OuterCls.InnerCls
 oi=new OuterCls.InnerCls();
  }
}
class OuterCls{
  public static class InnerCls{
    InnerCls(){
      System.out.println("InnerCls");
    }
   }
}
 
输出结果会如你所料:

InnerCls
 
和普通类一样。

二、this & super

在上一篇拙作中,我们讨论了static的种种用法,通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是,并不是说有了这种便利,你便可以随处使用,如果那样的话,你便需要认真考虑一下自己是否在用面向对象的思想编程,自己的程序是否是面向对象的。好了,现在开始讨论this&super这两个关键字的意义和用法。

在Java中,this通常指当前对象,super则指父类的。当你想要引用当前对象的某种东西,比如当前对象的某个方法,或当前对象的某个成员,你便可以利用this来实现这个目的,当然,this的另一个用途是调用当前对象的另一个构造函数,这些马上就要讨论。

如果你想引用父类的某种东西,则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系,所以我们在这一块儿来讨论,希望能帮助你区分和掌握它们两个。

在一般方法中

最普遍的情况就是,在你的方法中的某个形参名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便需要明确使用this关键字来指明你要使用某个成员,使用方法是“this.成员名”,而不带this的那个便是形参。

另外,还可以用“this.方法名”来引用当前对象的某个方法,但这时this就不是必须的了,你可以直接用方法名来访问那个方法,编译器会知道你要调用的是那一个。下面的代码演示了上面的用法:

public class DemoThis
{
  private String name;
  private int age;
  DemoThis(String name,int age)
  {
    setName(name);
 //你可以加上this来调用方法,
 像这样:this.setName(name);
 但这并不是必须的
    setAge(age);
    this.print();
  }  
  public void setName(String name)
  {
    this.name=name;
 //此处必须指明你要引用成员变量
  }
  public void setAge(int age)
  {
    this.age=age;
  }
  public void print()
  {
    System.out.println
 ("Name="+name+" Age="+age);
 //在此行中并不需要用this,
 因为没有会导致混淆的东西
  }
  public static void main(String[] args)
  {
    DemoThis dt=new DemoThis("Kevin","22");
  }
}
 
这段代码很简单,不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它,两者效果一样。下面我们修改这个程序,来演示super的用法。

class Person
{
  public int c;
  private String name;
  private int age;
  protected void setName
  (String name)
  {
    this.name=name;
  }
  protected void setAge(int age)
  {
    this.age=age;
  }
  protected void print()
  {
    System.out.println
 ("Name="+name+" Age="+age);
  }
}
public class DemoSuper
extends Person
{
  public void print()
  {
    System.out.println("DemoSuper:");
    super.print();
  }
  public static void main(String[] args)
  {
    DemoSuper ds=new DemoSuper();
    ds.setName("kevin");
    ds.setAge(22);
    ds.print();
  }
}
 
在DemoSuper中,重新定义的print方法覆写了父类的print方法,它首先做一些自己的事情,然后调用父类的那个被覆写了的方法。输出结果说明了这一点:

DemoSuper:
Name=kevin Age=22
 
这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问,那你可以像使用this一样使用它,用“super.父类中的成员名”的方式,但常常你并不是这样来访问父类中的成员名的。

在构造函数中

构造函数是一种特殊的方法,在对象初始化的时候自动调用。在构造函数中,this和super也有上面说的种种使用方式,且它还有特殊的地方,请看下面的例子:

class Person
{
  public static void prt(String s)
  {
    System.out.println(s);
  }
  Person(){
    prt("A Person.");
  }
  Person(String name)
  {
    prt("A person name is:"+name);
  }
}
public class Chinese extends Person{
  Chinese(){
    super(); 
 //调用父类构造函数(1)
    prt("A chinese.");
 //(4)
  }
  Chinese(String name)
  {
    super(name);
 //调用父类具有相同形参的构造函数(2)
    prt("his name is:"+name);
  }
  Chinese(String name,int age){
    this(name);
 //调用当前具有相同形参的构造函数(3)
    prt("his age is:"+age);
  }
  public static void main
  (String[] args)
  {
    Chinese cn=new Chinese();
    cn=new Chinese("kevin");
    cn=new Chinese("kevin",22);
  }
}
 
在这段程序中,this和super不再是像以前那样用“.”连接一个方法或成员,而是直接在其后跟上适当的参数,因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的构造函数,如1和2处。this后加参数则调用的是当前具有相同参数的构造函数,如3处。

当然,在Chinese的各个重载构造函数中,this和super在一般方法中的各种用法也仍可使用,比如4处,你可以将它替换为“this.prt”(因为它继承了父类中的那个方法)或者是“super.prt”(因为它是父类中的方法且可被子类访问),它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。

最后,写了这么多,如果你能对“this通常指代当前对象,super通常指代父类”这句话牢记在心,那么本篇便达到了目的,其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承,请参阅相关Java教程。
三、final

final在Java中并不常用,然而它却为我们提供了诸如在C语言中定义常量的功能,不仅如此,final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。

final成员

当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。

其初始化可以在两个地方,一是其定义处,也就是说在final变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一,要么在定义时给值,要么在构造函数中给值,不能同时既在定义时给了值,又在构造函数中给另外的值。下面这段代码演示了这一点:

import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
public class Bat
{
    final PI=3.14;
 //在定义时便给址值
    final int i;
 //因为要在构造函数中进行初始化,
 所以此处便不可再给值
    final List list;
 //此变量也与上面的一样
    Bat(){
        i=100;
        list=new LinkedList();
    }
    Bat(int ii,List l){
        i=ii;
        list=l;
    }
    public static void
 main(String[] args)
 {
        Bat b=new Bat();
        b.list.add(new Bat());
        //b.i=25;
        //b.list=new ArrayList();
        System.out.println("I="+b.i+"
  List Type:"+b.list.getClass());
        b=new Bat(23,new ArrayList());
        b.list.add(new Bat());
        System.out.println("I="+b.i+"
  List Type:"+b.list.getClass());
    }
}
 
此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法,这使你有了一点灵活性。如Bat的两个重载构造函数所示,第一个缺省构造函数会为你提供默认的值,重载的那个构造函数会根据你所提供的值或类型为final变量初始化。

然而有时你并不需要这种灵活性,你只需要在定义时便给定其值并永不变化,这时就不要再用这种方法。

在main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型,输出结果中显示了这一点:

I=100 List Type:class
java.util.LinkedList
I=23 List Type:class
java.util.ArrayList
 
还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句。

然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。

另外方法中的内部类在用到方法中的参变量时,此参变也必须声明为final才可使用,如下代码所示:

public class INClass
{
   void innerClass
   (final String str)
   {
        class IClass
  {
            IClass()
   {
                System.out.println(str);
            }
        }
        IClass ic=new IClass();
    }
  public static void main
  (String[] args)
  {
      INClass inc=new INClass();
      inc.innerClass("Hello");
  }
}
 
final方法

将方法声明为final,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。

另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。

final类

当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。

对于final类中的成员,你可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。

下面的程序演示了final方法和final类的用法:

final class final
{
    final String str="final Data";
    public String str1="non final data";
    final public void print()
 {
        System.out.println
  ("final method.");
    }
    public void what()
 {
        System.out.println(str+"\n"+str1);
    }
}
public class FinalDemo

//extends final 无法继承
    public static void
 main(String[] args)
 {
        final f=new final();
 &bsp;      f.what();
        f.print();
    }
}
 
从程序中可以看出,final类与普通类的使用几乎没有差别,只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出,只是记住慎用。

final在设计模式中的应用

在设计模式中有一种模式叫做不变模式,在Java中通过final关键字可以很容易的实现这个模式,在讲解final成员时用到的程序Bat.java就是一个不变模式的例子。

到此为止,this,static,supert和final的使用已经说完了,如果你对这四个关键字已经能够大致说出它们的区别与用法,那便说明你基本已经掌握。

然而,世界上的任何东西都不是完美无缺的,Java提供这四个关键字,给程序员的编程带来了很大的便利,但并不是说要让你到处使用,一旦达到滥用的程序,便适得其反,所以在使用时请一定要认真考虑。

- 作者: 缘客石 2006年03月14日, 星期二 16:58  回复(1) |  引用(0) 加入博采

JVM的垃圾回收机制详解和调优

1.JVM的gc概述

  gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。

  在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和jvm支持的垃圾收集算法,便可以进行优化配置垃圾收集器。

  垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集。两种常用的方法是引用计数和对象引用遍历。

  1.1.引用计数

  引用计数存储对特定对象的所有引用数,也就是说,当应用程序创建引用以及引用超出范围时,jvm必须适当增减引用数。当某对象的引用数为0时,便可以进行垃圾收集。

  1.2.对象引用遍历

  早期的jvm使用引用计数,现在大多数jvm采用对象引用遍历。对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,gc必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。

  下一步,gc要删除不可到达的对象。删除时,有些gc只是简单的扫描堆栈,删除未标记的未标记的对象,并释放它们的内存以生成新的对象,这叫做清除(sweeping)。这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。因此,许多gc可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。

  为此,gc需要停止其他的活动活动。这种方法意味着所有与应用程序相关的工作停止,只有gc运行。结果,在响应期间增减了许多混杂请求。另外,更复杂的gc不断增加或同时运行以减少或者清除应用程序的中断。有的gc使用单线程完成这项工作,有的则采用多线程以增加效率。

  2.几种垃圾回收机制

  2.1.标记-清除收集器

  这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。

  2.2.标记-压缩收集器

  有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。

  2.3.复制收集器

  这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,jvm生成的新对象则放在另一半空间中。gc运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。

  2.4.增量收集器

  增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。

  2.5.分代收集器

  这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。jvm生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。

  2.6.并发收集器

  并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。

  2.7.并行收集器

  并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多cpu机器上使用多线程技术可以显著的提高java应用程序的可扩展性。
  3.Sun HotSpot 1.4.1 JVM堆大小的调整

  Sun HotSpot 1.4.1使用分代收集器,它把堆分为三个主要的域:新域、旧域以及永久域。Jvm生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域。在永久域中jvm则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。

  下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。

  下面的命令是把初始大小设置为128M:

  java –Xms128m

     –Xmx256m为控制新域的大小,可使用-XX:NewRatio设置新域在堆中所占的比例。

  下面的命令把整个堆设置成128m,新域比率设置成3,即新域与旧域比例为1:3,新域为堆的1/4或32M:

java –Xms128m –Xmx128m
–XX:NewRatio =3可使用-XX:NewSize和-XX:MaxNewsize设置新域的初始值和最大值。

  下面的命令把新域的初始值和最大值设置成64m:

java –Xms256m –Xmx256m –Xmn64m

  永久域默认大小为4m。运行程序时,jvm会调整永久域的大小以满足需要。每次调整时,jvm会对堆进行一次完全的垃圾收集。

  使用-XX:MaxPerSize标志来增加永久域搭大小。在WebLogic Server应用程序加载较多类时,经常需要增加永久域的最大值。当jvm加载类时,永久域中的对象急剧增加,从而使jvm不断调整永久域大小。为了避免调整,可使用-XX:PerSize标志设置初始值。

  下面把永久域初始值设置成32m,最大值设置成64m。

java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m

  默认状态下,HotSpot在新域中使用复制收集器。该域一般分为三个部分。第一部分为Eden,用于生成新的对象。另两部分称为救助空间,当Eden充满时,收集器停止应用程序,把所有可到达对象复制到当前的from救助空间,一旦当前的from救助空间充满,收集器则把可到达对象复制到当前的to救助空间。From和to救助空间互换角色。维持活动的对象将在救助空间不断复制,直到它们获得使用期并转入旧域。使用-XX:SurvivorRatio可控制新域子空间的大小。

  同NewRation一样,SurvivorRation规定某救助域与Eden空间的比值。比如,以下命令把新域设置成64m,Eden占32m,每个救助域各占16m:

java -Xms256m -Xmx256m -Xmn64m -XX:SurvivorRation =2

  如前所述,默认状态下HotSpot对新域使用复制收集器,对旧域使用标记-清除-压缩收集器。在新域中使用复制收集器有很多意义,因为应用程序生成的大部分对象是短寿命的。理想状态下,所有过渡对象在移出Eden空间时将被收集。如果能够这样的话,并且移出Eden空间的对象是长寿命的,那么理论上可以立即把它们移进旧域,避免在救助间反复复制。但是,应用程序不能适合这种理想状态,因为它们有一小部分中长寿命的对象。最好是保持这些中长寿命的对象并放在新域中,因为复制小部分的对象总比压缩旧域廉价。为控制新域中对象的复制,可用-XX:TargetSurvivorRatio控制救助空间的比例(该值是设置救助空间的使用比例。如救助空间位1M,该值50表示可用500K)。该值是一个百分比,默认值是50。当较大的堆栈使用较低的sruvivorratio时,应增加该值到80至90,以更好利用救助空间。用-XX:maxtenuring threshold可控制上限。

  为放置所有的复制全部发生以及希望对象从eden扩展到旧域,可以把MaxTenuring Threshold设置成0。设置完成后,实际上就不再使用救助空间了,因此应把SurvivorRatio设成最大值以最大化Eden空间,设置如下:

java … -XX:MaxTenuringThreshold=0 –XX:SurvivorRatio=50000 …

  4.BEA JRockit JVM的使用

  Bea WebLogic 8.1使用的新的JVM用于Intel平台。在Bea安装完毕的目录下可以看到有一个类似于jrockit81sp1_141_03的文件夹。这就是Bea新JVM所在目录。不同于HotSpot把Java字节码编译成本地码,它预先编译成类。JRockit还提供了更细致的功能用以观察JVM的运行状态,主要是独立的GUI控制台(只能适用于使用Jrockit才能使用jrockit81sp1_141_03自带的console监控一些cpu及memory参数)或者WebLogic Server控制台。

  Bea JRockit JVM支持4种垃圾收集器:

  4.1.1.分代复制收集器

  它与默认的分代收集器工作策略类似。对象在新域中分配,即JRockit文档中的nursery。这种收集器最适合单cpu机上小型堆操作。

  4.1.2.单空间并发收集器

  该收集器使用完整堆,并与背景线程共同工作。尽管这种收集器可以消除中断,但是收集器需花费较长的时间寻找死对象,而且处理应用程序时收集器经常运行。如果处理器不能应付应用程序产生的垃圾,它会中断应用程序并关闭收集。

  分代并发收集器 这种收集器在护理域使用排它复制收集器,在旧域中则使用并发收集器。由于它比单空间共同发生收集器中断频繁,因此它需要较少的内存,应用程序的运行效率也较高,注意,过小的护理域可以导致大量的临时对象被扩展到旧域中。这会造成收集器超负荷运作,甚至采用排它性工作方式完成收集。

  4.1.3.并行收集器

  该收集器也停止其他进程的工作,但使用多线程以加速收集进程。尽管它比其他的收集器易于引起长时间的中断,但一般能更好的利用内存,程序效率也较高。

  默认状态下,JRockit使用分代并发收集器。要改变收集器,可使用-Xgc:<gc_name>,对应四个收集器分别为gencopy,singlecon,gencon以及parallel。可使用-Xms和-Xmx设置堆的初始大小和最大值。要设置护理域,则使用-Xns:java –jrockit –Xms512m –Xmx512m –Xgc:gencon –Xns128m…尽管JRockit支持-verbose:gc开关,但它输出的信息会因收集器的不同而异。JRockit还支持memory、load和codegen的输出。

  注意 :如果 使用JRockit JVM的话还可以使用WLS自带的console(C:eajrockit81sp1_141_03in下)来监控一些数据,如cpu,memery等。要想能构监控必须在启动服务时startWeblogic.cmd中加入-Xmanagement参数。

  5.如何从JVM中获取信息来进行调整

  -verbose.gc开关可显示gc的操作内容。打开它,可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。打开-xx:+ printgcdetails开关,可以详细了解gc中的变化。打开-XX: + PrintGCTimeStamps开关,可以了解这些垃圾收集发生的时间,自jvm启动以后以秒计量。最后,通过-xx: + PrintHeapAtGC开关了解堆的更详细的信息。为了了解新域的情况,可以通过-XX:=PrintTenuringDistribution开关了解获得使用期的对象权。

  6.Pdm系统JVM调整

  6.1.服务器:前提内存1G 单CPU

  可通过如下参数进行调整:-server 启用服务器模式(如果CPU多,服务器机建议使用此项)

  -Xms,-Xmx一般设为同样大小。 800m

  -Xmn 是将NewSize与MaxNewSize设为一致。320m

  -XX:PerSize 64m

  -XX:NewSize 320m 此值设大可调大新对象区,减少Full GC次数

  -XX:MaxNewSize 320m

  -XX:NewRato NewSize设了可不设。4

  -XX: SurvivorRatio 4

  -XX:userParNewGC 可用来设置并行收集

  -XX:ParallelGCThreads 可用来增加并行度 4

  -XXUseParallelGC 设置后可以使用并行清除收集器

  -XX:UseAdaptiveSizePolicy 与上面一个联合使用效果更好,利用它可以自动优化新域大小以及救助空间比值

  6.2.客户机:通过在JNLP文件中设置参数来调整客户端JVM

  JNLP中参数:initial-heap-size和max-heap-size

  这可以在framework的RequestManager中生成JNLP文件时加入上述参数,但是这些值是要求根据客户机的硬件状态变化的(如客户机的内存大小等)。建议这两个参数值设为客户机可用内存的60%(有待测试)。为了在动态生成JNLP时以上两个参数值能够随客户机不同而不同,可靠虑获得客户机系统信息并将这些嵌到首页index.jsp中作为连接请求的参数。

  在设置了上述参数后可以通过Visualgc 来观察垃圾回收的一些参数状态,再做相应的调整来改善性能。一般的标准是减少fullgc的次数,最好硬件支持使用并行垃圾回收(要求多CPU)。

- 作者: 缘客石 2006年03月5日, 星期日 00:16  回复(1) |  引用(0) 加入博采

J2SE的发展史

1.J2SE:怀胎

    Java的历史可以追溯到1991年4月,Sun公司的James Gosling领导的绿色计划(Green Project)开始着力发展一种分布式系统结构,使其能够在各种消费性电子产品上运行,他们使用了C/C++/Oak语言。由于电子产品种类繁多,运行环境各不相同,使得用这些语言进行开发的软件必须为不同的电子产品而专门设计,所以项目组疲于奔命,消费性电子产品软件环境的发展无法达到预期的目标,绿色计划也陷于停滞状态。
    直至 1994年下半年,由于Internet的迅猛发展和环球信息网 WWW的快速增长,第一个全球信息网络浏览器Mosaic诞生了;此时,工业界对适合在网络异构环境下使用的语言有一种非常急迫的需求;Games Gosling决定改变绿色计划的发展方向,他们对Oak进行了小规模的改造,就这样,Java在1995年的3月23日诞生了!Java的诞生标志着互联网时代的开始,它能够被应用在全球信息网络的平台上编写互动性及强的Applet程序,而1995年的Applet无疑能给人们无穷的视觉和脑力震荡。
    但没有相应的开发库而只靠Java语言来进行开发肯定是困难重重,所以Sun公司在1996年的1月23日发布了JDK1.0来帮助开发人员的开发。JDK包括两大部分:运行环境和开发工具。而运行环境包括五大部分:核心API,集成API,用户界面API,发布技术,Java虚拟机(JVM)。Java能够在信息网络时代快速发展,得益于它独特的组成结构,它并不直接被编译成所在平台的机器语言然后执行,而是先被编译成字节码,然后才到装有JVM的硬件上运行,所以它能够跨平台运行;而且,不是一定要使用Java语言来写程序才能编译成Java字节码,我们用C/C++/Javascript等语言来写程序也可以编译出JVM要求的字节码文件。在这个时期,人们使用最多的 Java API 无疑地就是 AWT,因为 JDK 1.0 时只有 AWT 称得上是一组完整的 framework,其它 Java API 都比较偏向个别的 class。
紧跟着,Sun公司在1997年2月18日发布了JDK1.1。JDK1.1相对于旧版本最大的改进,是推出了JIT(Just-In-Time)编译器。传统的JVM收到一道字节码指令后,解释成机器码,马上执行该机器码,然后丢掉该机器码,再接收下一道字节码指令……即便是以前执行过的字节码指令,依然需要重新解释成机器码才能执行。而JIT编译器会把常执行的部分先解释好放在内存,以后再次执行该指令时,不用再解释,直接从内存中取出机器码就可以执行了,这样提高了不少的效率。
    在JDK1.1时代,Java平台分为PersonalJava与EmbedddJava,前者比较适用于运算资源和内存丰富的设备,而资源有限者适用于后者。这样的分类明显不符合时代发展的潮流,所以,Java平台处处蕴藏着新的翻天覆地的革命……

2.J2SE1.2:诞生

    JDK1.2在1998年12月4日的隆重发布,标志着Java2平台的诞生。Java2的J2SE1.2时代是一个大变革时代,它进行了如下的三大革命:
* 市场推广革命
在Java1.0或者1.1中,称API库为JDK,但为了方便市场推广,Sun公司在Java1.2版以后将JDK1.2改名为J2SDK,将Java改名为Java 2。在99年Sun公司还将Java2平台分为三大块:J2SE,J2EE,J2ME。这次市场推广革命顺应了网络急速发展的潮流,对Java2平台的发展起到了很好的催化剂的作用。

* API供应标准革命
而随着供应商的不同,Java的API分为三大类:
Java Core API:由Sun公司制定的基本的API,所有的Java平台都应该提供。这就是我们平常所说的Java核心类库。
Java Optional API:由Sun公司制定的扩充API,Java平台可以有选择地提供。比如, JFC以前是Sun对JDK的功能扩展,不是Java的公共规范。Java2平台则包含了JFC技术,不需要再扩展了。
特殊API:由特殊厂商或者组织提供的API。比如,美国法律对出口中国的加解密算法的安全强度有一定的限制,Java平台中不能包括一些安全强度较高的加密解密算法,所以部分其它国家的公司,如加拿大的BouncyCastle公司等,可以提供一些自己的安全API。

* API制定过程的革命
如果你有需求不能通过遵循标准的API来实现,可以向 www.jcp.org 提出制定新的API的请求,经过审核,你的请求可能被通过或者驳回;如果是被通过,则开始进入制定该API的程序。

    J2SE1.2时代进行的这些革命形成的制度一直沿用到现在,对Java技术的发展形成了深远的影响。
除了上述的三大革命,Java2 还支持并新增了许多新特性,最受追捧的当属Swing库。Swing 是轻量级 的 API,它不但有各式各样先进的组件,而且连组件风格都可抽换。Swing 出现之后,大家很快地就不太使用 AWT 了。但请不要以为 Swing 是用来取代 AWT 的,其实 Swing 是架构在 AWT 之上的, Swing 的出现只是减少程序员直接使用 AWT 的机会,而不会让 AWT 消失。Java2 还废弃了一些API,最重要的莫过于Thread类中对suspend(),resume()和stop()等方法的废弃。由于JDK1.1的集合类库中的Vector类和HashTable类都考虑了同步,在平常的使用中影响效率,所以Java2专门添加了对应的非同步类,并完善了集合类库。
   Java2平台能够得到广泛的应用,还应归功于这个时期的一些优秀书籍,比如著名的<<Core Java2>>(卷一和卷二),该书随着JDK版本的变化已经出到第六版。

3. J2SE1.3:拓广

    Java2平台推出后,得到了市场的强烈反响,所以,在2000年5月8日推出的J2SE1.3对J2SE1.2的改进,主要是对各种已有API的加强和对新API的拓展。

数字运算: 加入了java.lang.StrictMath,方便我们的一般的数字运算。
新的Timer API:相信大家对其中的java.util.Timer和java.util.TimerTask一定不陌生。
Collections包:??加入了一些新的API,方便我们的使用。
虚拟机停止钩子:J2SE1.3还加入了一个强大的功能,那就是虚拟机停止钩子(Virtual Machine Shutdown Hooks),这个功能使得我们能够在虚拟机停止时完成我们自己的操作,比如关闭网络连接或者保存会话状态或者清除临时文件等等。
DNS服务:在JNDI接口方面,加入了一个DNS服务的实现。
Jini实现:J2SE1.3包含了一个Jini实现,这使得我们可以方便地把诸如打印机、摄像机和磁盘驱动设备插入现有网络中,并且能自动搜索已在网上的设备可以提供的服务并享用这些服务。
XML支持:由于计算机网络和XML技术的快速发展, J2SE1.3在Optional API中引入了Java API for XML包。
HotSpot虚拟机:J2SE1.3引入了HotSpot虚拟机。在solaris版的JDK1.3中,已经不支持传统的虚拟机,而windows版的JDK1.3同时支持传统虚拟机和HotSpot虚拟机。

    从上面的分析可以看出,J2SE1.3主要是对J2SE1.2查漏补缺和拓展新的API。从应用领域方面考虑,J2SE1.3已经涵盖了数据库、WEB、多媒体、网络、电话、影像、加解密、图形等等大部分的信息技术领域。
    在这个时期Java2还有一个重要活动就是推出SCSL(Sun社区源代码许可)许可协议。Sun公司开放源代码项目的“女1号”Danese Cooper在1999年加入公司,负责Sun(包括Java)和开放源代码社区之间的协调工作。Sun一直尽可能在赢利和开放源代码之间寻求更好的平衡。
在J2SE1.3时代,Java2平台得到了充分的发展,各大应用服务器在这个时代渐渐成熟并被广泛采纳,而Java2相关的培训和书籍更是如火如荼,《Thinking in Java》当属不得不推荐的一本。

4. J2SE1.4:快速

    J2SE1.4平台的推出发生在2002年2月13日,由于此前在Java平台和.Net平台间发生了规模浩大的孰优孰劣的论战,而论战中,Java平台最大的缺点就是性能问题,所以J2SE1.4平台把性能的改善放在了最重要的位置。

HotSpot虚拟机:  HotSpot虚拟机能够很大程度上提高性能,所以J2SE1.4已经不支持传统的虚拟机。现在,启动应用程序应该通过-client或者-server选项来启动,如果你还是通过-classic选项来启动,会收到这样的错误提示:”Error: Classic VM no longer supported。”
锁机制: 由于旧版的HotSpot虚拟机的锁机制会导制严重的性能和功能问题,J2SE1.4已经改写了该锁机制。

安全API: Java Cryptography Extension(JCE),Java Secure Socket Extension(JSSE)和Java Authentication and Authorization(JAAS)这三大安全API从optional API移到了core API中。这样,J2SE1.4的安全域(SecureRandom)实现可以利用操作系统提供的安全机制,以便缩短应用程序的启动时间。同时从optional API移到core API的功能还有XML处理API和JDBC2.0 API。
RandomAccess标记接口: 加入了RandomAccess标记接口,如果一个List实现了该接口,则表示它支持快速的随机访问,这样可以提高List访问的速度。

LinkedHashMap: 加入了LinkedHashMap,这是一个插入排序的Map实现,但它的运行速度和HashMap一样快。
反射: 很多产品中都要使用反射(Reflection)机制,但大家知道,反射是相当耗时的,所以,J2SE1.4中重写了java.lang.reflect.Field、java.lang.reflect.Method.invoke()、java.lang.reflect.Constructor.newInstance()和Class.newInstance()等方法,使得我们利用反射也能写出高性能的应用程序。

64位计算: J2SE1.4支持64位计算。64位支持使J2SE1.4可以使用超过几百GB的内存,而以前的版本仅可以使用4G以内的内存。内存使用的扩大可以提高程序的运行效率。
新的I/O API:  J2SE1.4在API层面最大的变动,就是它更新了原有的java.io包,以及加入了一组更有效率更多功能的新的I/O API。旧的I/O处理是以串流(stream)的方式来存取的,以字节为单位,而新的I/O处理是以块(block)的方式来存取的。一个块可能包含了512字节,而磁盘的存取本就是一个块一个块来进行的,所以新的I/O处理方式对于底层底数据存取更自然更有效率。
断言和日志处理: J2SE1.4版本在Java语言层面上加入了断言(assert关键字),在API层面上加入日志处理API,这些为程序的调试提供了强有力的支持 。

   从上面的分析可以看出,Java 2平台在经过数年的发展后,已经比较成熟稳定,J2SE1.4主要是对平台的性能进行较多的考虑和修改。在分布式程序方面,1.4版比1.3版的运行效率提高了一半以上;而在客户端程序方面,1.4版比1.3版的效率提高了1/3。当然,除了考虑运行效率,J2SE1.4还有其它方面的改进,如推出了一个全新的运行Java应用程序的方法Java Web Start,该方法克服了Java Applet的安全性限制的问题和Java Application的软件发布难的问题,并结合了上面两种方法的好处,为Java软件客户提供了非常友好的使用体验。
    J2SE1.4版是J2SE第一个参与了 Java共同体过程(JCP)的J2SE版本。 象Borland、Compaq、Fujitsu、 SAS、 Symbian、 IBM这样的公司,和Sun一起定义并发展了J2SE 1.4规范。在开放、良好的文档编撰与管理的过程中,形成了一个高质量的、代表了Java共同体的多样性的规范。J2SE1.4的Specification Lead是Doug Kramer。Doug Kramer是优秀的Java开发人员和著名的教育培训专家,他同时还是J2SE1.4.2的Specification Lead 和J2SE1.5的JVM的Specification Lead。

5. J2SE5.0:易用

    在2004年十月J2SE5.0发布的时候,SUN公司这样解释这次版本名称不是J2SE1.5而是J2SE5.0的原因:“从Java诞生至今已有9年时间,而从有J2SE算起也有5个年头了;在这样的背境下,将该版本号从1.5改为5.0可以更好的反映出新版的J2SE的成熟度、稳定性、可伸缩性、安全性。”
    J2SE的这次变更之重大和意义之深远,的确也值得我们为之把版本号变换到J2SE5.0。我们再看看Sun公司网站对J2SE5.0的features描述:“通过增强Java平台的力量,允许开发者更容易地使用,Java编程语言的这些改进将吸引大量各种Java开发者”,这是“Java技术发展历程的一个重要里程碑” 。从这个描述我们可以看出,J2SE5.0最大的目标是通过提供易用性而吸引各种开发者(当然包括以前的C/C++开发者) ,而它对以前版本的修改并不仅仅是API的升级,而且包括对Java语言层面的改进。

    访问环境变量:  最初的Java语言有一个访问环境变量的方法System.getenv(),但因为Java宣称的”Write Once,Run AnyWhere”特性,所以在JDK1.0中去掉了这个能够访问平台专有信息的方法。在J2SE5.0中,它又来了,并有所扩充。由此可见J2SE5.0对编程方便的重视程度。
泛型  J2SE5.0提供了强大的泛型机制,让程序员可以减少代码重复,这个变化应该可以吸引小部分的C#开发人员吧。

    增强的for循环  为了克服普通for循环的代码臃肿特点,J2SE5.0提供了增强的for循环,我们现在可以这样写一个for循环:

public void printAll(Collection<String> coll)
{
     for(String str : coll)
{
    System.out.println(str);
}
    }


怎么样?是不是简单了很多?
自动的装箱/拆箱 我们以前手工的做装箱(Boxing)/拆箱(unBoxing)操作时,是不是很烦?放心,J2SE5.0提供了自动的装箱/拆箱操作,我们现在可以写如下的代码:
    
public void boxing(int i)
    {
        ArrayList<Integer> list=new ArrayList<Integer>();
        list.add(0,i);
        System.out.println(“i的值是”+list.get(0));
    }

    可变参数数目 J2SE5.0 开始支持Varargs(不固定自变量个数),J2SE5.0中还加入了以前抛弃的枚举和C风格的格式化输出,这应该是为了吸引以前的C开发者吧。毕竟,在C开发中枚举和格式化输出用的是太多了。
    并发 J2SE5.0中加入了java.util.concurrent包,并向集合框架中加入了Queue接口,J2SE5.0还为各种集合提供了并发情况下的实现。
Properties类增强 由于XML的普及性应用,J2SE5.0为java.util.Properties类加入了从XML文件中装载属性和把属性值存储到XML文件中的方法。
    Annotation功能  J2SE5.0提供了注解(annotation)/元数据(metadata)功能,相信以后的大部分应用产品都将充分利用它的注解而实现产品的各种特性。
其它  J2SE5.0还在多线程(并发机制)、安全、国际化、UI等方面进行了大规模的变更,使得我们能够更方便地进行Java开发。

    其实,上面的这些变更,并不是我们程序员非要不可的内容。我们完全可以通过自己的办法来达到这些变更实现的功能。但J2SE5.0的目标就是让我们程序员能够更加方便地进行开发,所以,我们在基于J2SE5.0开发时,应该能够明显的体会到它的易用性。

6.展望

    时至今日,J2SE已经发展为一个覆盖面广、效率高、易用性强的技术平台,但Java并没有停止前进的脚步。Mustang版本的J2SE正在紧锣密鼓的开发当中,按以前的惯例,每两年会发布一个全新的J2SE版本,所以Mustang开发版对应的J2SE6.0发布版将在2006年完成。
    J2SE6.0的Specification Lead是Sun公司老资格的开发者Mark Reinhold,我们在J2SE1.2中就能看到他的影子。Mark Reinhold曾经是NIO(新的I/O处理) API的Specification Lead,现在是Sun的J2SE架构师与总工程师。Mark Reinhold透露说,J2SE6.0将更易于管理,可以给外部的管理软件提供更多信息。它可以让调试软件在程序运行的时候进行检查,而不是在运行之前,这样更容易找到问题所在。他说,J2SE6.0 将会更好地和图形用户界面集成,包括微软即将发布的Windows 版本Longhorn。
    2005年3月23日是Java技术十周年庆典日,在这十年的发展中,Java平台吸引了四百万开发者,在网络计算遍及全球的今天,更是有17.5亿台设备使用了Java技术。作为Java技术的基础,J2SE的功绩不可掩没,我们期望J2SE伴随Java平台一路走好!

- 作者: 缘客石 2006年03月5日, 星期日 00:10  回复(1) |  引用(0) 加入博采

EJB 3.0

SUN中国软件技术中心 王强 wynne.wang@sun.com

1 简化开发的目标

1.1我们的目标

EJB3.0是当前很多人谈论的话题,企业软件开发的一个关键是,提供一个尽量简单的的应用框架:它可以使开发人员不用关注于复杂的问题,比如事务处理、安全和持久化等。可以集中精力关注于商业逻辑,而不用关心那些低层的技术细节,从而提高开发者的效率,得到高质量的软件。这也是制定EJB 3.0规范的目标,简化开发!


1.2 当前的问题


EJB3.0希望开发人员能够从这种新的开发模式中受益,更好地推进J2EE的应用. 随着JAVA不断的进步和发展,越来越多的企业选择J2EE作为它們的解決方案,J2EE体系结构提供一个简化的中间层集成框架来满足应用的需求。然而,对于一般的开发人员,目前J2EE 1.4下的EJB 2.1 框架有些过于复杂了。 按照EJB2.1规范的定义,EJB组件必须事先很多的接口, 比如Home接口、Remote接口、local 接口,等等.还要针对各种应用类型定义许多的xml描述文件。当我们需要访问某个组件或者服务的时候,必须通过JDNI查找,通过名字绑定服务,才能找到我们需要的对象。

比如我们要使用某个EJB shopping cart, 就要首先实现一个initial context,然后通过他查找这个EJB 的home 接口,然后调用home街口的create方法,得到一个EJB object, 最后调用这个EJB object的商务方法. 下面是一个例子:
// EJB 2.1 Client view of the ShoppingCart Bean
...
Context initialContext = new InitialContext();
ShoppingCartHome myCartHome = (ShoppingCartHome)
initialContext.lookup(“JAVA:comp/env/ejb/cart”);
ShoppingCart myCart= myCartHome.create();
//Use the Bean
Collection widgets = myCart.startToShop(Widgets )
...
// Don't forget code to handle JAVAx.ejb.CreateException
...
EJB2.1的规范还要求我们必须实现Javax.EJB里面定义的接口, 实现里面的Methods比如 EJBCreate(), EJBPassivate(), and EJBActivate()。大多数情况下这些方法是不需要开发人员作任何修改的。这些规定实际上都和真正核心的商务逻辑没什么关系,都只是一些技术模板,规定了开发人员必须按照这样的模板进行开发. 比如下面这段代码:

人们开始思考,怎么样才能把EJB的开发变的更加简单,更好的利用这种技术. 当前正在制定的EJB 3.0的标准的目标就是简化开发, 让更多的开发人员被它的易用和强大功能所吸引过来,喜爱这项技术。为了达到这个目标,要做的第一件事情,也是最重要的事情,就是从一个开发人员的角度,将EJB的使用尽量的简化。

1.3 JCP专家组的工作


这个工作是由EJB3.0 专家组来完成的。我们知道JAVA的开发和推动是由一个开放的组织来完成的,这个组织的名字叫做JAVA Community Process。简称JCP. SUN的理念是: 创新无处不在. 所以JCP小组从世界的每个角落听取关于JAVA的建议,将各方面对JAVA的要求通过制定JSR(JAVA Specification Request )形式确定下来. EJB 3.0规范的JSR编号是 220. 整个专家组的制定成员包括J2EE 注册用户, 应用服务器的开发厂商和J2EE社区的成员.


简化一个现有的技术,尤其是得到广泛的开发人支持的技术,比如EJB, 不是一个简单的工作. 作为铺垫,专家组进行了大量的准备工作,检验了EJB技术的复杂性,当前EJB流域流行的各种模式和反模式,以及从客户和开发人员来的各种需求。开发人员和用户根据实际的需要,希望EJB能够提供他们满意的特性.
检验结果发现,大多数情况下,人们不需要更高级的技术,而是需要更简化的技术,来简化当前的开发模式。而不是像以前的EJB发布一样,在技术复杂度上的提高。定义一个新技术,不仅对老的技术有一定的更新, 也要能充分并容老的技术, 提供一定的向后兼容性. 因为采用这些技术的企业,已经在这些技术上投资了很多. 如果因为技术的更新就使得对应的IT系统和数据变得不能使用,是一件非常糟糕的事情.


所以,定义了EJB3.0规范的同时,如何支持现有的EJB技术是非常必要的. 要保证现有的EJB API是持续可用的, 还要和新的EJB3.0 API结合起来, 对早期的API应该续提供支持而不是标记为不赞成使用

1.4 标注的新功能


EJB3.0的很多新特性是通过JAVA SE5.0来实现的。这里我们就要谈到JAVA SE 5.0,它所提供的许多特性,其中最有趣的一点就是标注(Annotation)的功能。我们知道以前的JAVA语言都是命令格式的, 比如a.b(), 表示让类a做事情b, 但是很多时候我们只是需要对某个对象做一些注解,比如对某个类标记为可持续化的Serializable. 这只是一个标记,为了以后的处理提供说明,本身不需要做任何操作。
在Deploy 的时候, 提供了很多说明的XML文件,比如部署描述文件,里面说明了引用的EJB的名字,接口, 以及当前EJB的Transaction Type等等信息. 所有这些信息都是说明性的,而不是命令性的. 用来对某个对象的某个属性坐一段说明. 因此,有一个非常有趣的想法,能不能通过对JAVA语言的扩展,结合标注和命令这两者的优点 ?


这也是有一个专家组在JCP的组织内完成的, JSR规范的编号是JSR 175, 为JAVA SE 5.0支持注解(Annotation)的功能. 这个规范为EJB3.0 的简化实现提供了一些基本的支持, 也是最关键的支持. 标注可以有自己的属性,也可以定义自己的持续时间,表示这段信息是否保存到 源代码中,还是一直持续到Class中,或者一直保持到运行时间. 标注有自己的缺省值.大多数情况下,无须说明我们就可以推算出来这个对象的行为。 
2 轻松的实现开发

2.1 减轻开发人员的负担

EJB3.0的简化工作包括下面几个部分:
 提供一个简化的API, 包括对EJB的定义,对EJB的引用等等
 减少开发的类数目,不再需要那么多的interface
 相关性注入
 简化的查询机制
 从开发人员的角度不必要使用部署描述文件, 很多的工作可以放到代码里面用标注来说明,比如Entity Bean 的Transaction Type
 简化的持久化功能
 简化和改善数据对象的O/R Mapping.


标注可以应用到编程语言的一些基本元素上,比如类,方法,变量,包等等. 当我们在代码中使用了这些标注, 根据这个标注对应的持续策略, 它可以被编译到Class 文件中去,或者一直保持到运行的时候。大多数在 EJB 3.0 中定义的标注都是Runtime保持策略, 这样做的好处是提供了最大的灵活性。而且由于大量工作放到了运行的时候来做,也减少一部分Deploy的工作。


我们通过定义缺省的语法来说明大多数常见的情况。开发人员不需要再专门说明常见的情况,“OK, 没问题,缺省的设置就已经可以满足需要了“ 这样,开发人员的工作大大减轻了。
这也引出来了EJB3.0中的一个很有意思的概念 "Configuration By Exception" --只有在例外的情况下才需要我们的参与.


EJB3.0的目标是简化开发人员的工作,让他们专注于商务应用的开发而不是把精力放到很多繁琐的例行工作上,这些工作可以交给Container来完成。EJB通过注入来指定自己需要的资源,不用再写那些麻烦的方法. 将对象的创建和获取提取到外部。由外部容器提供需要的组件。这样,开发人员只用在开始的时候定义,说我需要这个资源, 后面就可以直接使用这个资源, 这样会大大的简化开发, 因为开发人员只用关心如何使用这个对象和商务方法, 而不用担心其他的技术细节。

2.2 抛开繁琐的细节
下面我们看看都作了那些简化。我们的目的是把那些繁琐的技术细节隐藏起来,程序开发人员只用关心自己的商务逻辑代码,而不用关心那些复杂的技术模板,必须实现的接口等,哪怕这些方法和接口根本不需要实现.

 不再需要EJB的部件接口
 每个EJB 都只是一个普通的JAVA Class
 不再需要home接口, 我们不再用home 来创建这个EJB
 不再需要实现javax.ejb.EnterpriseBean借口
 对于需要在回调方法里实现的部分,我们采用标注的方式说明一个方法为回调方法
 不再需要使用复杂的JNDI名字调用机制,对于需要服务或者资源的地方
 我们采用了相关性注入的方法,另外也可以通过简化的lookup方法来查找资源

下面让我们看一个简单的无状态SessionBean的例子。无状态session Bean是最简单也是最常用 Bean,很多初学EJB的人都从无状态Session Bean开始。如何让无状态Session Bean 变的简单易用成为一个非常有意义的话题。

前面假设我们已经定义了相关的interface, 这个EJB2.1的的功能是对员工的工资做处理,打开一个数据库连接,进行员工工资信息的某些操作,等等.

// EJB 2.1
public Class PayrollBean implements JAVAx.ejb.SessionBean
{
SessionContext ctx;
DataSource empDB;
public void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}
public void ejbCreate() {
Context initialContext = new InitialContext();
empDB = (DataSource)initialContext.lookup( JAVA:comp/env/jdbc/empDB );
}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() {}
public void setBenefitsDeduction (int empId, double deduction) {
...
Connection conn = empDB.getConnection();
...
}
...
}
// NOTE deployment descriptor needed


这里我们首先要实现一个 sessionBean interface,保持一个对sessioncontext的引用,然后是在EJBcreate 方法里面我们调用JNDI得到一个datasource。 后面我们必须要定义一些回调方法,虽然这些方法我们不会实现任何逻辑。然后在 商务方法里面,我们打开一个数据库连接。

注意,我们还没有完。为了使用这个EJB, 必须加上xml的部署描述文件打好包。

下面是一个常见的部署描述文件可以是这样子的。

<session>
<ejb-name>PayrollBean</ejb-name>
<local-home>PayrollHome</local-home>
<local>Payroll</local>
<ejb-class>com.example.PayrollBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<resource-ref>
<res-ref-name>jdbc/empDB</res-ref-name>
<res-ref-type>javax.sql.DataSource</res-ref-type>
<res-auth>Container</res-auth>
</resource-ref>
</session>
...
<assembly-descriptor>
...
</assembly-descriptor>

从开发人员的角度来检查这样一个简单的EJB 是非常有意义的。我们可以更好的理解有那些地方可以有改动。一个主要的问题是,定义了这么多的方法和结构以后,程序的清晰化受到了影响,结构变得混乱。真正需要关注的商务方法没有很好的强调和体现。

下面让我们看一看EJB 3.0 是如何实现一个简单的stateless session Bean的。

// Same example, EJB 3.0
@Stateless public class PayrollBean implements Payroll {
@Resource DataSource empDB;
public void setBenefitsDeduction (int empId, double deduction) {
...
Connection conn = empDB.getConnection();
...
}
...
}


@Stateless 标注表示这是一个stateless session Bean. 使用这样的标注可以让我们不再需要使用SessionBean 接口, 这样就大大的简化了EJB的实现类。 Bean的商务接口是payroll. 这是一个普通的JAVA interface. 缺省的情况,container会把他作成一个local interface. 如果需要实现一个远程接口,只需再定义一个标注 @Remote. 注意在接口里面不需要定义 RemoteExceptions,它由Container 层在后面处理掉了

2.3 引用对象的新方法
在老的EJB规范中,还有一个比较复杂的地方是访问环境对象。EJB 2.1 里面访问环境要首先在组件定义的相关性引用,比如resource-refs, EJB-refs, 然后在JNDI名字空间里面配置这些环境对象。 最后查找运行的时候JNDI空间里面查找这些环境对象
EJB 3.0对此提供了两种简化的方案:

1) 相关性注入
2) 一个简化的查询lookup方法

相关性注入是一种技术,开发人员在原代码中加入标注的一个定义,说明需要这个环境对象,然后由Container在初始化的时候把真的环境对象注入里面。
目前EJB3.0 spec里面有两种注入方式,setter注入和变量注入, 这些在以后的规范中可能会有变化,比如一种统一的注入方式。 我们可以通过注入标记@EJB来定义一个EJB的引用,也可以通过注入标记@resource 表示我们要引用一个资源,它可以是EJB以外的一切环境对象. 专家组为了尽可能的简化开发,将使用注入的对象类型作了简化。

EJB3.0 将仍然提供一个动态查询的方法,但是从程序开发人员的角度,不需要再使用JNDI API,而是采用更为简化的EJB Context 的 Lookup方法。

在新的EJB 3.0规范中,因为不再需要复杂的home接口和EJB 接口。EJB client端的编码也大大的简化了,和访问一个普通的JAVA 对象没有什么区别。上面的那个EJB2.1的例子比较起来,在EJB 3.0 里面使用一个EJB变的非常简单,只需要两行代码:

// EJB 3.0 client view
@EJB ShoppingCart myCart;
...
Collection widgets = myCart.startToShop(Widgets );
...

首先定义一个EJB的应用,然后就可以直接调用这个EJB的商务方法,剩下的工作由Container来为我们完成。

2.4 新的事务管理

EJB中的事务(Transaction)管理大大简化了用户开发程序。把应用分成一个一个小的单元,叫做transaction. 事务系统保证了这个单元里面的任务是完整的,要么全部执,要么出错后完全退回到初始状态。


J2EE中有两种类型的事务, 容器管理的和Bean管理的。他们在如何启动和结束事务上是不同的。Bean 管理的事务由组件使用 UserTransaction类显式启动和结束的。代码中需要调用方法 UserTransaction.begin() 和 UserTransaction.commit() 。Container 管理的事物是由container 自动来完成的。
针对不同的事务类型,可以定义6种不同的事务属性。
事务属性告诉 Container 是否把EJB方法里面的工作放到用户的事务里面。还是针对这个方法重新启动一个新的事务. 或者执行这个方法而不包含在事务里面,等等。
在EJB3.0规范中,我们缺省的定义是容器管理的事务. 而且针对所有的Bean方法,应用Required Transaction属性,它的意思是如果调用这个方法的应用没有Transaction Environment,那么这个方法会自动创建一个新的。
开发人员可以使用标注的方式在针对整个EJB或者某个具体的方法指定他的Transaction Attribute.
首先我们看一个工资处理的EJB的例子, 这是一个标准的EJB 3.0 Stateless Session Bean。缺省情况下,每个方法是都是由container来管理Transaction的,缺省的事务属性是required
// Uses container-managed transction, REQUIRED attribute
@Stateless public PayrollBean implements Payroll {
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empId) {...}
public void setSalary(int empId, double salary) {...}
}

下面还是这个例子,我们知道对于有关重要数据的改动,总是非常敏感的。比如我们在更改某个用户的工资的时候同时修改他的所得税,我们希望这两个调用是在同一个Transaction里面发生的。

@Stateless public PayrollBean implements Payroll {
@TransactionAttribute(MANDATORY)
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empid) {...}
@TransactionAttribute(MANDATORY)
public void setSalary(int empId, double salary) {...}
}

那么调用用户的工资改动的方法就必须在一个已经存在的Transaction Environment
中,为此,我们用@transactionattribute(mandatory)标注 这个方法必须在一个客户端的transaction中,如果客户端没有这样的一个 Transaction Context, Container会扔出来一个 Javax.ejb.EjbTransactionRequiredException 的错误信息。

2.5 新的安全机制
EJB架构不鼓励开发人员用代码的方式实现安全机制,而是采用安全角色的方法,通过定义可以访问的安全角色,来限制对某个方法的访问权限。

缺省情况,在3.0里面所有的方法都是"unchecked"。也就是说缺省情况下对所有方法是不用安全控制策略的。如果调用某个方法的客户端具有某个用户角色(Role) ,我们可以制定是否这个方法也是沿用这个角色。缺省情况下的安全策略是"Caller Identity",也就是说被调用者的角色和调用者的角色应该是一致的

这是一个EJB 3.0的安全实现的例子。我们对于其他的方法都没有设定安全策略。但是对于设定某个员工的工资是多少,这样的安全要求比较高的方法设定了只有人事部门的管理员才能调用。我们采用了一个

@RolesAllowed("HR_PayrollAdministrator ")
// Security view
@Stateless public PayrollBean implements Payroll {
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empid) {...}
// salary setting is intended to be more restricted
@RolesAllowed( HR_PayrollAdministrator )
public void setSalary(int empId, double salary) {...}
}

2.6 事件的通知和检查

EJB 3.0中, 开发人员不需要实现那些不必要的callback methods。 他可以把任意方法指定为一个事件通知方法。通过标记一个通知标注,我们把一个方法标记为一个回调方法, 例如:

@Stateful public class AccountManagementBean
implements AccountManagement {
Socket cs;
@PostConstruct
@PostActivate
public void initRemoteConnectionToAccountSystem {
...
}

@PreDestroy
@PrePassivate
public void closeRemoteConnectionToAccountSystem {
...
}
...
}

在这个EJB 3.0 的例子当中, 我们定义了一个有状态的session Bean,把初始化的工作都交给init remote connection方法,同时标记他为一个回调方法,在construct ,和 activite之后调用
同时我们把清理的工作都交给close remote connection方法,同时标记他为一个回调方法,在destroy 和passivate之前调用

对于那些高级的用户,需要定制自己的事件检查和侦听机制。检查方法和所被检查的对象方法可以在同一个 Bean 中,也可以在不同的JAVA Class里面。 这种检查机制有下面的特点:
 在方法周围进行检查
 包装商务方法的整个调用过程
 可以对方法调用的参数和结果进行处理
 检查类的序列中,可以拿到上下文数据
 多个检查类可以按照指定的顺序执行
 可以用部署表述符来指定执行顺序

我们用 @Interceptors 标注指定一个外部的检查方法类, 用 @AroundInvoke制定内部的某个方法为检查方法。在检查方法里面,我们用proceed()来调用具体的商务方法。目前的检查方法是检查一个Bean里面的所有方法,专家组正在制定标准, 让它可以具体到检查制定的某个方法。

下面是一个EJB3.0的检查方法的例子:

我们制定了这个无状态session Bean检查方法类是这三个类.
accountaudit, metrics, customsecurity。那么实际执行的时候会按照这个制定的顺序来实行
检查.

@Interceptors({
com.acme.AccountAudit.class,
com.acme.Metrics.class,
com.acme.CustomSecurity.class
})
@Stateless
public class AccountManagementBean
implements AccountManagement {
public void createAccount(int accountId, Details details) {...}
public void deleteAccount(int accountId) {...}
public void activateAccount(int accountId) {...}
public void deactivateAccount(int accountId) {...}
...
}

2.7 部署描述文件的优先级

在EJB3.0中,我们可以用标注的方法来指定对环境对象的引用,也可以用部署描述符文件(Deployment Description)的方式来制定对环境对象的应用, 也可以两个同时使用. 如果我们在部署描述文件和代码标注中都制定了环境对象,那么部署描述文件中的那个引用有更高的优先级, 这样就给了应用的Deployer相对比较大的灵活性来控制.

3 持久化的魔力


3.1 最初的目标

EJB 3.0 专家组的另外一个目标是为实体Bean( Entity Bean ) 和对象/关系的映射,提供一个轻量级的模型。实际上,目前关于实体Bean的争论很多, 很多批评人士对它的架构感到不满,我们的目标是改善EJB 容器管理的持久化模型,从各地的一些优秀的开源软件中吸取灵感,从一些反模式(Anti-Pattern)中吸取经验, 从而让新的标准称为技术上的领跑者。 EJB3.0的持续化包括下面的一些特性:

 简化实体bean的编程方式,减少不必要的开发接口
 改善EJB的持久化, 为O/R映射提供继承和多态的信息
 可以在EJB Container之外使用实体Bean
 不需要Container 就可以对商务方法进行测试
 不再需要数据传输对象DTO (Data Transfer Objects) 之类的设计模式(Design Pattern)
 改善的EJB QL

为了解决这些意见,ejb 3.0 的专家组集中在一个经过简化的持久化模型,目前业界已经有类似的产品和模式,比如Hibernate和Toplink, 这是一个全新的方向,代表着轻量级的对象/关系(O/R)映射模型。


在EJB3.0中,实体Bean是普通的Java 类, 这是一些真正的类, 而不是抽象类。而且更为简化的是,开发人员不再需要实现任何接口,不论是商务借口或者是回调接口。
而且不再需要实现数据传输对象(DTO), 因为现在的Entity Bean本身就是一个简单的普通JAVA 对象POJO (Plain Old Java Object), 标记为序列化之后就可以在客户端和服务端进行传递,不再需要特殊的处理。

3.2 管理类的角色
在新的EJB3.0规范里面,我们看到了一个 EntityManager 类, 对于实体Bean而言,这是一个类似于调用工厂(Factory)或者统一的Home接口之类的角色。Entity manager自己的生命周期可以由Container或者应用程序都可以来管理。
Entity Manager 将负责跟踪数据库事务上下文中, 实体bean 对象的状态。对于开发人员来说, javax.persistence.EntityManager 成为对实体bean的统一访问点。 可以把它看作是对实体Bean操作的一个”home”. 我们要通过entity manager 调用实体bean的生命周期管理。 比如:Persist, Remove, Merge, Flush, Refresh, 等方法。

Entity Manager是所有Entity Bean的持久化管理接口,任何对Entity Bean的操作都必须通过它来进行。有的开发人员会对这个接口感到熟悉,因为它与Hibernate的Session接口和JDO的Persistence Manager非常相似。Entity Manager的接口主要方法如下:

package javax.ejb;

public interface EntityManager
public void create(Object entity);
public < T > T merge(T entity);
public void remove(Object entity);
public Object find(String entityName, Object primaryKey);
public < T > T find(Class < T > entityClass, Object primaryKey);
public void flush();
public Query createQuery(String ejbqlString);
public Query createNamedQuery(String name);
public Query createNativeQuery(String sqlString);
public void refresh(Object entity);
public void evict(Object entity);
public boolean contains(Object entity);
}

EntityManager 还大大方便了查找方法的实现,记得我们在EJB2.1里面是怎么做的吗?在EJB 3.0里面,可以直接调用EntityManager.find(String entityName, Object primaryKey),查找具有某个主键的实体 bean 实例。例如:

public OrderBean findByPrimaryKey(String orderId)
{ return (OrderBean)em.find("OrderBean" , orderId);
}

EntityManager作为Query对象的生产工厂, 可以用createQuery(String ejbQlString) 创建一个EJB QL 查询,也可以用createNamedQuery(String queryName)来创建一个 NamedQuery 查询。下面是一个例子:


public List findWithAddr(String addr) {
return em.createQuery(
"SELECT o FROM Orders o WHERE o.addr LIKE :orderAddress")
.setParameter("orderAddress", addr)
.setMaxResults(100)
.listResults();
}

3.3 O/R Mapping的标注


O/R Mappings 标注的元数据,使得用户可以修饰他们的EJB3.0 Entity Bean. 在 EJB 3.0缺省环境下,表的名字就是类的名字, 两者是一致的, public Java Bean getter方法假定为访问表中同样名字的属性值, 开发人员可以通过@Table, @column等各种不同的标注来修改这个缺省的定义. 例子:


@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",
pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
public class Customer { ... }


@Entity 说明这是一个Entity Bean, Table标记了对应的O/R Mapping的主表, 而 SecondaryTable标记了 Entity对应的辅助表,
持续化的API, 和查询语言,以及O/R Mapping标注, 都是EJB 3.0规范的一部分。
持久化API的设计目标是能够独立于Container的运行,只需要有一个Java SE环境就可以运行了。

4 小结


本文以描述了EJB 3.0规范的一些新特性。EJB 3.0将是EJB历史上最大的一次改动,它充分吸收了一些开源项目,比如Spring、Hibernate的经验,变得更加方便实用,体现了简化开发的设计目标。这篇文章希望能够给大家带来一点关于EJB 3.0的印象,目前EJB 3.0规范已经进入了Proposed Final Draft阶段,当然,将来这个规范的技术细节还可能发生变化。

- 作者: 缘客石 2006年03月2日, 星期四 10:36  回复(1) |  引用(0) 加入博采

经典陶艺

- 作者: 缘客石 2006年01月17日, 星期二 14:53  回复(1) |  引用(0) 加入博采

2006年1月15日博客网新系统上线?

博客网新系统能否如方老总所言,在1月15日上线,期待中!

新系统上线后,博客网的问题是否能够得到解决,拭目以待!

稳定压倒一起,如果系统还是继续不稳定下去的话,估计离大家集体出逃的日子也不远了!

个人认为,博客网应该向博友们正式道歉,目前如此之不稳定的博客网让大家极度郁闷,严重打击了众博友的积极性!

另外,后台管理系统界面不友好、操作不便捷的问题也一直没有得到解决?博客网自己人就感觉不到么?而且,应该很容易调整的,怎地就不给改呢?

- 作者: 缘客石 2006年01月11日, 星期三 23:00  回复(1) |  引用(0) 加入博采

人生励志32佳句

      01、大多数人想要改造这个世界,但却罕有人想改造自己。
  02、积极的人在每一次忧患中都看到一个机会,而消极的人则在每个机会都看到某种忧患。
  03、莫找借口失败,只找理由成功。(不为失败找理由,要为成功找方法)
  04、伟人之所以伟大,是因为他与别人共处逆境时,别人失去了信心,他却下决心实现自己的目标。
  05、世上没有绝望的处境,只有对处境绝望的人。
  06、当你感到悲哀痛苦时,最好是去学些什么东西。学习会使你永远立于不败之地。
  07、世界上那些最容易的事情中,拖延时间最不费力。
  08、人之所以能,是相信能。
  09、一个有信念者所开发出的力量,大于99个只有兴趣者。
  10、每一发奋努力的背后,必有加倍的赏赐。
  11、人生伟业的建立 ,不在能知,乃在能行。
  12、任何的限制,都是从自己的内心开始的。
  13、含泪播种的人一定能含笑收获。
  14、欲望以提升热忱,毅力以磨平高山。
  15、一个能从别人的观念来看事情,能了解别人心灵活动的人,永远不必为自己的前途担心。
  16、一个人最大的破产是绝望,最大的资产是希望。
  17、不要等待机会,而要创造机会。
  18、如果寒暄只是打个招呼就了事的话,那与猴子的呼叫声有什么不同呢?事实上,正确的寒暄必须在短短一句话中明显地表露出你对他的关怀。
  19、昨晚多几分钟的准备,今天少几小时的麻烦。
  20、做对的事情比把事情做对重要。
  21、人格的完善是本,财富的确立是末。
  22、没有一种不通过蔑视、忍受和奋斗就可以征服的命运。
  23、行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。
  24、没有天生的信心,只有不断培养的信心。
  25、只有一条路不能选择——那就是放弃的路;只有一条路不能拒绝——那就是成长的路。
  26、人性最可怜的就是:我们总是梦想着天边的一座奇妙的玫瑰园,而不去欣赏今天就开在我们窗口的玫瑰。
  27、征服畏惧、建立自信的最快最确实的方法,就是去做你害怕的事,直到你获得成功的经验。
  28、失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。
  29、让我们将事前的忧虑,换为事前的思考和计划吧!
  30、再长的路,一步步也能走完,再短的路,不迈开双脚也无法到达。
  31、任何业绩的质变都来自于量变的积累。
  32、成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。 

- 作者: 缘客石 2006年01月10日, 星期二 23:17  回复(1) |  引用(0) 加入博采