用 CodeRuler 征服中世纪王国

用这个新的、生动的图形模拟器拓展您的 Java 编程技能

developerWorks
文档选项
将此页作为电子邮件发送

将此页作为电子邮件发送

样例代码


拓展 Tomcat 应用

下载 IBM 开源 J2EE 应用服务器 WAS CE 新版本 V1.1


级别: 初级

Sing Li, Author, Wrox Press

2004 年 7 月 01 日

保卫您的城堡!占据您的领土!命令您的骑士英勇地战斗并击败他们的敌人。夺取敌人阵地并占领其土地,同时避开他们险恶的骑士。如果编写庞杂的 Java 代码让您感到沮丧,那么现在也许可以实现您的中世纪之梦了。在提高 Java 编程技能和掌握 Eclipse 开发环境的同时,可以管理您自己的王国。这都包括在超级 CodeRuler 中的难忘工作时光中了。模拟游戏爱好者 Sing Li 将您带入统治终极王国的快速通道。

CodeRuler 源自于 2004 ACM International Collegiate Programming Competition (请参阅 参考资料),它是 IBM alphaWorks 最新的幻想游戏模拟器挑战。这个游戏有一个简单的设定:您是自己的中世纪王国的统治者。您的农民和骑士有赖于您明智的战略思想、灵活的应变能力和过人的 Java 编程技能以生存、发展和繁荣。作为游戏参与者,您的目标是编写模拟这个统治者的 Java 代码。游戏模拟器让您的统治者对付六个对立的统治者(或者内置的示例统治者)并决定赢家。

本文引导您以简捷的方式统治您的中世纪王国。它揭示了游戏的环境、描述了规则、讨论了总策略,并提供了两个可以立即使用(或者修改)的、完善的可用统治者。

模拟环境

CodeRuler 是一个图形、生动的模拟游戏环境。作为一个中世纪统治者,您必须与其他统治者争夺土地和势力。您的王国包括:

  • 农民,可以占据和耕种土地。
  • 骑士,可以作战和俘获其他统治者的农民、骑士或占领城堡。
  • 一座可以产生更多骑士和农民的城堡。拥有的土地越多,产生他们的速度越快。

图形游戏世界

游戏在一个王国地图所描绘的二维世界中展开的。(背景地形草图只是作为墙纸,它不影响游戏的玩法或者改变游戏的进展。)图 1 展示了正在进行中的 CodeRuler 游戏。


图 1. 运行中的 CodeRuler

图 1 显示两个对抗的统治者。统治者——游戏对象战略性活动背后的策划者——不出现在游戏世界中。游戏对象(农民、骑士和城堡)是在模拟世界中移动的彩色点阵。图 2 展示了对象的形状及它们可能的移动方向。


图 2. CodeRuler 游戏对象的移动模式

从图 2 中可以看出,骑士和农民使用同样的移动模式。每回合,他们都沿着 8 种可能方向中的一个方向移动一格。每个方向有一个相关联的数字,您在 Java 编码中将使用这个数字。每一个数字还有一个预定义的常量(如 NW),在代码中会使用这些常量。

控制台得分显示

可以在 图 1的右侧看到状态控制台。 当前游戏中的统治者和他们所属的团体的名称出现在控制台的顶部。这两个数字是统治者的当前得分(左边)和农民拥有的土地方格数。图 3 展示了一个示例得分显示。


图 3. 控制台得分显示

在图 3 中,#18 号统治者名为 Simple Ruler from IBM developerWorks。这个统治者的当前得分是 123,这个统治者的王国占有 774 个方格的土地。可以在任何时候单击右上方的红色 X 中止模拟。

土地占有显示一览

可以在 图 1的状态控制台的中间看到缩小的世界。从图 4 中,很容易看出蓝色统治者占用的土地明显比洋红色的统治者占有的多。


图 4. 土地占有一览

模拟时钟

图 1状态控制台的底部是一个时钟。图 5 显示了特写图像。


图 5. CodeRuler 时钟

一个太阳沿着时钟的钟面移动行。当太阳移动了完整的一周时游戏就结束了。时钟的每一次跳动是模拟器一个回合。作为统治者,您决定每一回合自己的对象所作的移动。





回页首


战斗规则

每一个统治者最初控制:

  • 10 个农民
  • 10 个骑士
  • 1 个城堡
创造新农民和城堡

城堡创造农民或者骑士的速度取决于所拥有的土地方格数:

