用java的jwindow实现程序运行出的splash画面

bootcool 原作 import javax.swing.*; import java.awt.*; /* JWindow 是一个能够在用户桌面的任何地方显示的容器。 所以能够使用它构成程序刚运行时的splash画面。 */ public class ESplash extends JWindow implements Runnable { private Thread thread = null; private Image logo = null; private Color bg_color = new Color(255, 255, 255); private Toolkit toolkit =getToolkit(); private int image_width; private int image_height; public ESplash() { logo = new ECreateIcon().getSplashImage(); loadImage(logo, 0); image_width = logo.getWidth(this); image_height = logo.getHeight(this); setBackground(bg_color); setCursor(new Cursor(3)); setSize(image_width + 10, image_height + 10); //设置JWindow的显示位置 int Xpos = (toolkit.getScreenSize().width - getSize().width) / 2; int Ypos = (toolkit.getScreenSize().height - getSize().height) / 2; setBounds(Xpos, Ypos, getSize().width, getSize().height); setVisible(true); } /* 通过使用MediaTracker加载图像,确保图像被正确的加载。 图像被加载后,将进行绘图。 */ private void loadImage(Image image, int ID) { if(image != null) { MediaTracker tracker = new MediaTracker(this); tracker.addImage(image, ID); try { tracker.waitForID(ID); } catch(InterruptedException _ex) { } } } /* 在JWindow部件上绘制图像。 */ public void paint(Graphics g) { g.drawImage(logo, 5, 5, image_width, image_height, this); //设置字体的色彩 g.setColor(new Color(102, 102, 150)); g.drawString("正在初始化系统......", 7, getSize().height - 72); //设置矩形框的背景色彩。 g.setColor(new Color(255, 255, 255)); //绘制矩形框 g.fillRect(5, getSize().height - 70, 317, 7); g.drawRect(5, getSize().height - 70, 317, 7); //重新设置将要填涂在矩形框中的颜色 g.setColor(new Color(102, 102, 150)); for(int n = 0; n < 317; n += 5) try { //线程休眠50毫秒 Thread.sleep(50L); //填涂矩形框 g.fillRect(5, getSize().height - 70, n, 5); } catch(Exception _ex) { } } public void run() { //设置鼠标为等待状态 setCursor(new Cursor(3)); repaint(); } public void stop() { //结束线程 thread = null; logo = null; } //更新图形区,防止绘图时产生闪烁现象。 public void update(Graphics g) { paint(g); } } ///////////////////////////////////////////////////// import java.awt.*; import java.awt.image.*; import java.awt.event.*; import javax.swing.*; public class ECreateIcon{ private static Image splashimage; public ECreateIcon(){ splashimage = getImageFromResource("resources/images/Esplash.gif"); } //获得图像 private Image getImageFromResource(String image_path) { return Toolkit.getDefaultToolkit().getImage(image_path); } public ImageIcon createImageIcon(String filename) { String path = "/resources/images/" + filename; return new ImageIcon(getClass().getResource(path)); } public Image getSplashImage() { return splashimage; } } <

小小豆叮

用JAVA实现Undo、Redo,Copy、Paste、Cut

programlover原作 package clipborad; import javax.swing.JTextArea; import java.awt.datatransfer.*; import java.awt.*; import javax.swing.*; import java.io.*; import javax.swing.undo.*; import javax.swing.text.*; import javax.swing.event.*; import java.awt.event.*; /** * Title: 用JAVA实现Undo、Redo,Copy、Paste、Cut * Description:用JAVA实现Undo、Redo,Copy、Paste、Cut * Copyright: Copyright (c) 2001 * Company: * @author: 彭建雄 * @version 1.0 */ public class ClipTextArea extends JTextArea implements ClipboardOwner{ Clipboard strBoard = this.getToolkit().getSystemClipboard(); UndoManager undo = new UndoManager(); Document doc = getDocument(); public ClipTextArea() { doc.addUndoableEditListener(new UndoableEditListener(){ public void undoableEditHappened(UndoableEditEvent e) { doc_addedit(e);} }); addActionMap(); } public void addActionMap() { getActionMap().put("Undo", new AbstractAction("Undo") { public void actionPerformed(ActionEvent evt) { try { if (undo.canUndo()) { undo.undo(); } } catch (CannotUndoException e) { } } }); getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo"); getActionMap().put("Redo",new AbstractAction("Redo") { public void actionPerformed(ActionEvent evt) { try { if (undo.canRedo()) { undo.redo(); } } catch (CannotRedoException e) { } } }); getInputMap().put(KeyStroke.getKeyStroke("control R"), "Redo"); getActionMap().put("Copy",new AbstractAction("Copy") { public void actionPerformed(ActionEvent evt) { copy(); } }); getInputMap().put(KeyStroke.getKeyStroke("control C"), "Copy"); getActionMap().put("Cut",new AbstractAction("Cut") { public void actionPerformed(ActionEvent evt) { cut(); } }); getInputMap().put(KeyStroke.getKeyStroke("control X"), "Cut"); getActionMap().put("Paste",new AbstractAction("Paste") { public void actionPerformed(ActionEvent evt) { paste(); } }); getInputMap().put(KeyStroke.getKeyStroke("control V"), "Paste"); } public void copy() { String sCopy = getSelectedText(); if (sCopy != null) { StringSelection sSelection = new StringSelection(sCopy); strBoard.setContents(sSelection,this); } } public void cut() { String sCopy = getSelectedText(); if (sCopy != null) { StringSelection sSelection = new StringSelection(sCopy); strBoard.setContents(sSelection,this); this.replaceSelection(""); } } public void paste() { Transferable sTransf = strBoard.getContents(this); if (sTransf!=null) { try { String sPaste = (String)sTransf.getTransferData(DataFlavor.stringFlavor); this.replaceRange(sPaste,getSelectionStart(),getSelectionEnd()); } catch(UnsupportedFlavorException e) { } catch(IOException ioe) { } } } public void lostOwnership(Clipboard clipboard,Transferable contents) { } void doc_addedit(UndoableEditEvent e) { undo.addEdit(e.getEdit()); } } <

小小豆叮

在win98下安装JSP环境 (jswdk-1.0.1或tomcat3.1在Jdk

在PWIN98SE环境下 ,我使用JSP运行环境为JSWDK-1.0.1和JDK1.3,要到http://java.sun.com处下载。JDK1.3安装过程很简单,JSWDK1.0.1无须安装只要解压缩即可。然后修改AUTOEXEC.BAT如下:   PATH=%PATH%;c:jdk1.3   set CLASSPATH=C:jdk1.3libTools.jar;C:jdk1.3libdt.jar   set JAVA_HOME=c:jdk1.3(我把JDK装到了C盘)   有两个问题有必要说明一下。   一. 启动jswdk-1.0.1下的startserver.bat的时候,报告Out of Environment Space错误。(tomcat3.1通 ,启动tomcatin下的 startup.bat)   查阅JSWDK1.0.1的redme.html得到:   Out of Environment Space error message   On Windows 95/98 systems, you may see an "Out of Environment Space" error message when starting the server. This happens if Windows provides too small a space for environment variables. To work around this limitation:   1 Close the DOS window (the error can corrupt its CLASSPATH variable).   2 Open a new DOS window.   3 Click on the MS-DOS icon at the top left of the window.   4 Select the Properties option.   5 Click on the Memory tab.   6 Adjust the "Initial Environment" drop-down box from "Auto" to "2816".   7 Click OK.   8 Start the server.   明白了,只要调整startserver.bat(startup.bat) 的属性,将“内存”中的“初始环境”由“自动”修改做“2816”就搞定了。 二.还是无法启动SERVER,好象有个弹出窗口在出错误信息,但无法捉到,转向输出也不起作用   打开startserver.bat仔细检查,原来大部分都是在设置环境变量,只有一句关键的:   start java com.sun.web.shell.Startup %1 %2 %3 %4 %5 %6 %7 %8 %9   那么试着在DOS窗口下运行JAVA,提示注册表访问错误,键名为:   “SoftwareJavaSoftJavaJava Runtime Environment”   打开注册表检查,发现键名是“Java运行时环境”,靠,真不知道JDK的安装程序在搞什么飞机,把这个键名居然给本地化了!改回英文,再在IE5中敲入http://127.0.0.1:8080/,   呵,终于看到了JavaServer (tm) Web Development Kit 的大字标题。 <

小小豆叮

(iText)的源文件

<

小小豆叮

推荐:将数据通过WebServer发布成PDF格式(iText)

<

小小豆叮

XeoMenu 1.1

<

小小豆叮

一个研究极端编程(XP)的国外站点

<

小小豆叮

Sealing Violation错误模式

一、背景介绍

1.Seal简介
Seal是Java 2开发平台引入的一个新的安全特性。众所周知,在Unix下,当前目录不要轻易加入到PATH环境变量中去,否则黑客可以写一个文件名等同于系统命令的执行程序放在用户的目录下,从而使用户在自己目录下执行系统命令时落入圈套。同样,由于Java的开放性,黑客可以写一个具有同样名称的包,并将此包放在CLASSPATH的最前面。由于JAVA虚拟机是根据包在CLASSPATH中的排列顺序决定优先顺序的,所以如果用户需要调用这个名称的包,则将又落入圈套。

抛开黑客不说,在现实软件环境中,很有可能一个包的新旧版本共存在CLASSPATH中,这样如果新旧版本存在某些差异,则将带来不可预知的结果。这一点也恰恰是为什么微软的Win98用的时间越长就越不稳定的原因。

所以,从JAVA 2开始,Java引入了Seal(加封)安全机制。所有的JAR文件和包都可以选择是否需要加封,如果JAR文件被加封,则等价于JAR文件中的所有包被加封。一旦被加封的包中的某个类被引用,则规定任何以后对这个包中的成员的引用都必须在同一个JAR文件中。从而保证了版本的一致性。如果违反了上述Seal机制,则我们就将遇到Sealing Violation错误。

2.JAXP包与Sealing Violation错误
如果不是因为XML的流行和JAXP的出现,Java程序员中可能不会有太多人遇到上述Sealing Violation错误。首先是因为JDK中加封的包并不多,其次是因为这些加封的包用处并不广,从而很少会出现冲突现象。可是XML和JAXP改变这一切。XML毫无疑问的将会覆盖整个Internet,这样对XML进行Parse是必不可少的功能之一,Sun的JAXP软件包便可以实现对XML,XSL的Parse工作。让我们打开JAXP中jaxp.jar中的META-INF\manifest.mf文件,我们可以看到文件的第一行为:Sealed: true

也就是说,JAXP是加封过的,由此而带来的不可避免的问题就是,几乎任何一位使用JAXP的程序员都会遇到Sealing Violation这个问题。由于Sealing Violation在以往的Java编程中非常少见,这样大量缺少经验的程序员受困与此。

二、错误演示
以下我们将通过一个普通的Java程序来演示Sealing Violation错误的产生的大部分情形。

我们使用的Java程序是JAXP1.1中的一个很简单的范例程序,它的功能是利用XSLT转换XML,源程序如下:


import javax.xml.transform.*
import java.io.*

public class SimpleTransform
{
	public static void main(String[] args)
    throws TransformerException, TransformerConfigurationException, 
           FileNotFoundException, IOException
  {  
	TransformerFactory tFactory = TransformerFactory.newInstance();
	Transformer transformer = tFactory.newTransformer(new StreamSource("test.xsl"));
	transformer.transform(new StreamSource("test.xml"), 
			              new StreamResult(new FileOutputStream("test.out")));
  }
}

注意,虽然上述程序只显式import了javax.xml.transform包,但我们要注意这个程序对其它包的依存关系。在这里,JAXP的Transformer是通过SAX解析器解析XML的,这样在javax.xml.transform内部还要调用org.xml.sax*包。

为了造成冲突,我们使用了James Clark编写的XML解析引擎XT,这样我们的CLASSPATH环境变量如下(省略了jar文件的路径名):
.\; sax.jar; xt.jar; xalan.jar; jaxp.jar; crimson.jar

其中sax.jar和xt.jar属于XT软件包且都没有被加封,剩下三个属于JAXP软件包并且Sealed属性都为True。

现在让我们编译并运行上述范例,命令行的输出为:


Exception in thread "main" java.lang.SecurityException: sealing violation
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Unknown Source)
        at javax.xml.transform.TransformerFactory.newInstance(TransformerFactory.java:146)
        at SimpleTransform.main(SimpleTransform.java:10)

好了,错误出现了,原因很简单,XT的sax.jar中定义了org.xml.sax.*包,而JAXP中的xalan.jar中也定义了这个包,这样当我们的程序调用xalan.jar中的javax.xml.transform.*包时,需要对XML进行解析,这样便去找org.xml.sax包,由于sax.jar排在前面,这样javax.xml.transform.*中对org.xml.sax.*的调用实际上便发生在sax.jar文件里,而这恰恰违反了xalan.jar中Seal的约束。

三、解决方案

1.改变CLASSPATH顺序
改变jar文件在CLASSPATH中的顺序显然是最简单直接的想法。让我们把JAXP和XT换个位置,这样CLASSPATH变量如下:
.\; xalan.jar; jaxp.jar; crimson.jar; sax.jar; xt.jar

然后运行上述程序,OK,顺利通过。

真的就这么简单吗?从本质上来说确实是这样,可在实际编程中,我们还会遇到各种各样的现象,需要我们仔细的去发现背后的原因,让我们来看两个例子:

第一个例子:

既然sax.jar中的org.xml.sax.*是罪魁祸首,那好,让我们把它拿掉。这样CLASSPATH为:
.\; xt.jar; xalan.jar; jaxp.jar; crimson.jar

xt.jar中只有com.jclark.xsl.*包,可是Sealing Violation错误又出现了,为什么?原因当然还是org.xml.sax.*又冲突了。让我们看看xt.jar的meta-inf/manifest.mf文件:
Manifest-Version: 1.0
Main-Class: com.jclark.xsl.sax.Driver
Created-By: 1.2.1 (Sun Microsystems Inc.)
Class-Path: sax.jar

从最后一行我们可以看出,实际上sax.jar文件又被包含了进来。

第二个例子:

将CLASSPATH恢复为正确的格式:
.\; xalan.jar; jaxp.jar; crimson.jar; sax.jar; xt.jar

然后运行Tomcat3.2,结果在jsp和servlet调用上述范例程序中的功能时,又出现了Sealing Violation错误,这个原因同Tomcat有关,以下是Tomcat启动批处理文件中的几行:
if exist "%TOMCAT_HOME%\lib\ant.jar" set CP=%CP%;%TOMCAT_HOME%\lib\ant.jar
if exist "%TOMCAT_HOME%\lib\jasper.jar" set CP=%CP%;%TOMCAT_HOME%\lib\jasper.jar
if exist "%TOMCAT_HOME%\lib\jaxp.jar" set CP=%CP%;%TOMCAT_HOME%\lib\jaxp.jar
if exist "%TOMCAT_HOME%\lib\parser.jar" set CP=%CP%;%TOMCAT_HOME%\lib\parser.jar
if exist "%TOMCAT_HOME%\lib\servlet.jar" set CP=%CP%;%TOMCAT_HOME%\lib\servlet.jar
if exist "%TOMCAT_HOME%\lib\webserver.jar" set CP=%CP%;%TOMCAT_HOME%\lib\webserver.jar
:chkClasspath
if "%CLASSPATH%" == "" goto noClasspath
set CP=%CP%; %CLASSPATH%

好了,这样上述批处理命令的最后一行将Tomcat自己的jar文件加了进去,由于Tomcat3.2带的jaxp.jar还没有包含javax.xml.transform包,这样仍然使用的是后面的在CLASSPATH中的jaxp.jar,而这时Tomcat的parser.jar又扮演了冲突的角色,因为它定义了org.xml.sax.*。这种情况下的解决方法就是改动上述批处理文件,将CLASSPATH提前,即如下改动上述最后一句:
set CP=%CLASSPATH%; %CP%

从以上两个例子我们可以看出,虽然原因很简单,可很有可能被表面现象所隐藏,这就需要我们去考虑每个可能造成这个错误的地方。

2.更改JAR文件的Seal属性
这个解决方法看起来很暴力,直接将JAR文件中的meta-inf/manifest.mf中的Sealed:true改成Sealed:false。这种方法不但增加了安全隐患而且移植性差,你不但需要将你修改过的JAXP包随自己的软件包一块发布,而且还要保证它们列在CLASSPATH的最前面。这是下策,可是有时候又不得不如此,这一点恐怕Tomcat最有体会,因为Tomcat4.0就是采用了这种方法,以下是Tomcat4.0的Release-Notes-4.0.txt中的一段话:

WARNING: Tomcat 4.0 ships with a modified version of the JAXP/1.1 "jaxp.jar" and "crimson.jar" files from JAXP/1.1 final release. The "sealed" attribute has been removed from the manifest file for these two JARs, to avoid "package sealing violation" errors that were caused by them in a JDK 1.3 environment. You MUST NOT replace these files with a different (or later) release of JAXP, unless that later release has had the sealed attribute removed, or you will encounter "package sealing violation" errors when trying to use a different XML parser in a web application.

3.升级JDK
虽然JDK1.4现在还只是Beta版,可如果你喜欢这种解决方式的话,你可以安装JDK1.4 beta,在JDK1.4中,这个问题得到了解决。我并不太清楚JDK1.4对这个问题的解决方法,不过我猜想最有可能的方法是对CLASSPATH中Seal过的包进行优先处理,这样用户就无需考虑顺序问题。

四、总结
事实上,绝大部分关于JAXP的Sealing Violation的问题都是由于存在org.xml.sax.*这个包的多个实现,不仅仅是上面提到的两个JAR文件,还有j2ee.jar,xml.jar等。由此可见,Sun在加封JAXP时缺乏全盘的考虑。无论如何,从JDK1.4 beta版的情况来看,这个错误已经得到了Sun的重视,希望在不久的将来我们看到这个问题的完美解决。

参考资料

  • JAXP主站点http://java.sun.com/xml
  • XT主站点http://www.jclark.com/xml/xt.html
  • Tomcat主站点http://jakarta.apache.org/tomcat/

<

小小豆叮

BEA Weblogic多缓冲区溢出漏洞

发布日期: 2001年8月20日 更新日期: 2001年8月20日 受影响系统: Netscape Enterprise Webserver (NES) ----------------------------------- NSAPI Weblogic binaries are: NES for UNIX - libproxy.so NES for NT - proxy30.dll, proxy35.dll, proxy36.dll - Solaris with NES versions 3.0 to 4.1 - AIX with NES 3.6 - HP-UX 10.20 with NES version 3.6 - HP-UX 11.00 with NES version 3.6 - Windows NT with NES versions 3.0 to 4.1 Internet Information Server (IIS) --------------------------------- ISAPI Weblogic binaries are: IIS - iisproxy.dll - NT 4.0 with IIS 4.0 Apache Server ------------- ISAPI Weblogic binaries are: Apache for UNIX - mod_wl.so, mod_wl_ssl.so, mod_wl_ssl_raven.so - Solaris with Apache Server 1.3.9, 1.3.12 - Linux with Apache Server 1.3.9, 1.3.12 - HP-UX 11.00 with Apache Server 1.3.9, 1.3.12 - C2Net Stronghold/3.0 and Covalent Raven/1.4.3 - C2Net Stronghold/3.0 and Covalent Raven/1.4.3 - C2Net Stronghold/3.0 - Linux with Apache Server 1.3.9, 1.3.12 - HP-UX 11.00 with Apache Server 1.3.9, 1.3.12 漏洞描述: BEA Syetems Inc. Weblogic服务器提供措施将它集成到第三方web服务器。这是通过一个插件来完成的,它允许第三方web服务器代理向Weblogic服务器的请求。象BEA文档中描述的那样,Netscape Enterprise Server、IIS和Apache均以动态链接库的形式支持插件。这些web服务器通过配置可以将请求servlets和JSP文件重定向到运行在同一个或不同主机的Weblogic服务器。BEA Weblogic服务器提供的这些插件存在几个缓冲区溢出,允许远程攻击者执行代理web服务器所在系统上的任意代码。 测试方法: 此程序只供研究使用,可能会对系统带来危害,不得用于其它用途!否则后果自负! 测试使用iPalnet Webserver Enterpris 4.1作为代理Web服务器。按照BEA文档,使用下面的servername/config/obj.conf文件将它配置成将.JSP请求重定向到不同主机上的Weblogic服务器: obj.conf: [... text deleted ...] [...] Service method=(GET|HEAD|POST|PUT) type=text/jsp fn=wl-proxy\ WebLogicHost=weblogic WebLogicPort=7001 PathPrepend=/jspfiles [....] [....] 上述配置将指导iPlanet Webserver使用提供的库(libproxy.so),将.JSP文件请求重定向到Weblogic服务器,它侦听在主机的7001/tcp端口。字符串'/jspfile'将插在送到Weblogic主机URL的前面。 例如,请求'http://webserver/test.jsp?my_parameter'将变成'http://weblogic:7001/jspfiles/test.jsp?my_parameter',然后发送到Weblogic服务器。 处理被代理的请求是由libproxy.so的wl_proxy函数进行的。在位置wl_proxy+1812,将调用strcat()连接obj.conf文件中指定的PathPrepend参数,这里没有对栈中分配的目的缓冲区进行边界检查。如果请求长度大于2100个字符,一个缓冲区溢出条件就会触发,任意代码就可以以运行代理web服务器用户的身份执行。 漏洞的攻击是困难的,因为溢出后某些放在栈中的自动变量被破坏,将会在执行ret指令前在wl_proxy+1896处被访问。 而且,代理web服务器将对收到的请求进行某些长度检查,所以不太可能发送任意长度的请求。然而代理web服务器的这个长度限制是充分允许一个攻击者进行攻击的。 攻击时,并不需要设置PathPrepend参数。 示例代码如下: $ perl -e 'print "GET http://webserver/test.jsp?";print "A"x2200;\ print " HTTP/1.0\n\n"'|nc weblogic 80 解决方法: 对于所有OS平台上独立版本或者作为BEA WebLogic Enterprise 5.1一部分的BEA WebLogic Server 和 Express 5.1.x、4.5.x,升级集成于第三方web服务器的代理插件。软件包(800kb)。 文件包括用于Netscape Enterprise Server (NES),MicrosoftInternet Information Server (IIS),和Apache Web servers代理插件的升级版本。保存计算的URI路径的缓冲区最大尺寸是2048字节。如果一个计算URI路径比缓冲区的尺寸大,代理插件就会按照HTTP 1.1规范向客户报告错误“414 - Request-URI Too Long”。 <

小小豆叮

软件工程能帮多大忙?

一、思想各不相同,行为却是一样的 中国的程序员多数贫寒而有大志,看到国产软件行业凋敝,拯救中国软件业的愿望便由此生根发芽。我但是,我越做就越失望。 为什么?因为胡说八道胡乱指挥“项目经理”的太多,有寡不敌众的感觉吗?不是的。胡乱指挥的人的确多,什么中文软件,什么自主知识产权,什么扶持软件产业,什么硅谷梦想,的确寡不敌众。 但这根本不重要!反过来,即使这个社会人人都是比尔.盖茨,安迪.葛洛夫,对中国的软件业的繁荣也于事无补! 对“振兴民族软件业,知识救国”的失望,是因为我越来越意识到:人的思想和行为是两回事,人的思想可以有很大的差别,但他们的行动都是一样的。 去买麦当劳,您很想守秩序排队,还想隔得很开,不要身贴身——身贴身完全没必要。但如果是那样,您就一直远远站着吧,所有人都以为您只是在等人。 排队的好处他们不懂吗?可能不懂,但是即使懂,好象您自己就懂,但您也照样得挤进去。不挤不得食。您和他们的思想境界或许有巨大的差异,但身历其境,您的行动跟他们的都是一样的。你或许曾经迟疑,那你就已经吃亏。 相信每个读者都遇到过��“项目经理”们都宣称要在项目实施软件工程。什么ISO90002,CMM,RUP。但行动上呢?他们的真实去做的是其实是很小的,仍然是无文挡,无控制,无注释的“三无产品”。如果帮助他们更彻底地履行他们的誓言,好意告诉他们,瀑布式的做法风险很大,软件需求必须定义清楚,团队必须沟通,他们就会拿出合同说时间不等人过期要罚款。 说,可以有很多套;做,则只有一套。思想上,大家可以天马行空,各执己见,乱说一通,然后一口咬定——不要紧,没代价。但是,如果付诸行动,则要承受扎扎实实的损失。口头上我们势必要跟对手过不去,但行动上我们很难跟自己的钱包过不去。结果,在一定的约束条件下,可观察的人的行为都是非常相近的,至少比起他们内心想法的差异而言是如此。正因如是,思想上的伪劣商品,比实物世界的伪劣商品多得多!   二、“做了什么”有别于“为什么那么做” 我们永远不知道BILL.GATES那么作为什么便获得了成功,说“不知道”,那是因为很多失败的人也是那么做的。有人想模仿微软的成功经验,但无数失败的人也是那么做的,而另外很多不那么做的人却也同样取得了成功。 我们不知道微软的开发方法为什么能使得WINDOWS独步天下?是霸权,是垄断,是市场的需求?我们不知道。我们只知道,只有那样,开发的成本才会最低,开发的质量才会很高。 贫穷的印度为什么会有那么好的软件工程和软件管理?是他们会英语,是政府扶持,是劳动力便宜?我们不知道。我们只知道,在那样条件下,要靠软件来发展国民经济,那么CMM是软件出口的前提,好的软件管理是公司赚取利润的保证。至于这些不懂软件技术的印度项目经理为什么会发现并坚持这个选择,我们不知道。 应该开诚布公地告诉读者:软件工程不知道怎样把一个好的idea变成举世闻名的软件。要成功,那么运气、努力和经验,市场环境,就缺一不可,但是,软件工程既没有兴趣、也没有能力逐一掂量这些因素、逐一对这些因素论功行赏。软件工程只是指出一个软件企业要符合什么条件才更有机会在竞争中生存和发展。但怎样才能达到这个点,软件工程就束手无策,无言以答。 软件工程是解释软件开发过程行为的经验科学。它解释的对象是可观察的企业的行为,而不是不可观察的企业管理人员的动机、愿望、理想、观点和感受。软件工程只是解释——也仅能解释——一个成功的软件企业“做了什么”,而无法解释——也没有兴趣解释——一个成功的软件企业“怎么会这么做了”。 这类似于我们知道一个奥运冠军是怎样的,但这种知识跟如何才能训练出一个奥运冠军是两回事;我们知道谁谁谁是诺贝尔奖得主,但这有别于要我们培养一个诺贝尔得主;我们知道一个健康、聪明的男婴是怎样的,但如何才能生育一个健康、聪明的男婴,则是另外一回事。 三、通向繁荣之路漫漫 一个在大学教书的软件工程教授的言论,与他如果担任的项目经理和软件企业老板时的行为,是有很大不同的。孰好孰坏不是我们这里讨论的目的,我只是指出人们的行为依赖于他所处的约束条件,而与他的或许五花八门的观点和理想没有多大的关系。 不良的软件企业管理为什么会施行?这往往是在特定约束条件下,人们角逐自身利益的结果。他们要那样做,才去寻找自圆其说的理由,而不是他们先找到合理的理论后,才根据这个理论来行事。我们常常会颠倒了前后因果。是企业管理者想赶工期,才选择了“三无开发”,而不是有了项目经理有了“三无开发”才说服老板加快进度。不是软件企业的老板不知道“瀑布式开发”风险巨大才搞瀑布式开发,而是他们为了生计必须这样做,才选中了那些早就被彻底驳倒的理论和至今一知半解的项目管理者。不是BILL.GATES精通了软件工程才发财,而是他发了财后,软件专家才出来说:“那就是软件工程,象他那样就能发财。” Rational公司的软件工具盛行于世界,还邀请微软ORACLE来助阵,但我们的项目经理们听进去了他多少? 大批的专家去印度,爱尔兰,以色列,去学习考察软件企业的管理,但是我们软件产业作坊式生产今天改观了多少? 华为,金碟邀请IBM,微软来出谋划策,即使这些计谋都是正确的,但那也不比建议搞定信息产业部来的更好。人在江湖,知易行难! 我们绝大多数人都懂得牛顿定律,但这一知识除了帮助我们更好地理解世界外,并没有对我们的个人生活产生多大的帮助。我们走路尚且没有跌倒,我们抛起儿子尚且不会忘记接住,恐怕不能归功于对牛顿定律的认识。是的,牛顿定律在工程技术方面极大地改善了我们的生活,但那是因为我们有足够的资源,可供反复试验的缘故。 但软件工程却没有那么幸运。软件工程给我们带来了前所未有的智力上的乐趣,我们一旦掌握了软件工程的基本原理,我们就能象走路、骑车、游泳、说话一样自然地运用,用来理解软件开发中的现象。但我们每个企业的发展的顺流逆流,却似乎有着它本来的走向。 在另一方面,人们却不能象做物理试验那样廉价地在一个企业中做软件工程的试验。一个软件企业管理理念的成败,往往不取决于它理论上是否成立,而只能取决于企业家、政治家、程序员与经理、官员,客户的共同博弈。 软件工程能帮多大忙?软件工程确实提高了个人在软件能力上的洞察力,但它无力支配各种现实力量的角逐。 <

小小豆叮

比较两种php调用Java对象的方法

Java语言功能强大,因此在许多情况下在php中来调用Java的功能将十分有用。在php中调用Java语言有两种方法,一种是使用php中的Java扩展模块,另一种是使用minij2ee应用服务器提供的SJOP协议实现。下面我们来比较一下这两种方法各自的特点。 1.php的Java模块 php发布版中包含一个Java扩展模块,可以用来调用Java对象,例如: <?php $system=new Java("java.lang.System"); print "Java version=".$system->getProperty("java.version")."
\n"; ?> 使用这种方法的优点是比较方便,只要用new Java()来创建一个Java对象,就可以同php类一样来调用Java对象。但是这种方法也有以下明显的缺点: 1.由于php的Java模块根据php的数据类型选择最适合的Java方法,因此无法调用Java过载的函数。 2.php的Java模块将在当前Web Server的进程中载入JVM(Java虚拟机),因此系统开销极大,影响Web Server进程的执行效率。 3.在某些操作系统和Web Server环境中,php的Java模块将使Web Server进程僵死。见http://www.php.net/bugs.php?id=6122。 由于这些原因,php的Java模块一直无法应用到实际的的软件系统中。 2.minij2ee应用服务器SJOP协议实现 在介绍minij2ee应用服务器SJOP协议实现之前,先简单介绍一下minij2ee应用服务器。minij2ee应用服务器是第一款支持php的J2EE应用服务器产品,使php能够用于开发企业级应用系统。SJOP全称是Sample Java ORB Protocol(简单Java对象请求代理协议),是一种简单高效的对象请求代理协议。比如: \n"; ?> minij2ee应用服务器实现SJOP协议的主要目的是使php中能够访问EJB企业级组件,因此minij2ee提供了一个EJB-PHP编译器,可以把EJB组件编译成php的类,使php程序中能够方便的调用EJB组件,例如: <?php require("Cart.php"); file://Cart.php是编译Cart EJB后生成的Cart EJB的php类定义。 $home=new CartHome(); file://创建EJB的Home接口。 $objref=$home->create($cart_name); file://创建Cart EJB。 $cart=new Cart($objref); $cart->add("some goods"); file://向购物车中添加一个物品。 ?> 使用minij2ee应用服务器的php支持,就可以开发出基于php和J2EE技术的,面向对象的,稳定高效的企业级应用系统。关于更详细的PHP-J2EE技术的介绍,请访问http://www.minij2ee.com/document/document_index_6_0.html。 <

小小豆叮

浅析Java的流

由于手头有个项目需要用的Java的输入输出操作, 所以,啃了几天书,对Java的流技术有些了解,不过,还是有很多不是很明白,下面是我看书整理的一些笔记,严格的说应该不是原创,我贴出来只是希望对大家有一些帮助 1、Java拥有一个完整的流类型,总数超过60,而四个抽象类构成了这些类的基础 InputStream和OutputStream读写以单字节为基础的字节数据流; Reader和Writer读写以双字节的Unicode字符为基础的字节数据流; 2、二进制数据的读写 DataInputStream和DataOutputStream 可以完成对所有基本Java类型的读写; FileInputStream和FileOutputStream 对一个磁盘文件涉及的数据流进行输入输出处理,是字节级的读写操作; 3、Java的流类型中按职责分有两类: (1)从文件或者一些特殊地方读入字节数据(如FileInputStream); (2)把这些字节数据“组装”成更有用的数据类型(如DataInputStream、PrintWriter) 要完成具体操作时,需要对流进行组装处理 4、Java的流不具备预读和缓冲功能,需要额外处理 BufferInputStram对流进行缓冲处理; PushbackInputStream对流进行预读处理; 5、RandomAccessFile随机存取文件,这个类同时实现了DataInput和DataOutput; 6、文本流,即通过主机环境的工具,人眼能够辨别的文件内容 由于windows的字串写入是ASCII格式的,将Unicode编码写进一个文本文件,用主机环境的工具,人眼无法辨别文件内容,Java提供一套流过滤器弥补Unicode编码和本机操作系统采用字符编码的裂缝。 7、InputStreamReader 特殊的编码方案输入字节流转换为Unicode字符;这个类采用主机默认编码,在windows中使用ISO 8859-1编码,即“ASNI”编码; OutputStreamWriter 把Unicode字符流转换为特殊的编码方案字节流 8、FileReader和FileWriter 对磁盘文件进行读写,已经弥补了Unicode编码和本机编码的裂缝,可以直接使用 9、写文本输出: 二进制写入数据:DataOutputStream; 文本格式写入数据:PrintWriter; 读文本输入: 二进制读入数据:DataInputStream; 文本格式读入数据:BufferReader; 10、其他一些流 如:ZipInputStream和ZipOutputStream 对ZIP文件进行读写操作 欢迎访问我的主页http://flying965.myrice.com <

小小豆叮

文本编辑器源文件

/版权所有helj import java.awt.*; import java.io.*; import java.util.*; //W_color;C_str;C_file;C_back;W_find;W_fond interface I_Method { public void setQuit(); public void setfont(Font f); public void setcolor(Color c); public void Find(String find,int dire); } class D_color extends Dialog { private Button OK,Cancel; private I_Method mp; private Rectangle test; private Color current= Color.black; private Color []colors; private Color []colors1; private Color []colors2; private int strlen= 250; public D_color(Frame fr,I_Method mp,int x,int y) { super(fr,"调色板",true); this.mp= mp; colors= new Color[strlen]; colors1= new Color[strlen]; colors2= new Color[strlen]; for (int i = 0; i < strlen; i++) { float h = ((float)i)/((float)strlen); colors[i] = new Color(Color.HSBtoRGB(h,1.0f,1.0f)); } for (int i = 0; i < strlen; i++) { float h = ((float)i)/((float)strlen); colors1[i] = new Color(Color.HSBtoRGB(1.0f,h,1.0f)); } for (int i = 0; i < strlen; i++) { float h = ((float)i)/((float)strlen); colors2[i] = new Color(Color.HSBtoRGB(1.0f,1.0f,h)); } setLayout(null); OK= new Button("确定"); Cancel= new Button("取消"); OK.reshape(320,100,80,30); add(OK); Cancel.reshape(320,150,80,30); add(Cancel); test= new Rectangle(0,0,300,250); reshape(x/2-210,y/2-140,420,280); show(); } public void update(Graphics g) { int y= 0; for (int i = 0; i < strlen; i++) { g.setColor(colors[i]); g.fillRect(0,y,100,1); y+=1; } y= 0; for (int i = 0; i < strlen; i++) { g.setColor(colors1[i]); g.fillRect(100,y,100,1); y+=1; } y= 0; for (int i = 0; i < strlen; i++) { g.setColor(colors2[i]); g.fillRect(200,y,100,1); y+=1; } g.setColor(current); g.fillRect(330,30,60,60); } public void paint(Graphics g) { update(g); } public boolean mouseMove(Event evt,int x,int y) { if(test.inside(x,y)) { Graphics g1= getGraphics(); //g1.drawImage(back,oldx,oldy); //g1.clipRect(x-3,y-3,6,6); g1.setColor(Color.white); g1.drawArc(x-3,y-3,6,6,60,360); g1.drawLine(x-3,y,x+3,y); g1.drawLine(x,y-3,x,y+3); repaint(); //oldx= x-3; //oldy= y-3; return true; } return false; } public boolean mouseDown(Event evt,int x,int y) { if((x>=0)&&(x<=100)) { current= colors[y]; System.out.println(y); getGraphics().setColor(current); getGraphics().fillRect(330,30,60,60); return true; } if((x>100)&&(x<=200)) { current= colors1[y]; getGraphics().setColor(current); getGraphics().fillRect(330,30,60,60); return true; } if((x>200)&&(x<=300)) { current= colors2[y]; getGraphics().setColor(current); getGraphics().fillRect(330,30,60,60); return true; } return false; } public boolean action(Event evt,Object arg) { if(evt.target instanceof Button) { if(evt.target== OK) { mp.setcolor(current); dispose(); return true; } if(evt.target== Cancel) { dispose(); return true; } } return super.action(evt,arg); } } class W_quick extends Window { MainF fr; private Dimension sz; private int w,h; private int fw,fh; private FontMetrics fm; private int index=0; public W_quick(MainF fr,I_Method qm,int x,int y) { super(fr); this.fr= fr; setBackground(Color.lightGray); } public void show(int x,int y) { repaint(); reshape(x,y,100,160); show(); } private void draw_uplines(Graphics g,int x,int y,int w1,int h1) { g.setColor(Color.white); g.drawLine(x,y,x+w1-1,x); g.drawLine(x,y,x,y+h1-1); g.setColor(Color.black); g.drawLine(x+w1-1,y-1,x+w1-1,y+h1-1); g.drawLine(x+w1-2,y+1,x+w1-2,y+h1-2); g.drawLine(x+1,y+h-2,x+w1-2,y+h1-2); // g.drawLine(x,y+h1-1,x+w1-1,y+h1-1); // g.setColor(Color.black); // g.drawLine(x,y+h1-1,x+w1-1,y+h1-1); } public void paint(Graphics g) { sz= size(); w= sz.width; h= sz.height; // g.setFont(Ffont); fm= g.getFontMetrics(); fh=fm.getHeight(); fw= fm.charWidth('C'); g.setColor(Color.gray); g.drawRect(10,(index-1)*fh+6,w-20,fh); // g.setColor(Color.lightGray); // g.drawRect(0,0,w,h); draw_uplines(g,1,1,w-1,h-1); g.setColor(Color.black); g.drawString("Cut",13,fh); g.drawString("Copy",13,2*fh); g.drawString("Paste",13,3*fh); g.drawString("Delete",13,4*fh); } public boolean mouseMove(Event evt,int x,int y) { for(int i=1;i<=4;i++) if((y>(i-1)*fh)&&(y<(i*fh))) { index= i; repaint(); } return super.mouseMove(evt,x,y); } public boolean mouseDown(Event evt,int x,int y) { if(y>0&&yfh&&(y<(2*fh))) { fr.eitem.enable(false); fr.eitem1.enable(false); fr.eitem3.enable(false); fr.eitem2.enable(true); hide(); return true; } if((y>(2*fh))&&(y<(3*fh))) { fr.current_t.adds(); fr.eitem2.enable(false); fr.eitem.enable(false); fr.eitem1.enable(false); fr.eitem3.enable(false); hide(); return true; } if((y>(3*fh))&&(y<(4*fh))) { fr.current_t.dels(); fr.eitem3.enable(false); fr.eitem.enable(false); fr.eitem1.enable(false); fr.eitem2.enable(false); hide(); return true; } return super.mouseDown(evt,x,y); } } class ptest extends Panel { String test= "ABCDabcd"; D_font ff; public ptest(D_font ff) { this.ff= ff; repaint(); } private void draw_downlines(Graphics g,int x,int y,int w,int h) { g.setColor(Color.white); g.drawLine(x+1,y+h-1,x+w-1,y+h-1); g.drawLine(x+w-1,y+h-1,x+w-1,y+1); g.setColor(Color.black); g.drawLine(x,y,x,y+h); g.drawLine(x+1,y+1,x+1,y+h-1); g.drawLine(x,y,x+w,y); g.drawLine(x+1,y+1,x+w-1,y+1); } public void rep() { repaint(); } public void paint(Graphics g) { Dimension sz= size(); Point pt= location(); g.setColor(Color.darkGray); g.drawRect(0,0,sz.width-2,sz.height-2); g.setColor(Color.white); g.drawLine(1,1,1,sz.height-3); g.drawLine(1,1,sz.width-3,1); g.drawLine(0,sz.height-1,sz.width-1,sz.height-1); g.drawLine(sz.width-1,0,sz.width-1,sz.height-1); draw_downlines(g,15,10,sz.width-30,sz.height-20); g.clipRect(16,11,sz.width-31,sz.height-21); g.setColor(Color.black); g.setFont(ff.bf); g.drawString(test,20,sz.height-21); } } class D_font extends Dialog { public Font bf; private Button OK,Cancel; private Label l1,l2,l3; private String items1[]={"Helvetica","Courier","TimesRoman","Dialog"}; private String items2[]={"PLAIN","BOLD","PLAIN+BOLD","ITALIC","BOLD+ITALIC"}; private int[] style= new int[5]; private String items3[]={"8","9","10","11","12","14","16","18","20","22","24","26","28","36","48","72"}; private TextField f1,f2,f3; private List ll1,ll2,ll3; private ptest test; private I_Method im; public void init() { style[0]= Font.PLAIN; style[1]= Font.BOLD; style[2]= Font.PLAIN+Font.BOLD; style[3]= Font.ITALIC; style[4]= Font.BOLD+Font.ITALIC; } public D_font(Frame fr,I_Method qm,int x,int y) { super(fr,"字体",true); init(); setFont(new Font("TimesRoman",Font.PLAIN,14)); im= qm; GridBagLayout gridBag= new GridBagLayout(); GridBagConstraints c= new GridBagConstraints(); setLayout(gridBag); c.insets.left= 4; c.insets.right= 4; c.insets.bottom= 1; c.insets.top= 1; l1= new Label("字体:",Label.LEFT); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=0; c.gridy=0; c.gridwidth=2; c.gridheight =1; gridBag.setConstraints(l1,c); add(l1); l2= new Label("字体样式:",Label.LEFT); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=2; c.gridy=0; c.gridwidth=1; c.gridheight =1; gridBag.setConstraints(l2,c); add(l2); l3= new Label("大小:",Label.LEFT); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=3; c.gridy=0; c.gridwidth=1; c.gridheight =1; gridBag.setConstraints(l3,c); add(l3); OK= new Button("确定"); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=4; c.gridy=1; c.gridwidth=1; c.gridheight =1; gridBag.setConstraints(OK,c); add(OK); f1= new TextField(); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=0; c.gridy=1; c.gridwidth=2; c.gridheight =1; gridBag.setConstraints(f1,c); add(f1); f2= new TextField(); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=2; c.gridy=1; c.gridwidth=1; c.gridheight =1; gridBag.setConstraints(f2,c); add(f2); f3= new TextField(); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=3; c.gridy=1; c.gridwidth=1; c.gridheight =1; gridBag.setConstraints(f3,c); add(f3); Cancel= new Button("取消"); c.gridwidth= GridBagConstraints.REMAINDER; c.fill= GridBagConstraints.BOTH; c.weightx= 0.0; c.weighty= 0.0; c.gridx=4; c.gridy=2; c.gridwidth=1; c.gridheight =1; gridBag.setConstraints(Cancel,c); add(Cancel); ll1= new List(); for(int i=0;i=cus.index;i--) text[i]= text[i-1]; text[cus.index]= ch; totle++; sc.length[cus.liney]+= 1; repaint(); if(ch=='\n') { cus.linex= 1; cus.liney+= 1; cus.cusorx= 3; cus.cusory= cus.liney*cus.h; cus.index++; }else keycusor(1,0); } public void adds() { totle+= sel.count; int i; for(i= totle-1;i>(cus.index+sel.count);i--) text[i]= text[i-sel.count]; for(int j= sel.count;j>0;j--,i--) text[i]= sel.sels[j-1]; sel.count= 0; repaint(); } public void del(int de) { if(de<0) { for(int i= cus.index-1;i0) for(int i= cus.index;i0) { int end= sel.index+1; for(int i= end;icus.index) { if((i< totle)&&(i<=sel.index)&&(i>=cus.index)) { sel.sels[ji]= text[i]; ji++; g.setColor(Color.blue); g.fillRect(paint_x,paint_y-fh,fw+1,fh); g.setColor(Color.white); g.drawChars(text,i,1,paint_x,paint_y); } }else if((i< totle)&&(i>=sel.index)&&(i<=cus.index)) { sel.sels[ji]= text[i]; ji++; g.setColor(Color.blue); g.fillRect(paint_x,paint_y-fh,fw+1,fh); g.setColor(Color.white); g.drawChars(text,i,1,paint_x,paint_y); } paint_x+= fw; } } public void home() { cus.linex= 1; int i=0; cus.index=0; for(;i< cus.liney+sc.offsety-1;i++) cus.index+= sc.length[i]; cus.index+=cus.linex-1+sc.offsetx; cus.cusorx= 3; cus.paint(getGraphics()); } public void end() { cus.linex= sc.length[cus.liney-1]-1; int i=0; cus.index=0; for(;i< cus.liney+sc.offsety-1;i++) cus.index+= sc.length[i]; cus.index+=cus.linex-1+sc.offsetx; cus.cusorx= (cus.linex-1)*cus.w+3; cus.paint(getGraphics()); } public void pagedown() { if(sc.maxline>2*sc.h) { sc.offsety+= sc.h; fr.vert.setValue(sc.offsety); draw(getGraphics()); fr.vert.setValue(sc.offsety); } } public void pageup() { if((sc.offsety-sc.h)>=0) { sc.offsety-=sc.h; draw(getGraphics()); fr.vert.setValue(sc.offsety); } } public void scrollpaint(int x1,int x2) { sc.offsetx= x1; sc.offsety= x2; // getGraphics().translate(-x1,-x2); draw(getGraphics()); } public void setFont(Font cuf) { Ffont= cuf; //paint= true; repaint(); } public void setColor(Color col) { Bcolor= col; // paint= true; repaint(); } public void rep() { repaint(); } private void draw(Graphics g) { findtext= new String(text,0,totle); int findi= findcount; int aa= 0;//用于记录starti int c1= 0;//记录行的数目 int c2= 0;//记录每行的字节数 int c3= 0; //最大列数 g.setFont(Ffont); g.setColor(Color.white); g.fillRect(3,2,sz.width,sz.height); g.setColor(Bcolor); paint_x= 3-(int)(sc.offsetx*cus.w); paint_y= fh-(int)(sc.offsety*fh); for(int i=0;i< totle;i++) { if(text[i]=='\n') { paint_x = 3-(int)(sc.offsetx*cus.w); paint_y+=cus.h; //i++; sc.length[c1]= c2+1; c1++; if(c2>c3) c3= c2; c2= 0; }else if(i< totle) { fw= fm.charWidth(text[i]); g.setColor(Bcolor); g.drawChars(text,i,1,paint_x,paint_y); if(i== findindex[findi]&&bfind) { if(findi>0) findi--; g.setColor(Color.blue); g.fillRect(paint_x,paint_y-fh+2,fw+1,fh); g.setColor(Color.white); g.drawChars(text,i,1,paint_x,paint_y); } paint_x+= fw; c2++; } } c1+= 1; sc.maxline= c1; cus.paint(g); if(c3> sc.w) fr.horz.setValues(0,sc.w,0,c3-sc.w); if(c1> sc.h) fr.vert.setValues(0,sc.h,0,c1-sc.h); } public void update(Graphics g) { sz= size(); g.setColor(Bcolor); g.setFont(Ffont); fm= g.getFontMetrics(); cus.h= fh= fm.getHeight(); cus.w= fw= fm.charWidth('A'); sc.h= (int)((sz.height-3)/fh)+1; sc.w= (int)((sz.width-3)/fw)+1; draw_downlines(g,0,0,sz.width,sz.height); //g.clipRect(3,2,sz.width,sz.height); if(select) { drawselect(g); select= false; }else draw(g); } public void paint(Graphics g) { update(g); } public boolean mouseEnter(Event evt,int x,int y) { fr.setCursor(2); return true; } public boolean mouseExit(Event evt,int x,int y) { fr.setCursor(0); return true; } public void find(String find,int dire) { bfind= true; findcount =0; String sss= findtext; for(int i=0;i<100;i++) findindex[i]= 0; int j=0; while(true) { if(sss.indexOf(find)==-1) break; findindex[j]= sss.lastIndexOf(find); sss= sss.substring(0,findindex[j]); j++; findcount++; } repaint(); selold= true; } } public class MainF extends Frame implements I_Method { private W_quick Lshow; private FileDialog openDialog; private FileDialog saveDialog; public Scrollbar horz,vert; private MenuBar menuBar; private Panel File_panel,Back_p; private Panel Back_panels[]; private int screenw,screenh; private Color Fcolor= Color.black; private Font Ffont= new Font("TimesRoman",Font.PLAIN,14); private int Fwidth,Fheight; private int[] bookmarks= new int[20]; //************** public P_area current_t; private int i1; private int iii=0; private boolean insert= false; boolean begin= false;//用于标志是否开始选中 boolean end= false; private int sel1= 0; private int sel2= 0; MenuItem eitem; MenuItem eitem1; MenuItem eitem2; public MenuItem eitem3; MainF(String title) { super(title); Font font= new Font("TimesRoman",Font.BOLD,20); setFont(font); setBackground(Color.lightGray); horz= new Scrollbar(Scrollbar.HORIZONTAL,0,0,0,0); vert= new Scrollbar(Scrollbar.VERTICAL,0,0,0,0); // show(); addMenu(); init(); Lshow= new W_quick(this,this,0,0); openDialog= new FileDialog(this,"Open File...",FileDialog.LOAD); saveDialog= new FileDialog(this,"Save File...",FileDialog.SAVE); } private void init() { setLayout(new BorderLayout()); File_panel= new Panel(); File_panel.setBackground(Color.lightGray); Panel pp= new Panel(); pp.setLayout(new BorderLayout(2,2)); current_t= new P_area(this,Ffont,Fcolor,"aa","aa"); pp.add("Center",current_t); pp.add("South",horz); pp.add("East",vert); add("South",File_panel); add("Center",pp); Dimension screen_size= Toolkit.getDefaultToolkit().getScreenSize(); screenw=screen_size.width; screenh= screen_size.height-16; reshape(16,16,screen_size.width-32,screen_size.height-32); show(); } private void addMenu() { menuBar= new MenuBar(); setMenuBar(menuBar); Menu menu= new Menu("File"); Menu menu1= new Menu("Edit"); Menu menu2= new Menu("Option"); menuBar.add(menu); menuBar.add(menu1); menuBar.add(menu2); MenuItem fitem= new MenuItem("New"); menu.add(fitem); MenuItem fitem1= new MenuItem("Open"); menu.add(fitem1); MenuItem fitem2= new MenuItem("Close"); menu.add(fitem2); menu.addSeparator(); MenuItem fitem3= new MenuItem("Save"); menu.add(fitem3); MenuItem fitem4= new MenuItem("Save As"); menu.add(fitem4); menu.addSeparator(); MenuItem fitem5= new MenuItem("Quit"); menu.add(fitem5); menu1.add(new MenuItem("Undo")); menu1.add(new MenuItem("Redo")); menu1.addSeparator(); eitem= new MenuItem("Cut"); menu1.add(eitem); eitem.enable(false); eitem1= new MenuItem("Copy"); eitem1.enable(false); menu1.add(eitem1); eitem2= new MenuItem("Paste"); menu1.add(eitem2); eitem2.enable(false); menu1.addSeparator(); eitem3= new MenuItem("Delete"); menu1.add(eitem3); eitem3.enable(false); MenuItem eitem6= new MenuItem("Bookmark"); menu1.add(eitem6); menu1.addSeparator(); MenuItem eitem4= new MenuItem("Find"); menu1.add(eitem4); MenuItem eitem5= new MenuItem("Repace"); menu1.add(eitem5); MenuItem oitem= new MenuItem("Color"); menu2.add(oitem); MenuItem oitem1= new MenuItem("Font"); menu2.add(oitem1); } public void setQuit() { this.dispose(); System.exit(0); } public void setfont(Font f) { Ffont= f; current_t.setFont(f); repaint(); } public void setcolor(Color c) { Fcolor= c; current_t.setColor(c); repaint(); } public void paint(Graphics g) { if(Ffont!= null) g.setFont(Ffont); } public void read(String fn,String fd) { current_t.setname(fn,fd); String line= null; String input= null; File s= new File(fd,fn); FileInputStream in= null; DataInputStream datain= null; try { in= new FileInputStream(s); datain = new DataInputStream(in); }catch(Exception e) { System.out.println("Unable to open file"); return; } try{ while((line=datain.readLine())!= null) { line+= "\n"; if(input== null) input= line; else input+= line; } }catch(IOException e){ System.out.println("Error in reading file"); } current_t.open(input); try{ in.close(); }catch(IOException e){ System.out.println("e"); } } public void write(String fn,String fd) { FileOutputStream out= null; File s= new File(fd,fn); try{ out= new FileOutputStream(s); }catch(Exception e) { System.out.println("Unable to open file"); return; } PrintStream psOut=new PrintStream(out); psOut.print(current_t.getText());/// try{ out.close(); }catch(IOException e){ System.out.println("e"); } } public boolean keyDown(Event evt,int key) { System.out.println(key); switch(key) { case 1004://up current_t.keycusor(0,-1); return true; case 1005://down current_t.keycusor(0,1); return true; case 1006: current_t.keycusor(-1,0); return true; case 1007: current_t.keycusor(1,0); return true; case 8: current_t.del(-1); return true; case 127: current_t.del(1); return true; case 1001://end current_t.end(); return true; case 1000://home current_t.home(); return true; case 1002: current_t.pageup(); return true; case 1003: current_t.pagedown(); return true; } // if(!current_t.border) // current_t.addchar((char)evt.key); // else current_t.add((char)evt.key); return true; } public boolean mouseDown(Event evt,int x,int y) { //****单击右键*******************不完整 eitem.enable(false); eitem1.enable(false); eitem3.enable(false); current_t.bfind= false; if((evt.modifiers&Event.META_MASK)>0) { Lshow.show(evt.x,evt.y); return false; }else { Lshow.hide(); begin= true; current_t.movecusor(evt.x,evt.y); if(current_t.selold) { current_t.selold= false; current_t.selrep(); } } if(end) { // current_t.rep(); // current_t.paint =true; end= false; } return false; } public boolean mouseDrag(Event evt,int x,int y) { eitem.enable(true); eitem1.enable(true); eitem3.enable(true); current_t.drag(evt.x,evt.y); //} return true; } public boolean handleEvent(Event evt) { if(evt.id== Event.WINDOW_DESTROY) { W_quit aa= new W_quit(this,this); aa.show(); return true; } switch(evt.id) { case 601: case 602: case 603: case 604: case 605: //current_t.offsety= (vert.getValue()); // current_t.offsetx= (horz.getValue()); // current_t.paint= true; // current_t.rep(); current_t.scrollpaint(horz.getValue(),vert.getValue()); vert.setValue(current_t.sc.offsety); horz.setValue(current_t.sc.offsetx); return true; } return super.handleEvent(evt); } public boolean action(Event evt,Object arg) { String filename; if(evt.target instanceof MenuItem) { if(evt.arg.equals("New")) { return true; } if(evt.arg.equals("Open")) { openDialog.show(); String fn= openDialog.getFile(); String filedir= openDialog.getDirectory(); read(fn,filedir); return true; } if(evt.arg.equals("Save")) { write(current_t.filename,current_t.filedir); return true; } if(evt.arg.equals("Save As")) { saveDialog.show(); filename= saveDialog.getFile(); String filedir= openDialog.getDirectory(); write(filename,filedir); return true; } if(evt.arg.equals("Quit")) { System.exit(0); } if(evt.arg.equals("Cut")) { current_t.dels(); eitem.enable(false); eitem1.enable(false); eitem3.enable(false); eitem2.enable(true); return true; } if(evt.arg.equals("Paste")) { current_t.adds(); eitem2.enable(false); eitem.enable(false); eitem1.enable(false); eitem3.enable(false); return true; } if(evt.arg.equals("Copy")) { eitem.enable(false); eitem1.enable(false); eitem3.enable(false); eitem2.enable(true); return true; } if(evt.arg.equals("Delete")) { current_t.dels(); eitem3.enable(false); eitem.enable(false); eitem1.enable(false); eitem2.enable(false); return true; } if(evt.arg.equals("Close")) { current_t.totle= 0; current_t.rep(); return true; } if(evt.arg.equals("Find")) { new outPanel(this); return true; } if(evt.arg.equals("Font")) { new D_font(this,this,screenw,screenh); return true; } if(evt.arg.equals("Color")) { new D_color(this,this,screenw,screenh); return true; } } /*else if(evt.target instanceof Button) { } */ return false; } public void Find(String find,int dire) { current_t.find(find,dire); System.out.println(find); } public static void main(String[] args) { MainF ff= new MainF("文本编译器"); } } class outPanel extends Dialog { MainF fr; Label lbname; //name label TextField tfname; //entry field outlinePanel psex; //two outlined panels] CheckboxGroup cbsex; Checkbox up,down,boxc; //2 radios Button OK, Cancel; //two pushbuttons GridBagConstraints gbc; GridBagLayout gbl; int dire= 1; public outPanel(MainF fr) { super(fr,"查找",true); this.fr= fr; setFont(new Font("Helvetica", Font.PLAIN, 12)); setBackground(Color.lightGray); gbl =new GridBagLayout(); gbc = new GridBagConstraints(); setLayout(gbl); gbc.ipadx = 5; //make buttons wider gbc.ipady = 3; //make buttons higher gbc.insets.left =6; gbc.insets.right =6; gbc.insets.bottom =4; gbc.insets.top =4; gbc.weightx =1; gbc.weighty =1; lbname = new Label("查找目标:"); //label add_component(lbname, 0,0,1,1); tfname = new TextField(20); //text entry field add_component(tfname, 1,0,3,1); //panel with two buttons for sex boxc= new Checkbox("区分大小写"); add_component(boxc, 0,2,1,1); psex = new outlinePanel(); //first panel add_component(psex, 1,1,3,2); //insert in layout //put two radio buttons in panel up = psex.addCheckbox("向上", false); down = psex.addCheckbox("向下", true); psex.resize(148,53); //add two command buttons to bottom OK = new Button("找下一个"); //buttons at bottom Cancel = new Button("取消"); add_component(OK,4,0,1,1); add_component(Cancel,4,2,1,1); reshape(100,100, 380,146); show(); } //-------------------------------------- private void add_component(Component c, int x, int y, int w, int h) { gbc.gridx = x; gbc.gridy = y; gbc.gridwidth = w; gbc.gridheight =h; add(c); gbl.setConstraints(c, gbc); } //-------------------------------------- public boolean action(Event evt, Object arg) { if(evt.target== OK) { String ss= tfname.getText(); if(boxc.getState()) { ss.toLowerCase(); fr.Find(ss,dire); }else fr.Find(ss,dire); } if (evt.target == Cancel) { dispose(); return true; } if(evt.target== up) { dire=-1; } if(evt.target== down) { dire= 1; } if(evt.target== boxc) { } return super.action(evt, arg); } } //*///======================================= class outlinePanel extends Panel { CheckboxGroup cbg; int yposn; Vector cblist; //-------------------------------------- public outlinePanel() { super(); cbg = new CheckboxGroup(); //for radio buttons setLayout(null); yposn = 3; cblist = new Vector(); } //-------------------------------------- public void reshape(int x, int y, int w, int h) { super.reshape(x,y,w,h); for (int i=0; i< cblist.size(); i++) { Component c = (Component)cblist.elementAt(i); c.reshape(c.location().x, c.location().y, w-6, c.size().height); } } //-------------------------------------- public Component add(Component c) { super.add(c); return c; } //-------------------------------------- public Checkbox addCheckbox(String s, boolean checked) { Checkbox cb = new Checkbox(s, cbg, checked); add(cb); cblist.addElement(cb); cb.reshape(3, yposn, 50, 12); yposn += 15; return cb; } //-------------------------------------- public void paint(Graphics g) { Dimension sz = size(); Point pt = location(); g.setColor(Color.darkGray); // graw rectangle g.drawRect(0, 0, sz.width-2, sz.height-2); //outline gray rectangle with 2 white inner lines g.setColor(Color.white); g.drawLine(1, 1, sz.width-3, 1); g.drawLine(1, 1, 1, sz.height-3); //and two white outer lines g.drawLine(0,sz.height-1,sz.width-1,sz.height-1); g.drawLine(sz.width-1, 0, sz.width-1, sz.height-1); } //-------------------------------------- } <

小小豆叮

Java中使用DirectDraw

注释:DirectDraw®是微软DirectX® SDK的一个组成部分。Java版的DirectX包含在Java 2.0 SDK中。Java中通过同DirectX一起安装的com.ms.directX包中的一套类访问DirectDraw。 介绍 本文将探讨用于Java的DirectDraw SDK的一些优点、结构和使用。过去,使用动画的程序需要用C++编写(或者汇编语言),原因是动画需要很快的处理速度。动画是通过快速连续地显示一系列画面(或者叫做帧)实现的。为了使用户观看动画时没有闪烁感,至少需要达到每秒12帧的速率。更高的帧速率会产生更平滑的动画:例如,动画片的帧速率通常为每秒24帧。要达到最低的每秒12帧要求,在640´480,256色模式下每秒需要处理3.6M字节的图象数据。处理如此数量的视频数据是非常困难的。DirectDraw现在为Java开发人员提供了这种视频数据处理能力。 开始使用DirectDraw API需要一些基本步骤。现将其列举如下,并且将会在后面的部分中解释: 创建DirectDraw对象。 设置协作级别。 创建需要的表面。 装入需要的位图。 显示表面。 DirectDraw对象 要在Java中使用DirectDraw,必须先创建DirectDraw对象: dd = new DirectDraw(); 一旦创建了DirectDraw对象,就需要设置协作级别。 协作级别 必须要设置DirectDraw在最高层窗口上拥有的控制级别。在最简单的级别上,DirectDraw的功能同其他程序相同,限制在Windows® 95或者Windows NT®窗口内。这是DirectDraw的正常级别操作。正常协作级别可以通过下面的语句设置: dd.setCooperativeLevel(hwnd, DDSCL_NORMAL); 注意 为了使程序能够利用更高级的DirectDraw特性,如改变显示模式或者修改DirectDraw的表面的行为,需要使用独占模式。然而,使用高级特性超出了本文的范围。要得到关于使用高级特性的更多信息,请参考DirectX文档。 创建表面 DirectDraw使用术语表面(surface)代表显示内容。表面既可以放在显示内存中,也可以放在系统内存中。然而,如果显示硬件没有足够的内存存放表面,DirectDraw会在系统内存中模拟表面。DirectDraw表面的最大优点是它总是以线性内存区域的形式提供给开发者。即使程序设置的显示模式不是线性的,表面为开发者提供的仍然是线性内存区域。考虑下面的例子,ModeX中显示内存是以一系列位面(plane)配置的(图1)。为了画一个点,必须防卫适当的内存位面以及正确的地址。DirectDrawSurface对象处理了所有这些细节。 图 1. Mode X中的显示内存组织。 一个DirectDraw表面可以包含多个内存缓冲区,允许构建可以切换的表面。切换表面使程序可以利用双缓冲技术的优点。在双缓冲方法中,程序在后台缓冲区中绘制一些内容,画完后,快速地将后台缓冲区切换或者复制到前台缓冲区,使该画面显示出来。双缓冲方法速度非常快,尤其是当前台和后台缓冲区都在显示内存中时。此时,切换操作甚至不消耗任何CPU时间。 基于DirectDraw程序的用户看到的总是原始表面(Primary surface)。为了改变用户看到的内容,可以简单地改变原始表面的内容。通常,需要创建一个或多个后台表面,这些表面中包含不同时间显示的画面。图2说明了一个复杂的双缓冲原始表面。该原始表面由一个后台缓冲区、一个前台缓冲区和四个后台表面组成。每个后台表面包含一个不同旋转阶段的位图。要使圆柱体动起来,程序将每个位图从后台表面中复制到原始表面的后台缓冲区中的一个位置。每当每个圆柱体的复制完成时,程序将后台缓冲区切换到前台缓冲区。 图2. 双缓冲表面之间的关系 DirectDraw在Java中的实现包括两个为表面工作而特别设计的类。DDSurfaceDesc类用来描述所创建表面的属性。DirectDrawSurface类包含为DirectDraw表面工作所做的定义、方法和变量。 要创建DirectDraw表面,不管是原始表面还是后台表面,必须先创建DDSurfaceDesc对象,设置表面属性,并且把表面描述结构传递给DirectDraw对象的CreateSurface()方法。下面的语句创建了一个由一个缓冲区组成的原始表面: // Create a primary surface containing a single buffer. ddsd = new DDSurfaceDesc(); ddsd.flags = DDSD_CAPS ; ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE; pdds = dd.createSurface( ddsd ); 下面的语句创建了一个双缓冲原始表面: //Create a primary surface containing a back //buffer for double buffering. ddsd = new DDSurfaceDesc(); ddsd.flags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE | DDSCPAS_FLIP | DDSCAPS_COMPLEX; ddsd. BackBufferCount = 1; pdds = dd.createSurface( ddsd ); 下面的语句说明了如何创建一个320象素宽200行高的后台表面: Ddsd = new DDSurfaceDesc(); ddsd.flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN; ddsd.width = 320; ddsd.height = 200; pdds = dd.createSurface( ddsd ); 将位图装入到DirectDraw表面中 位图是程序可以用来显示图形内容的图片。图2中的四个圆柱体每一个都是一张位图。Java中的DirectDraw为操作位图文件提供了DirectDrawBitmap类。DirectDrawBitmap对象创建后,可以复制到DirectDraw表面。下面的语句将一个叫作Frntback.bmp的位图文件读入到DirectDraw表面: //Read the bitmap file. Bm = new DirectDrawBitmap(); bm.filename(“Frntback.bmp”); bm.initWidth(dx); bm.initHeight(dy); if( bm.loaded() != 0 ) { // Create a DirectDrawSurface for // this bitmap. ddsd = new DDSurfaceDesc(); ddsd.flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN; ddsd.width = bm.width(); ddsd.height = bm.height(); pdds = dd.createSurface( ddsd ); pdds.copyBitmap(bm, 0, 0, 0, 0); } //Off-screen surface pdds now contains the //bitmap data. 显示表面 当所有表面都创建并初始化(例如将位图复制到表面中)以后,程序需要显示它们时就可以将它们复制到原始表面。这种表面复制通过DirectDrawSurface对象的blt方法完成。在缺省的操作模式下,如果切换程序忙(正在进行切换),DDS.blt()方法立即返回一个错误码。因此,应该以某种循环的形式使用DDS.blt()方法,或者指定DDS.blt()方法的DDBLT_WAIT标志。第二个选项改变了DDS.blt()的行为,使得此方法等待,直到切换可以进行或者出现另一个错误。 下面的代码说明了如何使用DirectDrawSurface的blt方法。首先,有两点需要阐明。如果显示卡的模式改变或者程序使用对显示卡的独占访问而释放了当前在显卡上分配的所有表面内存,DirectDrawSurface就会丢失。当表面丢失时,需要恢复。恢复可以通过两个步骤完成。首先调用DirectDrawSurface对象的Restore方法重新分配表面内存以及重新添加DirectDrawSurface对象。然后,重建相关表面的内容。 rc.Left = 0; rc.Top = 0; rc.Right = bm.width(); rc.Bottom = bm.height(); // Show the off-screen surface on the //primary surface. done = 0; do { int retval; retval = ddsPrimary.blt( rc, ddsOne, rc, 0); if( retval == DD_OK ) { //If the bitmap has been successfully //copied, exit loop. done = 1; } else if ( retval == DDERR_SURFACELOST ) { while( ddsPrimary.Restore() != DD_OK && ddsOne.Restore() != DD_OK ) { ReloadBitmap(ddsOne, szBitmap); } } else if( retval != DDERR_WASSTILLDRAWING) { // Undetermined error; quit the loop. done = 1; } } while( done == 0); 其他信息 本文提供了初始化和使用Java中DirectDraw SDK的背景和基本步骤。要获得Java的DirectX SDK以及相关文档,可以从http://www.microsoft.com/java/下载Microsoft SDK for Java 2.0(其中包含DirectX SDK)。其中还包括几个示例程序,对于使用DirectX for Java创建动画非常有用。这些例子列举如下: 示例 目录 示例内容 Ddex3 DDraw\ddex3\ddraw.html DirectDraw的基本使用 Flipcube d3d\flipcube\D3D.html 3D立即模式的基本使用 Viewer d3drm\Viewer\direct3dRM.html Direct3D保持模式的完整使用 Castle d3drm\Castle\Castle.html Direct3D保持模式的高性能 DirectInput dInput\ddex3\dinput.html 操纵杆、鼠标、键盘和游戏键盘的使用 <

小小豆叮

理解java的多形性

“对于面向对象的程序设计语言,多型性是第三种最基本的特征(前两种是数据抽象和继承。” “多形性”(Polymorphism)从另一个角度将接口从具体的实施细节中分离出来,亦即实现了“是什么”与“怎样做”两个模块的分离。利用多形性的概念,代码的组织以及可读性均能获得改善。此外,还能创建“易于扩展”的程序。无论在项目的创建过程中,还是在需要加入新特性的时候,它们都可以方便地“成长”。 通过合并各种特征与行为,封装技术可创建出新的数据类型。通过对具体实施细节的隐藏,可将接口与实施细节分离,使所有细节成为“private”(私有)。这种组织方式使那些有程序化编程背景人感觉颇为舒适。但多形性却涉及对“类型”的分解。通过上一章的学习,大家已知道通过继承可将一个对象当作它自己的类型或者它自己的基础类型对待。这种能力是十分重要的,因为多个类型(从相同的基础类型中衍生出来)可被当作同一种类型对待。而且只需一段代码,即可对所有不同的类型进行同样的处理。利用具有多形性的方法调用,一种类型可将自己与另一种相似的类型区分开,只要它们都是从相同的基础类型中衍生出来的。这种区分是通过各种方法在行为上的差异实现的,可通过基础类实现对那些方法的调用。 在这一章中,大家要由浅入深地学习有关多形性的问题(也叫作动态绑定、推迟绑定或者运行期绑定)。同时举一些简单的例子,其中所有无关的部分都已剥除,只保留与多形性有关的代码。 7.1 上溯造型 在第6章,大家已知道可将一个对象作为它自己的类型使用,或者作为它的基础类型的一个对象使用。取得一个对象句柄,并将其作为基础类型句柄使用的行为就叫作“上溯造型”——因为继承树的画法是基础类位于最上方。 但这样做也会遇到一个问题,如下例所示(若执行这个程序遇到麻烦,请参考第3章的3.1.2小节“赋值”): //: Music.java // Inheritance & upcasting package c07; class Note { private int value; private Note(int val) { value = val; } public static final Note middleC = new Note(0), cSharp = new Note(1), cFlat = new Note(2); } // Etc. class Instrument { public void play(Note n) { System.out.println("Instrument.play()"); } } // Wind objects are instruments // because they have the same interface: class Wind extends Instrument { // Redefine interface method: public void play(Note n) { System.out.println("Wind.play()"); } } public class Music { public static void tune(Instrument i) { // ... i.play(Note.middleC); } public static void main(String[] args) { Wind flute = new Wind(); tune(flute); // Upcasting } } ///:~ 其中,方法Music.tune()接收一个Instrument句柄,同时也接收从Instrument衍生出来的所有东西。当一个Wind句柄传递给tune()的时候,就会出现这种情况。此时没有造型的必要。这样做是可以接受的;Instrument里的接口必须存在于Wind中,因为Wind是从Instrument里继承得到的。从Wind向Instrument的上溯造型可能“缩小”那个接口,但不可能把它变得比Instrument的完整接口还要小。 7.1.1 为什么要上溯造型 这个程序看起来也许显得有些奇怪。为什么所有人都应该有意忘记一个对象的类型呢?进行上溯造型时,就可能产生这方面的疑惑。而且如果让tune()简单地取得一个Wind句柄,将其作为自己的自变量使用,似乎会更加简单、直观得多。但要注意:假如那样做,就需为系统内Instrument的每种类型写一个全新的tune()。假设按照前面的推论,加入Stringed(弦乐)和Brass(铜管)这两种Instrument(乐器): //: Music2.java // Overloading instead of upcasting class Note2 { private int value; private Note2(int val) { value = val; } public static final Note2 middleC = new Note2(0), cSharp = new Note2(1), cFlat = new Note2(2); } // Etc. class Instrument2 { public void play(Note2 n) { System.out.println("Instrument2.play()"); } } class Wind2 extends Instrument2 { public void play(Note2 n) { System.out.println("Wind2.play()"); } } class Stringed2 extends Instrument2 { public void play(Note2 n) { System.out.println("Stringed2.play()"); } } class Brass2 extends Instrument2 { public void play(Note2 n) { System.out.println("Brass2.play()"); } } public class Music2 { public static void tune(Wind2 i) { i.play(Note2.middleC); } public static void tune(Stringed2 i) { i.play(Note2.middleC); } public static void tune(Brass2 i) { i.play(Note2.middleC); } public static void main(String[] args) { Wind2 flute = new Wind2(); Stringed2 violin = new Stringed2(); Brass2 frenchHorn = new Brass2(); tune(flute); // No upcasting tune(violin); tune(frenchHorn); } } ///:~ 这样做当然行得通,但却存在一个极大的弊端:必须为每种新增的Instrument2类编写与类紧密相关的方法。这意味着第一次就要求多得多的编程量。以后,假如想添加一个象tune()那样的新方法或者为Instrument添加一个新类型,仍然需要进行大量编码工作。此外,即使忘记对自己的某个方法进行过载设置,编译器也不会提示任何错误。这样一来,类型的整个操作过程就显得极难管理,有失控的危险。 但假如只写一个方法,将基础类作为自变量或参数使用,而不是使用那些特定的衍生类,岂不是会简单得多?也就是说,如果我们能不顾衍生类,只让自己的代码与基础类打交道,那么省下的工作量将是难以估计的。 这正是“多形性”大显身手的地方。然而,大多数程序员(特别是有程序化编程背景的)对于多形性的工作原理仍然显得有些生疏。 7.2 深入理解 对于Music.java的困难性,可通过运行程序加以体会。输出是Wind.play()。这当然是我们希望的输出,但它看起来似乎并不愿按我们的希望行事。请观察一下tune()方法: public static void tune(Instrument i) { // ... i.play(Note.middleC); } 它接收Instrument句柄。所以在这种情况下,编译器怎样才能知道Instrument句柄指向的是一个Wind,而不是一个Brass或Stringed呢?编译器无从得知。为了深入了理解这个问题,我们有必要探讨一下“绑定”这个主题。 7.2.1 方法调用的绑定 将一个方法调用同一个方法主体连接到一起就称为“绑定”(Binding)。若在程序运行以前执行绑定(由编译器和链接程序,如果有的话),就叫作“早期绑定”。大家以前或许从未听说过这个术语,因为它在任何程序化语言里都是不可能的。C编译器只有一种方法调用,那就是“早期绑定”。 上述程序最令人迷惑不解的地方全与早期绑定有关,因为在只有一个Instrument句柄的前提下,编译器不知道具体该调用哪个方法。 解决的方法就是“后期绑定”,它意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫作“动态绑定”或“运行期绑定”。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。 Java中绑定的所有方法都采用后期绑定技术,除非一个方法已被声明成final。这意味着我们通常不必决定是否应进行后期绑定——它是自动发生的。 为什么要把一个方法声明成final呢?正如上一章指出的那样,它能防止其他人覆盖那个方法。但也许更重要的一点是,它可有效地“关闭”动态绑定,或者告诉编译器不需要进行动态绑定。这样一来,编译器就可为final方法调用生成效率更高的代码。 7.2.2 产生正确的行为 知道Java里绑定的所有方法都通过后期绑定具有多形性以后,就可以相应地编写自己的代码,令其与基础类沟通。此时,所有的衍生类都保证能用相同的代码正常地工作。或者换用另一种方法,我们可以“将一条消息发给一个对象,让对象自行判断要做什么事情。” 在面向对象的程序设计中,有一个经典的“形状”例子。由于它很容易用可视化的形式表现出来,所以经常都用它说明问题。但很不幸的是,它可能误导初学者认为OOP只是为图形化编程设计的,这种认识当然是错误的。 <

小小豆叮

使用 EJB 组件你需要了解些什么呢?

什么是 EJB 组件?EJB 组件是为企业级应用设计的 java 组件模型。 EJB 组件是基于标准分布式对象技术、CORBA 和 RMI 的服务器端 java 组件。EJB 组件总是分布式的,这是它们与标准 JavaBeans 组件最根本的区别。 EJB 组件提供了应用的商务逻辑部分。由于它们不涉及表示层的问题,因此必须与其它的显示技术(如 servlets),服务于 HTML 客户端的 JSP 技术,或者使用了诸如 AWT、Swing 技术的 java 应用一起使用。 实现了 EJB 规范的应用服务器提供了可以解决安全性、资源共享、持续运行、并行处理、事务完整性等复杂问题的服务,从而简化了商业应用系统。 Sun 公司制定的 EJB 组件模型要求 EJB 组件运行于 EJB 服务器(通常称为应用服务器)的环境下。我们的示例中使用了高级版 WebSphere 应用服务器,但所讨论的功能适用于大多数 EJB 服务器。 需要考虑的技术问题 当你在决定 EJB 组件是否为适合你的实际情况的合适技术时,不妨先考虑几个问题。如果你对所有这些问题的回答都是肯定的话,那么 EJB 组件就是你可以采用的合适技术。反之的话,别的技术可能更适合。 你需要将商务逻辑组件与面向外界的 Internet 隔离开吗? 许多公司认为他们的应用软件,特别是构成商务逻辑的一些标准和数据结构,是极为重要的公司财产(例如,公司所拥有的分析应用工具构成了股票交易网站的一部分)。允许外人访问这些属于公司资产的原码和目标码将对公司产生极大的危害。因此,这些公司十分需要将商务逻辑置于一套安全防火墙后面(通常称为无戒备区,也称 DMZ)。 在这种情况下,分布式对象组件体系结构(例如 EJB 技术)允许你将有价值的公司资产隔离到 DMZ 以内,同时表示层代码可以访问 DMZ 内的 EJB 服务器。下图描述了这种分布式解决方案: 防火墙内部的 EJB 这里我们假设表示层逻辑不如后台的商务逻辑重要。如果不是这样,那么这种方案的安全性就要下降,整个系统可能都需要置于 DMZ 之内。如果整个应用必须(或者能够)置于第二层防火墙后面,那么选择其它技术(如通过 Java Servlets 发出 JDBC 请求来直接访问数据库)就显得更合理。 这种解决方案也有一些效率方面的缺陷。例如在安装 WebSphere 时,如果你将客户端(servlets 与 JSP文件)与图示的位于另一个 java 虚拟机(JVM)中的 EJB 组件分隔开,这种选择将降低整体性能。与客户端和 EJB 组件位于同一个 JVM 中的情况相比,这种方式下每一个需要经过防火墙的请求都将增加20%的耗时。 你需要不止一种类型的客户端访问共享数据吗? 通常,一个应用会有多种类型、需要访问相同信息的客户端。例如,一个应用可能会有供外部客户访问的基于 web 的 HTML 前端,以及供内部人员使用的更完整的应用前端。通常,这个问题是通过为同一应用编写两个共享相同数据源(数据库表)的版本来解决的。但是,这种方法效率不高,无论是从编程时间还是从同时发生多个数据库锁定时数据库的利用率来说。 EJB 技术的解决方案是将共享数据和商务逻辑集成到一套 EJB 组件中,以供不同类型的客户端(如 servlet/HTML 和 application)访问。EJB 组件控制着对后台数据的访问,并管理着当前事务以及数据库的内部锁定。通过去除应用中的重复代码,减少编写数据库控制逻辑的工作,这种方案降低了总的编程量。 在这个领域还有其它一些解决方案--比如,java 应用可以通过 HTTP 访问 java servlets,同时浏览器也可以通过 HTTP 访问 java servlets。这些解决方案的问题在于:如果 servlet 是用来在浏览器中显示信息的,它就必须包含一些表示层逻辑,这些表示层逻辑对于向另一个程序传递信息来说是多余的。因此,你最终不得不采用两套部分重复的 servlets 来处理两种情况。此外,HTTP 不是程序间通讯的效率最高的协议。你必须设计能通过 HTTP 管道进行程序间信息传递的数据格式--这通常或者是基于文本的格式,比如 XML(由接收端进行解析,由发送端生成),或者是基于对象的格式,比如 java 序列化。两种格式都需要大量的编程工作,它们都不如本地的 IIOP 速度快。 你是否需要对共享数据同时进行读和写操作? 通常,"胖客户端"解决方案要求应用在数据库级别上管理对共享数据的访问。其结果是:处理数据库锁定与同步的方案非常复杂,若不考虑数据库锁定与同步问题又会失去数据的完整性。 EJB 技术能自动处理这些复杂的共享数据同步问题。正如前面提到的那样,EJB 组件控制着对后台数据的访问,并管理着当前事务和数据库的内部锁定。这不仅省去了编写数据库控制逻辑的工作量,同时也保证了数据的一致性与正确性,从而降低了总的编程量。 你需要访问具备事务处理功能的多个异构数据源吗? 许多应用需要访问多个数据源。例如,一个应用程序可能既要访问来自中间层的 Oracle 数据库,又要通过中间件(如 MQSeries)访问 CICS、IMS 等大型主机系统。问题的关键是一些应用要求这种访问是完全事务化的,并且数据完整性在不同数据源间也能得到保证。例如,某个应用可能要求在处理用户的订购信息时,既要在 Oracle 数据库中存储详细的订购信息,同时又通过 MQSeries 在 CICS 系统中存储一份出货订单。无论是数据库更新或是 MQ 队列产生错误,整个事务都应被取消。 过去,构建这样的系统的唯一选择是采用事务监视器,例如 Encina、CICS、Tuxedo,它们使用非标准接口并需要用 COBOL、C、C++ 等语言进行开发。例如,高级版 WebSphere 中的 EJB 容器支持多个并发的事务,具备在多个 DB2 数据源间进行完整的事务提交及事务取消的能力,这些都是在一个完全支持二状态事务提交的环境中进行的。目前,WebSphere 对其它数据源(如 Oracle, MQSeries 和 CICS)只支持单状态的事务提交。企业版 WebSphere 中的 EJB 容器能对更多的数据源支持二状态的事务提交。 大多数容器正在支持各种数据源下的二状态事务提交方面不断完善。随着时间的推移,我们将在这一领域看到不断的进步。 你需要能与 HTML 文档、servlets、JSP 文件、客户端登录安全性无缝集成的方法级对象安全性吗? 某些类型的应用由于其安全性限制,使得以前它们很难通过 java 应用来实现。例如,某些保险业的应用程序为了满足管理规定的要求,必须限制对客户数据的访问。直到 EJB 技术出现后,才能够限制特定用户访问某个对象或方法。在这之前,可实施的办法只有:在数据库级别上限制访问,并捕获在 JDBC 层次上抛出的错误;或者通过客户安全密码在应用层上限制访问。 EJB 技术可以在任何 EJB 组件或方法上实施方法级的安全策略。创建的用户和用户组可以被授予或禁止对任何 EJB 组件或方法的操作权。在 WebSphere 中,用户组可以被授予或禁止对 web 资源(servlets, JSP 文件和 HTML 页面)的访问权,用户的 ID 可以通过底层的安全机制被安全地从 web 资源传递到 EJB 组件。 体系结构是否有标准化、轻量化、组件化的需要呢? 对于许多有远见的公司来说,关键问题是要实现平台、销售商和应用服务器设备间的相互独立。符合工业标准的 EJB 组件体系结构有助于实现这些目标。为 WebSphere 开发的 EJB 组件通常可以发布到其它类型的应用服务器上使用,反之亦然。尽管这一目标尚未完全实现,但它已成为许多客户选择的战略发展方向。从短期看,利用一些可能优于标准化的特性会更方便、更迅速,但从长远看标准化具有最大的好处。 你也应当考虑到越来越多的可选工具和 EJB 标准的优化实现手段,这些都是你无法从本地管理对象框架中获得的。由于大多数公司并不从事中间件业务,将注意力集中在与你的业务更直接相关的活动上会更有效。 你需要多个服务器来满足系统的吞吐量和有效性需要吗? 胖客户端系统显然不能适应 web 系统可能拥有的成千上万个用户。软件发布方面的问题也要求给胖客户端减肥。Web 站点的24小时不间断运行特点也使得时间成为关键问题。但并不是每个人都需要24小时不间断运行,并能同时处理上万个用户的系统。你应当能设计这样的系统:在不增加开发和标准化难度的前提下,实现系统的伸缩性。 因此,你要设法使得编写的商务逻辑可以进行伸缩来满足这些需要。EJB 技术为构建这种具有高伸缩性、高利用率的系统提供了骨架。例如,WebSphere 通过以下一些特性帮助开发者构建这类系统。这些特性也适用于其它的 EJB 服务器: 对象缓存与共享。WebSphere 自动在服务器层面上共享无状态会话 EJB 组件,从而减少了用于创建对象和回收碎片的时间。这将使得更多的处理时间周期可以分配给真正的实际工作。 服务器端的工作量优化。WebSphere 还使得 EJB 服务器具备群管理的特点。在 WebSphere 应用服务器上,你可以创建跨越多个节点的服务器组。除此之外,你可以创建模型(服务器的抽象表述),并把它们克隆到多个 JVM 中。你可以将克隆的模型配置成运行于组内的任何一台服务器上。另外,一个服务器的多个克隆模型也能运行与一台机子上,充分利用了多处理器的结构特点。同样,你也可以把所有克隆的模型作为一个组来管理。这就提高了可靠性,避免了个别地方故障时对应用服务器的破坏。 通过克隆可支持自动故障恢复。由于有几个克隆的服务器可以用来处理请求,故障不太可能破坏系统的吞吐量和可靠性。由于多个节点运行相同的服务,一台整机的故障就不会产生灾难性的后果。 所有这些特性都不需要对系统进行特殊的编程。利用这种可伸缩性也无需改动服务器端的代码。 WebSphere 支持其它服务器端 java 技术(如 java servlets,JSP文件)的发布、克隆和自动故障恢复。但是,这些更面向表示层的技术与其说是 EJB 组件的竞争对手,不如说是对其的补充。当总体时间和可伸缩性是问题的关键时,整体解决方案中应包括 EJB 组件。 关于两个方案的描述,或者说是关于正确使用 EJB 组件的描述 现在你已经了解了一些评估一个应用是否适合使用 EJB 技术的办法,下面我们来比较两个使用了 EJB 组件的应用,看看 EJB 技术究竟是帮助还是阻碍了开发者达到目标。在一个例子中 EJB 组件的正确使用使得代码更简洁、更易懂。在另一个例子中,使用 EJB 组件使得系统过于复杂,性能也不好。 这两个应用都是综合了多个其它应用的例子,而不是来自于某个单一的公司或方案小组。 一个使用 EJB 技术失败的例子 作为一个被授权评估新技术的高级技术小组的成员,某个方案小组最初设计了这个方案。该方案具有如下体系结构(每个方框代表运行于各自硬件上的不同程序): 不可取的 EJB 体系结构 该体系结构的一些具体设计使得 EJB 组件的使用不如它初看起来那么有吸引力: 应用的主体部分是信息的显示,这部分由 java servlets 实现。EJB 组件只用来获取、更新数据。 方案小组在后台主机的事务处理中使用的数据连接不包括遵从扩充体系结构(XA)标准的数据源。事务不能被成批取消或提交,对每个主机的访问都是一个独立的请求。 由于不能区分用户是来自 web 还是来自后台主机,EJB 技术的安全特性在此没有得到利用。 servlet 对 EJB 组件的每一次访问都是一个网络请求。组件的探测方法和 ejbLoad() 方法的内部逻辑执行了真正的主机请求,但这之后 EJB 组件仅仅进行数据缓存,直到事务提交后才由 ejbStore() 方法将信息回传给主机。直到事务提交前的每一次数据访问和更新都导致了大量的网络开销。 该方案小组仅仅把 EJB 组件用作主机数据与 servlets 间的数据映射机制,这不是 EJB 技术的有效使用方法。本例中的应用没有利用以下一些特性: EJB 组件的事务处理特性 EJB 组件的方法级安全性 EJB 组件的伸缩性与分布式特点(该方案小组仅使用了一个 EJB 服务器) 多种类型客户端间的商务逻辑共享 具备容器管理持久性(CMP)的 EJB 组件的自动数据映射功能 如果直接由和 java servlets 处于同一 JVM 中的 JavaBeans 组件来实现数据映射功能,系统的速度将比使用 EJB 组件快得多,同时避免了大量的网络开销。该系统还变得不必要的复杂(因为需要本地和远程接口,以及分布代码)。事实上,该方案后来被废弃并以不使用 EJB 组件的方式进行了重新设计,其中的数据映射由一套被 servlets 使用的标准 JavaBeans 组件实现。这使得最后的系统变得更简单、更快速。 一个成功使用EJB技术的例子 一个金融机构的方案小组设计了这套方案,最初使用了 java 和 RMI 技术。其系统体系结构如下: RMI服务器体系结构 除了多种类型的客户端,方案小组还在系统内构建了以下两个框架单元,在本图中未画出来: 一个数据库映射层,通过 RMI 服务器上的 JDBC 将其 Oracle 数据表映射成 java 类,或者反过来。 一个用户安全框架单元,用于验证来自浏览器或应用客户端的用户,并进而决定用户是否有权进行 RMI 和数据库请求。 由于他们的应用需求与 EJB 技术的目标紧密相连,该方案小组得以很快将其设计方案转变为以下体系结构: 新的 EJB 体系结构 通过使用容器管理持久性(CMP),EJB 组件使得方案小组可以抛弃数据库映射框架,转而利用 WebSphere 和 VisualAge for Java 中完整高效的实现机制。通过利用 EJB 技术的安全性,方案小组得以在应用中保留方法级安全性的同时,抛弃了他们的用户安全框架。结果,由于需要维护的代码量减少了,他们的应用变得更为简单。 简单地说,该应用的以下特点使得它适合使用 EJB 技术: 具有共享相同商务逻辑的多种类型客户端 使用了事务处理 需要通过 CMP 组件来实现与对象有关的映射 需要方法级的安全性 由于系统的设计目标是通过使用与实体同时发布的会话级 EJB 组件来实现最小的网络流量,该系统最终的体系结构很好地使用了 EJB 组件。对于前一种体系结构,由于客户端需要发出大量的请求,很可能会增加网络负担,另外由于要求客户端进行事务的启动、提交、取消,使得系统复杂化。 总结 构建一个可能使用 EJB 组件的新系统时,做出正确决定的第一步是要懂得如何确定 EJB 技术是否适合于该应用,包括选择 EJB 组件作为一种实现手段所带来的正面和负面影响。 <

小小豆叮

solaris上开发J2EE应用中文问题的解决

关于JSP和J2EE的中文问题和处理方法,网上已经有很多文章。一般在中文内核的操作系统(如中文NT,windows2000)上,该问题不是很突出,而在其他一些系统(如linux,solaris)中就比较明显了。近日在solaris上开发一个J2EE的应用系统,环境是solaris 2.7+minij2ee+mysql。系统在windows和linux下均没有出现中文处理的问题,但部署到solaris上面后所有中文显示为?。 后来经过分析,发现原因出在系统编码上。由于安装solaris时默认的系统编码为ASCII,因此以默认的系统编码处理字符串时汉字高位信息丢失。下面一段简单的jsp程序说明了这个问题: <%@ page contentType="text/html;charset=gb2312"%> <% String str=request.getParameter("i"); //byte[] b=str.getBytes("iso-8859-1"); byte[] b=str.getBytes(); out.println(new String(b,"gb2312")); %> 在浏览器中输入foo.jsp?i=中文,结果显示为??。如果将byte[] b=str.getBytes();换成上面注释掉的byte[] b=str.getBytes("iso-8859-1");,则正常显示出“中文”二字。查阅了mysql JDBC的驱动程序,问题相同。 考虑解决的方法有两个,一个是修改JDBC驱动程序,另一个是将汉字编码成7位,从实现方便的角度选择了后者。不过后者的缺点是字符串长度增加,并且无法直接通过sql工具来修改数据库了。网上有一种汉字编码的方法,是将汉字高位去1,英文则补一个0表示。这种方法有缺陷,因为特定的汉字编码后会出现“'”等SQL语句中有歧义的字符,导致sql失败。我摘取了minij2ee中uniString的编码方法,该方法将字节表示为其16进制编码,下面是源代码: public String encode() { try { StringBuffer sb=new StringBuffer(); byte[] bytes=m_enc.compareTo("")==0?m_str.getBytes():m_str.getBytes(m_enc); for(int i =0;i>4)&0xF,16); sb.append(ch); ch=Character.forDigit(bytes[i]&0xF,16); sb.append(ch); } return sb.toString(); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException("Unsupported encoding type."); } } public void decode(String encodestr) { StringBuffer sb=new StringBuffer(); int i=0; while(i!=encodestr.length()) { sb.append((char)Integer.parseInt(encodestr.substring(i,i+2),16)); i+=2; } m_str=new uniString(sb.toString(),"iso-8859-1").cvt(m_enc); } 使用编码后,问题解决。 另外提一下,minij2ee最新版本中提供了一个uniString类,解决了在所有操作系统上的中文问题。使用uniString对象,无需关心字符串本身编码,使用时只要调用函数来获得需要的编码即可,如在jsp里调用uniString.gb()即可以以gb2312输出字符串,在数据库存储时调用uniString.iso()即可以以iso-8859-1编码输出字符串,无论在中文内核还是英文内核的操作系统上均通用。 <

小小豆叮

SUN认证Java2程序员考试(SCJP) 试题解析

前言 无论你是个新手,还是程序设计方面的专家,你都会惊异于Sun公司Java的无穷魅力。Java带给你的并不仅仅是面向对象、开放、平台无关、易用、安全和“Write once, run anywhere”等软件开发方面的优势,更重要的一点是,它提供了一种新颖的表达思想的方式,一种全新的思维模式。随着待解决问题的规模不断膨胀,Java彻底的面向对象思想的灵活性就会凸现出来。毋庸置疑,Java是你开发大型软件时最得心应手的利器或是你转行IT的入门首选。 SCJP考试简介 ● 考试方式 全英文试题,以电脑作答,在授权的Prometric考试中心参加考试 考试编号:310-025 先决条件:无 考试题型:复选、填空和拖拽匹配 题量:59 及格标准:61% 时限:120分钟 费用:1250元 ● 要求具备的能力 ● 使用Java编程语言创建Java应用程序和applets。 ● 定义和描述垃圾搜集,安全性和Java虚拟机(JVM)。 ● 描述和使用Java语言面向对象的特点。 ● 开发图形用户界面(GUI)。利用Java支持的多种布局管理。 ● 描述和使用Java的事件处理模式。 ● 使用Java语言的鼠标输入、文本、窗口和菜单窗口部件。 ● 使用Java的例外处理来控制程序执行和定义用户自己的例外事件。 ● 使用Java语言先进的面向对象特点, 包括方法重载、方法覆盖、抽象类、接口、final、static和访问控制。 ● 实现文件的输入/输出 (I/O)。 ● 使用Java语言内在的线程模式来控制多线程。 ● 使用Java 的Sockets机制进行网络通信。 例题1: Choose the three valid identifiers from those listed below. A. IDoLikeTheLongNameClass B. $byte C. const D. _ok E. 3_case 解答:A, B, D 点评:Java中的标示符必须是字母、美元符($)或下划线(_)开头。关键字与保留字不能作为标示符。选项C中的const是Java的保留字,所以不能作标示符。选项E中的3_case以数字开头,违反了Java的规则。 例题2: How can you force garbage collection of an object? A. Garbage collection cannot be forced B. Call System.gc(). C. Call System.gc(), passing in a reference to the object to be garbage collected. D. Call Runtime.gc(). E. Set all references to the object to new values(null, for example). 解答:A 点评:在Java中垃圾收集是不能被强迫立即执行的。调用System.gc()或Runtime.gc()静态方法不能保证垃圾收集器的立即执行,因为,也许存在着更高优先级的线程。所以选项B、D不正确。选项C的错误在于,System.gc()方法是不接受参数的。选项E中的方法可以使对象在下次垃圾收集器运行时被收集。 例题3: Consider the following class: 1. class Test(int i) { 2. void test(int i) { 3. System.out.println(“I am an int.”); 4. } 5. void test(String s) { 6. System.out.println(“I am a string.”); 7. } 8. 9. public static void main(String args[]) { 10. Test t=new Test(); 11. char ch=“y”; 12. t.test(ch); 13. } 14. } Which of the statements below is true?(Choose one.) A. Line 5 will not compile, because void methods cannot be overridden. B. Line 12 will not compile, because there is no version of test() that rakes a char argument. C. The code will compile but will throw an exception at line 12. D. The code will compile and produce the following output: I am an int. E. The code will compile and produce the following output: I am a String. 解答:D 点评:在第12行,16位长的char型变量ch在编译时会自动转化为一个32位长的int型,并在运行时传给void test(int i)方法。 例题4: Which of the following lines of code will compile without error? A. int i=0; if (i) { System.out.println(“Hi”); } B. boolean b=true; boolean b2=true; if(b==b2) { System.out.println(“So true”); } C. int i=1; int j=2; if(i==1|| j==2) System.out.println(“OK”); D. int i=1; int j=2; if (i==1 &| j==2) System.out.println(“OK”); 解答:B, C 点评:选项A错,因为if语句后需要一个boolean类型的表达式。逻辑操作有^、&、| 和 &&、||,但是“&|”是非法的,所以选项D不正确。 例题5: Which two demonstrate a "has a" relationship? (Choose two) A. public interface Person { } public class Employee extends Person{ } B. public interface Shape { } public interface Rectandle extends Shape { } C. public interface Colorable { } public class Shape implements Colorable { } D. public class Species{ } public class Animal{private Species species;} E. interface Component{ } class Container implements Component{ private Component[] children; } 解答:D, E 点评: 在Java中代码重用有两种可能的方式,即组合(“has a”关系)和继承(“is a”关系)。“has a”关系是通过定义类的属性的方式实现的;而“is a”关系是通过类继承实现的。本例中选项A、B、C体现了“is a”关系;选项D、E体现了“has a”关系。 例题6: Which two statements are true for the class java.util.TreeSet? (Choose two) A. The elements in the collection are ordered. B. The collection is guaranteed to be immutable. C. The elements in the collection are guaranteed to be unique. D. The elements in the collection are accessed using a unique key. E. The elements in the collection are guaranteed to be synchronized 解答:A, C 点评:TreeSet类实现了Set接口。Set的特点是其中的元素惟一,选项C正确。由于采用了树形存储方式,将元素有序地组织起来,所以选项A也正确。 例题7: True or False: Readers have methods that can read and return floats and doubles. A. Ture B. False 解答:B 点评: Reader/Writer只处理Unicode字符的输入输出。float和double可以通过stream进行I/O. 例题8: What does the following paint() method draw? 1. public void paint(Graphics g) { 2. g.drawString(“Any question”, 10, 0); 3. } A. The string “Any question?”, with its top-left corner at 10,0 B. A little squiggle coming down from the top of the component. 解答:B 点评:drawString(String str, int x, int y)方法是使用当前的颜色和字符,将str的内容显示出来,并且最左的字符的基线从(x,y)开始。在本题中,y=0,所以基线位于最顶端。我们只能看到下行字母的一部分,即字母‘y’、‘q’的下半部分。 例题9: What happens when you try to compile and run the following application? Choose all correct options. 1. public class Z { 2. public static void main(String[] args) { 3. new Z(); 4. } 5. 6. Z() { 7. Z alias1 = this; 8. Z alias2 = this; 9. synchronized(alias1) { 10. try { 11. alias2.wait(); 12. System.out.println(“DONE WAITING”); 13. } 14. catch (InterruptedException e) { 15. System.out.println(“INTERR UPTED”); 16. } 17. catch (Exception e) { 18. System.out.println(“OTHER EXCEPTION”); 19. } 20. finally { 21. System.out.println (“FINALLY”); 22. } 23. } 24. System.out.println(“ALL DONE”); 25. } 26. } A. The application compiles but doesn't print anything. B. The application compiles and print “DONE WAITING” C. The application compiles and print “FINALLY” D. The application compiles and print “ALL DONE” E. The application compiles and print “INTERRUPTED” 解答:A 点评:在Java中,每一个对象都有锁。任何时候,该锁都至多由一个线程控制。由于alias1与alias2指向同一对象Z,在执行第11行前,线程拥有对象Z的锁。在执行完第11行以后,该线程释放了对象Z的锁,进入等待池。但此后没有线程调用对象Z的notify()和notifyAll()方法,所以该进程一直处于等待状态,没有输出。 例题10: Which statement or statements are true about the code listed below? Choose three. 1. public class MyTextArea extends TextArea { 2. public MyTextArea(int nrows, int ncols) { 3. enableEvents(AWTEvent.TEXT_ EVENT_MASK); 4. } 5. 6. public void processTextEvent (TextEvent te) { 7. System.out.println(“Processing a text event.”); 8. } 9. } A. The source code must appear in a file called MyTextArea.java B. Between lines 2 and 3, a call should be made to super(nrows, ncols) so that the new component will have the correct size. C. At line 6, the return type of processTextEvent() should be declared boolean, not void. D. Between lines 7 and 8, the following code should appear: return true. E. Between lines 7 and 8, the following code should appear: super.processTextEvent(te). 解答:A, B, E 点评:由于类是public,所以文件名必须与之对应,选项A正确。如果不在2、3行之间加上super(nrows,ncols)的话,则会调用无参数构建器TextArea(), 使nrows、ncols信息丢失,故选项B正确。在Java2中,所有的事件处理方法都不返回值,选项C、D错误。选项E正确,因为如果不加super.processTextEvent(te),注册的listener将不会被唤醒。 SCJP考试中的几点注意 ● 深刻理解面向对象的思想 Java是一种纯粹的面向对象的程序设计语言。在正式使用Java做开发之前,必须将我们的思维方式转入一个彻底的面向对象的世界。做不到这一点,就无法体会Java语言的精髓,写不出地道的Java程序。当然,你也无法彻底理解Java中的基本概念和他们之间的联系与区别,如例题3、例题5。你可以学到Java的语法规则,却不能看到Java的灵魂。 ● 对概念细节的精确把握 通过例题我们可以看到,SCJP的考察点相当细致。如例题1、2、4、7、8。所以只有对Java的概念、语法、规则了然于心,才能在考场上应对自如。 ● 适量的练习 程序设计是一项实践性很强的技术。只有上机实践,才能使课本中的理论、头脑中的思想通过你的双手成为一行行代码,完成规定的目标。虽然SCJP考试不考操作与编程,但有大量的程序阅读,如例题3、4、9、10。如果你自己写过许多代码的话,这种题就是小菜一碟。 ● 广泛的交流 善于交流是优秀程序员必备的技能,也是你解决疑难,提高水平的捷径。国内外有很多与Java认证相关的优秀网站和论坛,如: www.javaranch.com, www.javaunion.net等, 都是学习Java的宝库。同时,一些很棒的模考软件,如Jxam、JTest、 Javacert等,以及著名的模考题如MarcusGreen的三套题均可以找到。 <

小小豆叮

JSP国外优秀站点

<

小小豆叮

数据库三大巨头挥舞XML

时下数据库领域的三大巨头——IBM、Oracle 和Microsoft都挥舞着XML(扩展标记语言),它并非秘密武器,但可以使数据库运行得更快并适宜于提供Web服务。 Microsoft准备将其代号为“Yukon”的新版本SQL Server推向企业级舞台。Yukon将被作为Microsoft的.Net网络服务战略中的XML后端引擎,设计目标是“语言无关性”。Microsoft在不断地增加对嵌入式XML标准的支持,在10月公布的SQL XML2.0中包含了对XSD(XML计划定义)的支持。 Oracle和IBM在XML市场上也跃跃欲试。Oracle推出了和XML相关的技术,称做XDB(XML数据库支持),但目前没有提供更深的细节。IBM则强调DB2和XML扩展件的结合,提供了与Oracle的XDB技术基本相同的功能。IBM认为,XDB就是DB2加XML扩展件。DB2通过创建XML扩展件来管理XML内容。 Microsoft、IBM和Oracle目前都在XQuery(一种基于XML的查询标准,以访问各种不同的数据源)方面比拼。IBM在实验室里已经有了集成了XQuery的原型产品,可以认为它“能说两种语言”,因为它能接收基于XML或者基于SQL的查询请求,并返回任何一种语言的结果。 一些用户已经开始得到使用XML的回报。例如纽约的《Rolling Stone》杂志,用SQL Server 2000的XML功能从其数据库传输数据到自己的网站上,提高了工作效率。(IDG电讯) <

小小豆叮

基于J2EE的应用服务器相互连接实验获得成功

分散对象(Object)推进合作会日前针对NEC、日本BES系统公司、日本IBM等7家公司的基于J2EE(Java 2 Enterprise Edition)的Web应用服务器进行了相互连接实证试验,并获得了成功。   此次试验中使用了由客户机、前端服务器、后端服务器构成的环境。试验证实,即使前端服务器和后端服务器使用不同的Web应用服务器,也能够确保进行信息的检索与更新。此次试验在后端服务器上运行了管理工作人员的应用程序,在前端服务器上对这一应用的位置信息进行管理。其机制为,使用者从客户机访问前端服务器并获取应用的位置信息之后,再访问后端服务器,并通过后端服务器进行处理。   此次接受相互连接实证试验的Web应用服务器产品如下:WebOTX Ver3.2(NEC),J2SDK/J2EE RI1.3(Sun微系统公司),WebLogicServer 6.1J(日本BEA系统公司、日本优利),WebSphere Application Server V3.5(日本IBM),Cosminexus Server-Standard Edition 04-01(日立制作所),INTERSTAGE Application Server(富士通)。   进行实证试验的产品的标准为:J2EE的版本是J2EE1.2或者J2EE1.3,JDK(Java开发配套元件)的版本为JDK1.2或JDK1.3,通信协议为IIOP(Internet Inter-ORB Protocol)1.1或IIOP1.2。   参与此次实证试验的日立联合推进部长大谷真表示:“此次试验的成功,可以说已经使连接不同供应商的Web应用服务器来开展业务的可能性已达到了七成左右”。同样,日立公司的技术解决方案推进部长今城哲二也对市场的扩大充满信心:“有些客户已经在订货条件中加上了必须使用可相互连接的Web应用服务器这一条款”。 <

小小豆叮

Java Web Start for Windows

<

小小豆叮

BEA系统公司卡尔曼:商用市场 .Net不敌Java

  专业从事互联网应用软件开发的BEA系统公司的主席比尔·卡尔曼认为,Java已经赢得了新一代互联网服务大战。   他指出,尽管业界仍然对谁能够最终占得上风把握不定,但在争夺企业客户的大战中,.Net已经输给了Java。他在BEA公司于上周在伦敦举行的电子商务研讨会上说,“.Net推出来后,迅速占领了低端市场,但却丢了企业市场。”他预测说,绝大多数的商用应用软件将使用Java 2 Enterprise Edition(J2EE)。   卡尔曼高度赞扬了微软在互联网服务协议方面的工作,并指出,由于当前使用的技术价格昂贵,而且效率低下,因此,微软的这些协议将成为未来应用-应用通讯的基础。他还说,胖客户机-服务器模式只会使系统变得更复杂。对于控制台式电脑而言,.Net是一种高明的策略,但企业将在不久之后淘汰台式电脑。 图为BEA系统公司主席比尔·卡尔曼近影 <

小小豆叮

Nokia设奖鼓励Java应用程序开发

  Nokia公司为如何利用Java技术和Symbian公司的操作系统为移动电话开发最新最好的应用提出了一个巧妙的计划:举办开发者竞赛,让他们去开发。Nokia移动挑战赛由各种比赛组成,面向全球开发者——无论专业人员还是学生。Nokia日前推出了移动信息设备配置文件(MIDP)Java竞赛,12月10日将开始Symbian OS竞赛。Nokia表示获胜的应用将用于Nokia移动电话,如刚刚推出的支持Java的Nokia 7650图像电话和流行的支持Java的Nokia 9210通讯机。此外,Nokia表示这些应用的著作权仍属于其创作者所有。作为Nokia移动挑战赛的一部分,Nokia将向参赛者免费提供测试应用的工具和模拟器。获胜者将于2002年3月公布。 <

小小豆叮

无线编程论坛

<

小小豆叮

JNDI设计内幕

1 将接口分为Context 和 DirContext   JNDI有两个核心接口Context和DirContext,Context中包含了基本的名字操作,而DirContext则将这些操作扩展到目录服务。将这些操作分为两个包一方面为了模块化,另一方面也可以使服务减少不必要的开销。名字是计算服务中的一个基本功能,使用基本的名字服务就可以获得文件系统、电子表格、日历服务等功能;DirContext 对Context进行了扩展,提供了基本的目录服务操作,对名字对象属性的维护、基于属性的名字查找等等。   2 将JNDI 分成多个功能包   JNDI 分为两个客户端包(javax.naming,javax.naming.directory) 和一个服务端包 (javax.naming.spi)。这样做也同样是为了减少应用程序不必要的开销,使得应用程序只需要包括所必须的包。   3 将客户端API 和 服务端的API 分开   JNDI 将客户端接口与服务提供商需要的接口分开为不同的包。比如,客户端程序只需要使用javax.naming包中提供的类,而服务提供商可能需要javax.naming和javax.naming.spi两中包。这样分开可以使客户和服务器端只专注于与自身有关的类信息。   4 上下文列表的多种方法   一般来说有两种进行上下文列表的应用:上下文浏览应用和对上下文中的对象进行实际操作的应用。   上下文浏览应用一般只需要显示上下文中包含内容的名字,或者再获取一些诸如对象的类型之类的信息。这种类型的应用一般都是交互式的,可以允许用户在列举的上下文列表中选择一些进行进一步的显示。   另外有一些应用需要对上下文中的对象进行实际的操作,比如,一个备份程序需要对目录中所有文件的状态进行操作,或者某打印机管理员可能需要对大楼中的所有打印机进行复位。为了进行这样的操作,程序需要获取上下文中的实际对象。   对于这样两种类型的应用,Context接口提供了两种上下文列表方法list()和listBindings()。其中list()只返回一系列名字/类映射,而listBindings() 则返回名字、类和对象本身。显然list()用于上下文浏览应用而listBindings()用于那些需要对对象进行实际操作的应用。   5 对联合的支持 联合是JNDI的一个基本概念,在客户端接口中可以支持跨越多个名字空间的名字,调用名字接口的程序不需要知道细节问题,只需要指定有关的名字,有关在几个名字系统中如何解析复合名字的问题留给服务提供商来解决,与客户无关。  6 DirContext 与 DirObject   对于目录服务的实现来说,如果不用扩展Context的DirContext接口,也可以使用一个单独的包含了所有目录相关方法的接口如DirObject,这样的话如果应用只使用目录服务就可以只包括DirObject,而如果名字服务和目录服务都使用,则可以包括Context和DirObject。这样当然条理比较清晰,但是对于某些混合操作,比如一些对目录和名字都有效的操作就不太方便了,所以JNDI采用了DirContext而不是DirObject。   7 Schema的支持   DirContext接口包含对schema的支持,例如,客户可以通过DirContext对象获得指向该DirContext实例的schema的定义空间的schema对象,或者获取该schema对象的类定义。Attribute类还更进一步地支持获取属性类型信息、属性定义等的方法。服务提供商既可以动态地返回这些schema信息,也可以静态地事先准备好有关的schema信息。  8 Context 和 DirContext   接口中的方法重载 在Context和DirContext接口中的每个接受Name参数的方法都有一个接受字符串参数的同名方法。 设计以字符串为参数的方法的原因是由于有很多应用只通过对象的名字来访问这些对象,对于这些应用来说,直接使用名字来访问这些方法当然是最直观的。   而设计以Name类对象为参数的方法的动机,也是由于有不少对名字进行维护的应用并不关系名字的字面表达,所以需要以Name对象作为参数。 在JNDI中这两种形式的方法都可以调用,以方便各种不同的应用。   9 引用   JNDI包容了多种使用目录来定位对象的方法,例如,一些应用直接将对象自身绑定在目录中;一些应用可能动态地产生目录树,当应用退出时就删去该树;另外一些应用可能只是将指向对象的URL存贮在名字空间里;还有一些系统可能将一些引用信息绑定到树中,当使用时再用这些信息来访问实际的对象。    针对这些不同的方式,JNDI定义了一个Reference类来为应用信息的表达提供一种统一的方式。Reference类包含了诸如地址、类型信息等用于访问具体对象的信息。为了能将对象的引用绑定到目录树中,该对象的类必须实现Referenceable接口,其中包含了方法 getReference() 。 Serializable接口与Referenceable接口有颇多相似之处,不同在于可引用的对象只包含一些用于创建实际对象的信息而Serializable会包含更多的甚至不适合存储在目录结构中的信息。   10 引用到实际对象的自动定位 对于作为引用绑定在目录树中的对象,JNDI SPI 指定针对引用创建实际的对象。因此,在程序中只需要认为用lookup()方法返回的对象就是实际对象,而不用在调用什么方法来将引用转换为实际对象了,所有的工作都由JNDI内部完成了。 <

小小豆叮

什么时候在EJB系统中使用XML

XML是一个非常流行的词,我们现在来讨论XML 在EJB开发中的适用性。 XML适用于以下几种情况: 1.作为对已有系统的接口。如果你有数目巨大的已 有系统,或者你有一个很大的内部联系紧密的已有系 统,然后你将需要查看你从已有系统收发的数据, 这时候XML可以帮助你。不是去收发那个已有系统能 够懂的合适结构的数据,而是你能够为那个已有系 统发明一种XML facade。那个facade从EJB组件得到 XML输入,然后把XML映射成那个已有系统支持的合 适结构。当那个已有系统对你的EJB应用系统返回数 据时,那个XML facade转换已有数据成能被你的EJB 应用程序懂得的XML数据。这对处理J2EE应用的商业 分析员也很有用。他们能用XML来帮助操作数据映射, 而XML是一种很容易懂的技术。 2.作为一种文档持久性机制。当你将大量的文档(如 新闻报道,文章,书本,等等)持久化,把他们用XML 表示是合适的。 3.作为一种web service的接口。EJB组件能被封装成 一种web service。XML成为在web服务之间传输的在 线数据格式。我们有一个怎么去构建基于web service 的J2EE的白皮书,也有怎么从另外一个J2EE系统得到 web service的资料,在http://www.theserverside.com。 有一个重要情况XML不是很有用,那就是EJB组件之间 通信的在线格式。 这个想法不仅仅是应用程序组件互相发送合适的数据 而是组件之间能通过把XML作为参数互相传递来互操作。 因为数据使用XML格式,每个组件能检查XML文档来 决定什么数据被收到。 虽然有些基于J2EE的流程解决方案使用这个方法, XML并不适用于EJB组件之间的通信,原因是性能问题。 解析XML文档需要时间,通过在线传输XML文档需要 更长的时间。为了高性能的企业级应用,在运行时或 常规操作中使用XML代价太高。当性能负担慢慢的变得 不那么重要,当XML解析器变得更高效,当人们开始使 用文本压缩去在线传送XML文档时,那么… 最重要的不去使用XML的原因是因为他通常不需要 被应用。假设一个单独的机构写出了所有你的EJB组件, XML可能不会被怎么使用,因为很少需要在不同系统 之间进行数据映射,因此你能独立控制对象模型… <

小小豆叮

使用Java实现网络传输数据的压缩

Q: 本周,我回答了两个使用Java进行数据压缩的问题. 第一个问题是: 我怎样才能压缩那些不在文件中的数据. 第二个问题是: 我以极大的热情阅读了Todd Sundsted的"压缩你的数据,从而提高你的网络应用程序的性能",但是读完后我却有点失望.当我读到文章标题时我很高兴.我想我总算找到了解决问题的办法了. 在我们的公司,我们试图提高一个组织数据的RMI应用程序的性能.服务器端进行了绝大部分的处理和优化.我们花了一年半的时间去提高性能,但是现在看来瓶颈在于数据的传输上.在一天的任何时间内,我们都有可能在客户和服务器之间传送成千上万的数据. 一种可能的解决办法,我建议我们能够在把数据返回给客户端时先压缩这些数据,这在Todd的文章中已经说得很清楚了.但是,文章中的例子却是压缩文件,而不是我们所需要的----对数据进行压缩. 在RMI中的实现中,我们先从数据库取得数据,再把数据放入一个列表中,接着把这个列表返回给客户端,最后再把它们插入JTable中.我想在把数据返回给客户时,首先把列表中的数据压缩,然后在客户端解压缩,最后把数据插入到表格中. 这样的想法可行吗? A:最近我收到了一些关于Todd的文章的疑问.很多读者看起来对文章中的举例很疑惑.因为文章中的例子是以文件压缩为核心的. 首先回答第一个问题,当你使用ZipInputStream 和 ZipOutputStream 并没有强制你必须使用文件.唯一要注意的是你必须把数据转换为字节数组的形式. 第二个问题比较棘手.在网络中,以RMI方式通信就需要作一些调整了.为了在传送数据之前就让RMI进行数据压缩,你必须创建一个能够压缩数据的新的套接字.然后,当你创建了一个套接字后,你得告诉RMI使用这一套接字. 以下是创建一个RMI形式的套接字的简要步骤: 1:选择或者创建一个新的套接字.(可以参看SUN'S的"创建一个典型的套接字"). 2:创建一个服务器端的套接字. 3:创建一个RMIClientSocketFactory 4:创建一个RMIServerSocketFactory 5:创建一个继承了UnicastRemoteObjec的远程对象,从而使用新的factories. 根据这一大致的想法,我们来看每一步如何具体的实现. 步骤1: 创建ZipSocket 由于要进行Zip压缩,我们重新创建这样的套接字 mport java.io.InputStream; import java.io.OutputStream; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import java.net.Socket; public class ZipSocket extends Socket { private InputStream in; private OutputStream out; public ZipSocket() { super(); } public ZipSocket(String host, int port) throws IOException { super(host, port); } public InputStream getInputStream() throws IOException { if (in == null) { in = new ZipInputStream(super.getInputStream()); } return in; } public OutputStream getOutputStream() throws IOException { if (out == null) { out = new ZipOutputStream(super.getOutputStream()); } return out; } public synchronized void close() throws IOException { OutputStream o = getOutputStream(); o.flush(); super.close(); } } 步骤2: 创建ZipServerSocket import java.net.ServerSocket; import java.net.Socket; import java.io.IOException; public class ZipServerSocket extends ServerSocket { public ZipServerSocket(int port) throws IOException { super(port); } public Socket accept() throws IOException { Socket socket = new ZipSocket(); implAccept(socket); return socket; } } 步骤3:创建ZipClientSocketFactory 客户端的factory的创建必须遵循以下的形式: import java.io.IOException; import java.io.Serializable; import java.net.Socket; import java.rmi.server.RMIClientSocketFactory; public class ZipClientSocketFactory implements RMIClientSocketFactory, Serializable { public Socket createSocket(String host, int port) throws IOException { ZipSocket socket = new ZipSocket(host, port); return socket; } } 步骤4:创建ZipServerSocketFactory import java.io.IOException; import java.io.Serializable; import java.net.ServerSocket; import java.rmi.server.RMIServerSocketFactory; public class ZipServerSocketFactory implements RMIServerSocketFactory, Serializable { public ServerSocket createServerSocket(int port) throws IOException { ZipServerSocket server = new ZipServerSocket(port); return server; } } 步骤5: 创建一个继承了UnicastRemoteObjec的远程对象,从而使用新的factories. public class YourRMIObject extends UnicastRemoteObject { public YourRemoteObject( int port ) { super( port, new ZipClientSocketFactory(), new ZipServerSocketFactory() ); } // 剩下的是你自己的程序实现 } 现在你的通信数据得到了压缩. 关于作者: Tony Sintes 是一个独立咨询人,同时也是First Class Consulting, Inc. 的创始人.这一咨询公司主要致力与对各个不同的企业系统进行量身定制和培训 . 业余时间,Tony 是一个积极的自由作家,同时也是Sams出版的<<21天学通面向对象编程>>的作者 (Sams, 2001; ISBN: 0672321092). 资源: To download the source code that accompanies this article, go to: http://www.javaworld.com/javaworld/javaqa/2001-12/ziprmi/01-qa-1207-ziprmi.zip "Zip Your Data and Improve the Performance of Your Network-Based Applications," Todd Sundsted (JavaWorld, November 1998): http://www.javaworld.com/javaworld/jw-11-1998/jw-11-howto.html "Creating a Custom RMI Socket Factory," (Sun Microsystems, 1999): http://java.sun.com/products/jdk/1.2/docs/guide/rmi/rmisocketfactory.doc.html "Creating a Custom Socket Type," (Sun Microsystems, 1999): http://java.sun.com/products/jdk/1.2/docs/guide/rmi/sockettype.doc.html Be sure to check out Tony's suggested reading list at: http://www.firstclassconsulting.net/reading.html For more RMI stories, visit the RMI / RMI-IIOP section of JavaWorld's Topical Index: http://www.javaworld.com/channel_content/jw-rmi-index.shtml Want more? See the Java Q&A Index for the full Q&A catalog: http://www.javaworld.com/columns/jw-qna-index.shtml For over 100 insightful Java tips from some of the best minds in the business, visit JavaWorld's Java Tips Index: http://www.javaworld.com/columns/jw-tips-index.shtml Learn the basics of client-side Java in our Java Beginner discussion. Core topics include the Java language, the Java Virtual Machine, APIs, and development tools: http://forums.idg.net/webx?50@@.ee6b804 Sign up for JavaWorld's free Applied Java newsletter: http://www.idg.net/jw-subscribe You'll find a wealth of IT-related articles from our sister publications at IDG.net 译者:我觉得这篇文章很有实用性,并且告诉了我们一种在网络通信时进行数据压缩的一个可行的解决办法.所以把它翻译出来,希望大家都能学到这一方法.这篇文章翻译起来比较简单.但是由于水平有限,如果不对的地方恳请大家指正. bootcool@263.net. <

小小豆叮