拥有的土地 创造一个农民或者骑士所需要的回合数
124 或者更少不创造
12514
25012
50010
1,0008
2,0006
多于 4,0004

在游戏过程中,要:

  • 利用农民占据尽可能多的土地(并保持占据状态)。

  • 利用骑士俘获对手尽可能多的农民,使他们不再占据土地。

  • 利用骑士与对手的骑士战斗并俘获尽可能多的骑士。这会削弱对手的防卸能力。

  • 利用骑士攻击夺取其他统治者的城堡。城堡是生产骑士和农民的工厂,没有它们就不能创造更多的农民或者骑士。如果有多个城堡,创造农民或骑士的速度就可以比对手更快。有关创造速度请参阅侧栏 创造新农民和城堡

  • 战略上防止自己的城堡失陷。

俘获游戏对象

只有骑士可以俘获对手的农民、城堡或者骑士。只要将它移动移动到农民和城堡的方格中,就可以俘获他们。要俘获对手的骑士,必须首先将它的体力值降低到 0。每一个骑士一开始的体力都是 100,对手每次攻击俘获,它都会随机损失 15 到 30 之间的一个体力值。执行成功俘获的骑士得到 20 个体力单位。

得分方案

要赢得游戏,在游戏结束时您所扮演的统治者必须有最高的得分。注意赢家拥有的土地可能是最多的,也可能不是。表 1 给出了游戏的得分方案。

表 1. 俘获的得分方案

如果... 那么得到
俘获一个农民4 分
俘获一个骑士6 分
占领一座城堡15 分

在游戏结束时,所剩下的对象、占领的城堡和占据的土地都添加到分数中,如表 2 所示。

表 2. 剩余对象的得分方案

剩余的对象 得分
农民1 分
骑士2 分
城堡25 分
土地每 10 个方格 1 分




回页首


游戏细节

每个玩家都编写模拟统治者的 Java 代码。游戏模拟器使您的统治者与其他统治者比赛并决定胜利者。在代码中,必须协调农民、骑士和城堡的移动。一组 API 提供了关于您的对象和其他竞争统治者的对象的信息。使用这组 API,可以编写实现进攻、防御、甚至随机应变的战略的代码。

见识 CodeRuler 背后的策划者

要了解 CodeRuler 引擎的内幕及高级战略的一些思路,请参阅对 Code Ruler 的创造者 Tim deBoer 的揭开内幕的 采访

游戏的组件

CodeRuler 游戏要求用 Eclipse IDE 编写、调试和测试自己的统治者代码。(请参阅本文后面的 Eclipse:集成的王国开发环境。)

CodeRuler 包括:

  • 一个交互式的游戏模拟器,作为 Eclipse IDE 的插件。
  • 可以用来编写统治者的 API 的文档。
  • 一组移动、俘获和得分规则。
  • 一个让自已的统治者与一组示例统治者对抗的本地竞技场。
  • 一个联网机制,用于提交统治者,以参与公开赛或者设立自己的锦标赛。

模拟世界坐标系统

这个游戏是在一个包含 4608 个方格--宽 72 个方格、高 64 个方格——的模拟世界中展开的。方格是按 ( xy) 坐标系统编号的。 x轴从左到右, y轴从上到下。图 6 显示了 CodeRuler 世界的布局。位置 (0,0) 在左上角。


图 6. CodeRuler 世界坐标系统

CodeRuler API 和继承层次

在命令游戏对象之前,需要理解 CodeRuler API。这个 API 是高度面向对象的,并有明确的继承层次。理解层次结构是编写高效 CodeRuler 代码的关键。图 7 显示了继承层次结构。


图 7. CodeRuler 继承层次结构

图 7 中的继承树基于 Java 接口。每一个游戏对象都必须实现它相关的接口:农民必须实现 IPeasant 、骑士必须实现 IKnight 等。不过,永远不需要编写其中任何类,因为 CodeRuler 模拟器使用内置的实现。作为一个统治者,只有在需要得到关于游戏对象的信息时才需要使用接口提供的 API。

IObject 接口
IObject 接口是游戏中所有对象的父接口。每一个对象间接实现 IObjectIObject 提取了所有对象的共有行为:

  • getRuler() :对象所属的统治者
  • getX(), getY() : 对象的当前位置
  • isAlive() : 这个对象是否是活着的(即没有被俘)
  • getId() : 惟一 ID (跨越所有对象——骑士、农民和城堡)

IObject 父接口还有两个方便的方法。在设计战略时这些方法很有用、并可帮助您避免使用复杂的三角数学:

  • getDirectionTo() 计算到图中指定点最接近的方向。
  • getDistanceTo() 计算到图中指定点的距离。

IPeasant 接口
IPeasant 接口没有对 IObject 接口增加新的行为。可以用 Ruler 的 move() 方法移动农民,这会改变他们的位置。您用农民占据土地。农民的自动行为是占据他走过的任何土地。敌对的骑士可以通过移动俘获农民,不涉及体力计算。

ICastle 接口
ICastle 接口就像 IPeasant 接口,不对 IObject 接口增加新的行为。一个城堡的自动行为是创造更多的农民或者骑士。创造速度取决于拥有的土地数量。有关创造速度,请参阅侧栏 创造新农民和城堡

IKnight 接口
IKnight 接口对 IObject 接口增加了一个名为 getStrength() 的方法。一个骑士在其体力值降低为零时被俘获。可以在战略中用 IKnight 接口的 getStrength() 方法避免损失骑士。有关骑士的体力计算的讨论请参阅本文前面 俘获游戏对象

图 7 中的接口层次结构表示了模拟期间在世界中移动的游戏对象。不过,统治者不是游戏对象,并不在模拟世界中移动。 IRuler 接口指定统治者的行为。

IRuler 接口
IRuler 接口不需要——并且没有——继承 IObject 。图 8 显示了 IRuler 接口的继承层次。


图 8. IRuler 接口的继承层次

IRuler 接口指定了所有统治者实现的一般性行为。其中包括实现战略所需要的获取信息的方法:

  • getPeasants() 得到该统治者名下的所有农民。
  • getKnights() 得到该统治者名下的所有骑士。
  • getCastles() 得到该统治者名下的所有城堡。
  • getLandCount() 得到该统治者所拥有的土地方格数。
  • getPoints() 得到该统治者当前所赢得的分数。
  • getRulerName() 得到统治者的名字。
  • getSchoolName() 得到创造该统治者的团体名称。

Ruler 和 MyRuler 类
为了加强特定于游戏规则的行为,并且帮助实现 IRuler 接口,CodeRuler 提供了如 图 8 所示的 Ruler 类。这个类提供了大多数 IRuler 方法的默认实现。您要编写 MyRuler 类的内容,它必须继承 Ruler 类。您不需要、也不应该修改 Ruler 类。

模拟器的工作流程

从 CodeRuler 玩家的角度看,这个模拟器有下面的工作流程:

  1. 将初始游戏对象放到模拟世界中随机选择的王国位置
  2. 调用您实现的 initialize() 方法
  3. 每一回合调用您的 orderSubjects() 方法

getRulerName()getSchoolName() 方法不应当包含战略代码。模拟器可以在任何时候调用它们。

Ruler 提供了几个应当在自己的 MyRuler 实现中使用的关键操作方法:

  • move() 在世界中移动对象。
  • capture() 攻击俘获对手的游戏对象。

Ruler 还实现了几个会改变城堡的生产模式的方法。在默认情况下,城堡不断地制造农民。不过,可以使用这些方法告诉城堡要制造骑士:

  • createKnights() 告诉城堡制造骑士。
  • createPeasants() 告诉城堡制造农民。

最后,需要存在 Ruler 类的理由之一是为了在自己的 MyRuler 类中定义其他必须实现的抽象方法。模拟引擎在执行期间调用这些方法。

惟一必须编写的代码是在表 3 中所列方法的实现代码:

表 3. 所有 MyRuler 实现中的方法

方法 说明
getSchoolName() 返回一个有 25 个或者更少字符的字符串,它标识了您的小组或者团体。(CodeRuler 原来是为同事之间的竞争而设计的。)在游戏过程中确定统治者时将会用到它。例如,在 图 1中,Simple Ruler 的 school name 是 IBM developerWorks。
getRulerName() 返回一个有 25 个或者更少的字符的字符串,它惟一地标识了统治者。例如, 图 1中的一个统治者名为 Simple Ruler。
initialize() 当第一次将统治者放到游戏中时,系统调用这个方法。在这里执行所有必需的初始化。初始化的时间限于一秒钟。计算机在初始化时可以完成的工作随着 CPU 速度和所使用的 Java VM 而变化,但是一秒钟对于大多数代码初始化任务是足够了。不要试图执行任何依赖慢速输入/输出的工作。
orderSubjects() 这是 CodeRuler 的核心。每回合系统调用一次这个方法。需要使用战略并告诉您的对象在这个方法中做什么。




回页首


Eclipse: 集成的王国开发环境

需要下载并安装 Eclipse IDE (版本 2.1 或者更新版本) 以运行 CodeRuler 模拟环境(请参阅 参考资料)。CodeRuler 作为插件集成到 Eclipse IDE 中,因而可以利用 Eclipse 的开发人员友好的特性。

安装 Eclipse 和 CodeRuler

要安装 Eclipse,将发布文档解压缩到一个目录中并运行 eclipse 可执行文件 (在 *nix 中是 eclipse,在 Win32 系统中是 eclipse.exe)。需要已安装 JDK/JRE 1.4.2 或更新版本。(强烈推荐版本 1.4.2,因为 CodeRuler 是在这个 VM 版本中开发和测试的。) 在安装了 Eclipse 后,下载 CodeRuler 引擎 (请参阅 参考资料)。要安装 CodeRuler,需要将 CodeRuler 发布文档解压缩到 < eclipse 安装目录>/plugins 目录。这会在 pugins 目录中创建一个 com.ibm.games 目录。启动或者重新启动 Eclipse,将会装载 CodeRuler 插件。现在就可以使用 CodeRuler 了。

创建自己的 CodeRuler 项目

需要在 Eclipse 中创建一个新项目以使用 CodeRuler。从主菜单中,选择 Windows|Preferences。会弹出一个对话框,如图 9 所示。


图 9. 创建新的 CodeRuler 项目

在左边的列表中选择 IBM Games,如图 9 所示。然后,从右边的 Game 列表中选择 CodeRuler。最后,单击 OK以用模板创建一个新的 CodeRuler 项目。现在就可以编写自己的 CodeRuler 了。

在 IDE 左边的选项卡栏中,单击 Java Perspective 选项卡。图 10 表现了这个选项卡。


图 10. 在 Eclipse 中选择 Java perspective

展开 src 节点,默认包展现了 MyRuler.java 节点,如图 10 所示。双击 MyRuler.java 节点会在源代码编辑器中打开这个文件以进行编辑。必须在这里加入自己的代码。





回页首


编写第一个统治者

创建的第一个统治者很简单。它随机地移动所有的农民。清单 1 显示了这个统治者的代码,用粗体突出显示了添加的代码。

及时完成的重要性

在编写统治者代码时,要了解所遇到的时间约束。对于 initialize() 方法,只限于一秒钟,这对于所有没有输出/输出的初始化代码是相当多的时间了。如果花费了一秒以上的时间就会被中断(preempted)并可能只完成了部分初始化。在游戏的每一回合, orderSubjects() 方法限制为半秒钟。 orderSubjects() 调用的输入参数可以让您知道在上一回合中使用了多少时间。如果超过了时间限制,就没有资格参加其余的比赛了。


清单 1. Simple Ruler 的 orderSubjects() 实现

				
        import java.util.Random;
...

        
        protected Random rand = new Random();

public String getRulerName() {
   
        
        return "Simple Ruler";
   }
public String getSchoolName() {
   
        
        return "IBM developerWorks";
   }

public void orderSubjects(int lastMoveTime) {
   
        
        IPeasant[] peasants = getPeasants();
   for (int i = 0; i < peasants.length; i++) {
     move(peasants[i], rand.nextInt(8) + 1);
   }
}

      
      

清单 1 中的代码用 java.util.Random 生成 1 到 8 之间的一个随机数。这个数决定了农民移动的方向。注意使用了 Ruler 类的 getPeasants() 方法获得所有农民的数组,使用了 move() 方法移动农民。

随机移动农民可以使他们占据土地。但是因为这个统治者并不试图俘获任何东西,所以代码不需要移动骑士。

CodeRuler “不要做的事情”列表

聪明的战略对于赢得游戏是很关键的,但是 CodeRuler 不鼓励使用 Java 语言功能绑架游戏引擎或者用其他不正当的方法取胜。代码 不应当

  • 定义构造函数
  • 使用初始化块
  • 创建线程
  • 创建进程
  • 写入文件
  • 使用 JDBC
  • 使用 Swing 或者 AWT 创建 GUI 组件
  • 访问网络或者其他类似的系统功能
  • 使用反射和内省发现并穿过模拟器内部结构

在所以公开比赛和锦标赛中,使用这些黑客技术的玩家会被取消比赛资格。一个定制的 Java SecurityManager 会抓获大部分这种企图。

第一场比赛的战斗

要试验第一场比赛,首先通过单击工具栏中的保存按钮或者选择菜单中的 File >Save保存最新编辑的统治者。保存还会编译代码。在继续进行之前改正所有打字错误或者语法错误。

您会注意到 5 个特定于 CodeRuler 的图标按钮,如图 11 所示。


图 11. 在 Eclipse 工具栏中集成的 CodeRuler 按钮

表 4 说明了图 11 中从左到右的按钮的功能。

表 4. CodeRuler 按钮的功能

按钮 说明
Run against samples用这个按钮与所选的示例统治者对抗以测试自己的统治者。
Debug against samples用这个按钮与所选的示例统治者对抗以测试自己的统治者。以调试模式运行统治者,在所设置的中断点处停止。
Run against other teams在提交了代码后,会下载其他小组的统治者。用这个按钮与其他小组的统治者对抗以测试自己的统治者。
Debug against other teams在与其他小组的统治者对抗以测试自己的统治者时,以 IDE 的调试模式运行统治者。
Submit code提交统治者。这还会下载以前其他小组提交的所有统治者的一个加密包。

在第一次试验中,您的第一个统治者将会只与示例统治者对抗。这意味着您将只使用第一个按钮,即 图 11中突出显示的那个按钮。单击这个按钮时,CodeRuler 就会启动并装载您的统治者。您将有机会选择对手,如图 12 所示。


图 12. 选择比赛对手

试着增加一个 Do Nothing Ruler。开始比赛并观察农民是如何随机移动并占据土地的。可以容易地赢得这场比赛。

然后,试一下 Random Ruler。这个统治者的行为与您的统治者几乎一样。平均占有的土地大致相同。

如果与任何其他示例统治者对抗,那么您所创建的 Simple Ruler 很可能会输。大多数其他示例统治者会积极地俘获您的对象。现在应该在这个 Simple Ruler 中加入进攻能力了。

创建一个进攻性统治者

清单 2 显示了修改后的统治者代码,突出显示了增加的代码。


清单 2. 修改后的 Simple Ruler 实现,它积极获对手

import com.ibm.ruler.*;

        
        import java.awt.Point;
import java.util.Random;
import java.util.Vector;

public class MyRuler extends Ruler {
	public String getRulerName() {
	  
        
        return "Simple Ruler";
	}

	public String getSchoolName() {
	  
        
        return "IBM developerWorks";
	}

	public void initialize() {
	}
    
        
        protected Random rand = new Random();
				
        
    protected Vector enemies = new Vector();

    public void orderSubjects(int lastMoveTime) {
		
        
        
            IPeasant[] peasants = getPeasants();
		IKnight[] knights = getKnights();
				
        
            for (int i = 0; i < peasants.length; i++) {
		  move(peasants[i], rand.nextInt(8) + 1);
		}
            
				
        
            enemies.clear();
		IPeasant[] otherPeasants = World.getOtherPeasants();
		IKnight[] otherKnights = World.getOtherKnights();
		ICastle[] otherCastles = World.getOtherCastles();
		
		for (int i=0; i<otherPeasants.length; i++) {
		   enemies.add(otherPeasants[i]);
			
		}
		for (int i=0; i<otherKnights.length; i++ ){
		   enemies.add(otherKnights[i]);
		}
		for (int i=0; i<otherCastles.length; i++) {
		   enemies.add(otherCastles[i]);
		}
            
				
        
            int size = knights.length;
		for (int i = 0; i < size; i++) {
	        IKnight curKnight = knights[i];
		  if (!enemies.isEmpty()) {  
		    IObject curEnemy = (IObject) enemies.remove(0);
		     moveAndCapture(curKnight, curEnemy);
		   }
		   else
		      break;		  	
	 	 } // of outter for   
                  	
           }
	


  public void moveAndCapture(IKnight knight, IObject enemy) {
	if ((enemy == null) || !enemy.isAlive())
	  return;
	 // find the next position in the direction of the enemy
	int dir = knight.getDirectionTo(enemy.getX(), enemy.getY());
	Point np = World.getPositionAfterMove(knight.getX(), knight.getY(), dir);
	
      if (np == null)
        return;
      if ((np.x == knight.getX()) && (np.y == knight.getY())) {
	  move(knight, rand.nextInt(8) + 1); 	
        	return;
       }		
       
	// capture anything that is in our way
	IObject obj = World.getObjectAt(np.x, np.y);
	if ((obj != null)  && (obj.getRuler()!= this))
	  capture(knight, dir);
	else
	  move(knight, dir);
    }
 }

      
      

万能的 World 对象

在开始编写大量的统治者编码之前,要保证花同样多的时间研究 World 对象的文档。这个对象包含许多静态方法,它们对于实现您的战略会很有用。例如,可以使用 getLandOwner() 找出谁拥有某个格子的土地、用 getObjectAt() 标识特定位置上的游戏对象、用 getOtherPeasants()getOtherKnights()getOtherCastles()getOtherRulers() 发现敌人。

您将体会到清单 2 中用绿色突出显示的代码的作用。

用红色突出显示的代码设置了一个包含在模拟世界中所有活着的敌方游戏对象的 Vector 。注意使用 World 对象得到这个信息(即 World.getOtherPeasants() )。

用蓝色突出显示的代码遍历您的所有骑士并使他们朝着活着的对方游戏对象移动。它还俘获所有可能遇到的敌对对象。 它使用 moveAndCapture() 方法移动和俘获。

moveAndCapture() 方法使特定的骑士移向特定的敌方对象。它使用 World 对象的 getPositionAfterMove() 方法确定骑士是否陷入困境,如果是,就进行随机的移动。它还使用 World 对象的 getObjectAt() 方法测试并俘获可能在其路上的所有敌方对象。

试着用这个新的 Simple Ruler 与一些示例统治者对抗。您将看到它对其中的很多统治者可以有不错的进展。当然,还有很大的改进余地。作为一个练习,您可以试着这样修改代码:

  • 命令您的城堡在骑士数量降低时生成骑士。
  • 更有效地为骑士指定目标。
  • 使农民更有效地占据土地。
  • 使农民躲避俘获。
  • 当农民和骑士的数量变低时,转为防御性生存战略。




回页首


结束语

由您来选择:从最简单的基于试探的机械式统治者到最复杂的、由统计游戏理论模型驱动的指挥官,CodeRulers 提供了所有可能性。就像在真实世界中一样,最复杂的战略和复杂的编码未必能保证胜利。事实上,一些冠军统治者使用了最简单、然而最精彩的游击战术。如果战略设计和 Java 开发是您的最爱,那么您应当试试 CodeRuler。






回页首


下载

名字大小下载方法
两个简单统治者的示例代码  FTP
关于下载方法的信息Get Adobe® Reader®




回页首


参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 了解世界上历史最悠久、最大和最有声望的编程竞赛 ACM International Collegiate Programming Contest的更多信息。

  • 从 IBM alphaWorks 下载最新版本的 CodeRuler模拟引擎和相关的文档。

  • 请访问 eclipse.org以得到最新版本的 Eclipse IDE、文档、邮件列表和社团新闻。

  • 查看另一个由与 CodeRuler 同样的模拟引擎支持的经典模拟游戏。 Code Rally让您坐到一个汽车拉力赛的座位上!

  • 有关 alphaWorks 上另一个非常流行的战斗模拟游戏的情况,请访问 Robocode。这个活跃的 Robocode 玩家国际团体一定会给您带来挑战的。

  • 通过 Sing Li 的文章 “ 重锤痛击 Robocode!” ( developerWorks,2002 年 1 月) 和 “ Rock 'em, sock 'em Robocode: Round 2” ( developerWorks,2002 年 5 月) 了解更多有关 Robocode 的信息。

  • 请访问 Developer Bookstore,获取技术书籍的完整列表,其中包括数百本 Java 相关的书籍。

  • developerWorksJava 技术专区 上可以找到到数百篇 Java 技术文章。

  • 是否对无需通常的高成本入口点(entry point )或短期评估许可证的 IBM 测试产品感兴趣? developerWorks Subscription为 WebSphere ®、DB2 ®、Lotus ®、Rational ®和 Tivoli ®产品提供了低成本的 12 个月单用户许可证,包括基于 Eclipse 的 WebSphere Studio IDE,用于开发、测试、评估和展示您的应用程序。




回页首


关于作者

Author photo

Sing Li 是 Professional Apache Tomcat 5 Pro JSP, Third Edition Early Adopter JXTAProfessional Jini和 Wrox 出版社的其他许多书籍的作者。他定期为技术杂志投稿,同时还是 P2P 革命的积极推动者。Sing 是一位咨询专家和自由撰稿人,可通过 westmakaha@yahoo.com与他联系