目 录

终于写完啦!

自从大一接触Java以来,四年了,每天不Java一下就手痒,每天不Java一下就觉  得有些失落,Java已经成为我的生活的一个部分。Java给予我很多的乐趣,我想我应该为Java作些什么。四天前,当我开始接触J2ME,当我使用 J2MEWTK成功运行HelloWorld程序的时候,这个念头再次浮现在我的脑海里。我应该为Java做些自己力所能及的事情,所以才会有这篇不算太 长的文章《J2ME学习札记》。不管别人的看法怎么样,我总算是了了自己的一番心事。我之所以写这篇文章,还有另一个目的,那就是将我从Java中得到乐 趣与你分享,希望你也能够将你从Java中获取的乐趣写出来,与大家分享。

标 题: J2ME学习札记(1)------什么是J2ME?

J2ME就是Java 2 Platform,micro Edition的缩写。

J2ME是Java 2的一个组成部分,与J2SE、J2EE并称。根据Sun Microsystems,Inc.的定义:J2ME是一种高度优化的Java运行环境,针对市面上的大量消费类电子设备,例如Papers、 cellularphones(蜂窝电话), screen-phones(可视电话?)、digital set-top boxes(数字机顶盒)、car navigation systems(汽车导航系统)等等。 J2ME技术在1999年的JavaOne Developer Conference大会上推出。J2ME技术将Java语言的与平台无关的特性移植到小型电子设备上,允许移动无线设备之间共享应用程序。

标 题: J2ME学习札记(2)------我需要什么?

为了学习J2ME技术,我需要安装什么样的软件?

1)、J2ME Wireless Toolkit
J2ME Wireless Toolkit简称J2MEWTK,目前最新的版本就是1.0.3
Beta,J2MEWTK有什么用?打一个比方,J2MEWTK之于J2ME程序的开发,就相当于JDK之于Java程序的开发。J2MEWTK是由 Sun
公司开发的,你可以到Sun的网站去下载,这个工具和JDK一样,是完全免费的。

2)、J2SE SDK 1.3
J2SE SDK 1.3亦即JDK 1.3,JDK有什么用?相信学过Java语言的人都知道,我就不多写了,在安装J2MEWTK之前,你必须确保你的系统已经安装了一个可用的JDK。建 议不要使用JDK 1.2和JDK 1.4,前者版本低,后者不稳定。

3)、Tomcat 3.2.3/4.0
Tomcat 3.2/4.0用作服务器软件,为J2ME手机提供服务。由于手机的资源有限,所以我们需要尽量将商业计算集中于服务器端完成,减小客户端的负担,所以要 开发J2ME程序,一个服务器软件是少不了的。我采用JSP作为服务器端的解决技术,所以我选用的Web服务器是Tomcat。但是你也可以选用 IIS+ASP、Apache+PHP。这个选择不是唯一的。


标 题: J2ME学习札记(3)----开发环境安装

第一步,安装JDK 1.3.0或者安装一个包含JDK 1.3.0的软件。我安装了Jbuilder
5.0,它所带的JDK是HotSpot 1.3.0_01 Client版。

第二步,安装J2MEWTK 1.0.3 Beta,在安装的过程中,需要选择系统中JDK的安装目录,安装程序一般可以自动查出来JDK的安装目录,我们不用操心。

第三步,安装Tomcat 3.2.3/4.0,Tomcat 3.2.3是最稳定的3.x系列,推荐使用。Tomcat 4.0刚刚发布了正式版,也不妨一试。Tomcat 3.2.3/4.0需要经过配置,才能够运行。

第四步,测试Tomcat是否能够正常运行JSP程序。这一步很重要。

标 题: J2ME学习札记(4)-----J2ME开发工具比较

1)、J2MEWTK,这个工具在前文已经提到过,它是最基本的J2ME程序开发工具,免费,体积小,速度较快,完全遵守J2ME的各种规范。具有简单的 IDE界面,易于上手,开发十分方便快捷,可以和Forte 3.0捆绑。J2MEWTK适用于初学者和已经达到很高水平的开发者。窃以为J2MEWTK+JDK+Editplus/UltraEdit是绝配。

2)、VisualAge Micro Edition 1.4。这是IBM的产品,号称是J2ME开发领域的TOP
1,但是我用了半天,也没有看出好在那里。马上就删除了。窗口太复杂,不明所以,开发起来很难适应,速度和J2MEWTK一样,比较庞大,装了这个东西, 你的C盘就要小心了,多了很多乱七八糟的文件,还注册了许多COM组件,典型的非绿色软件。

3)、CodeWarrior for Java 6.0。这是Motolola的产品,功能十分强大,集成度很好,开发,调试,发布J2ME程序都很方便(还可以做一般的Java Program)。它的IDE和Visual Studio十分相似,很容易上手。CodeWarrior比较适合中等水平的开发者的使用。不过CodeWarrior不是免费软件,你只能够免费使用 30天。

4)、Borland Jbuilder的Nokia Bobile版。没用过。

上述分析,仅供参考,仅代表本人意见。

标 题: J2ME学习札记(5)------选择缺省的开发设备

在开发J2ME应用程序之前,我们必须选择这些程序运行的平台,亦即开发出来的J2ME程序运行在那一种手机或者是移动设备上面。在开发的过程中, 我们需要对J2ME应用程序进行测试,当然了,自己拥有一款支持J2ME的手机是最好的,例如Motolola的i85s,但是这个要求不是每一个开发者 都能够满足(我连手表都没有,更不用说手机了),因此,大多数的J2ME开发环境都提供了各种各样的手机、移动设备模拟器,你可以首先在手机模拟器上测试 你的J2ME程序,开发完毕以后,再将程序交给专业的厂商(一般是移动设备提供商),由它们将程序载入手机中进行真实的程序测试。模拟器和真实的设备之间 有一定的差别,但是你的程序只要在模拟器上通过了,那么问题不会很大。

J2MEWTK提供六种模拟器,分别是:Default Color Phone、Default GrayPhone、Minimum Phone、Motolola i85s、PalmOS Device、RIM Java Handle。这些模拟器虽然外观不一样,操作也不太一样,但是J2ME程序在其上运行的结果是不会有什么区别的,在真实的设备上也是没有什么区别的,这 体现了Java的跨平台特性。你还可以自定义自己的手机模拟器,这在J2MEWTK的User Guide中有详细的描述。

在开发过程中,选择何种模拟器是十分重要的,原则上来说,你为那一种设备开发程序,就要使用那一种设备的模拟器。但是J2ME程序在不同的模拟器上的运行 效果基本上是一致,所以你也可以采用你自己喜欢的模拟器。我们推荐你选用RIM Java Handle模拟器作为开发的首选模拟器,因为它的屏幕很大,看起来很舒服(但是别忘了你的真实设备的屏幕大小)。

在J2MEWTK中,可以设定缺省的模拟器,怎么设呢?从开始菜单选择J2MEWTK---->Default Device Selection,在下拉列表框中选择模拟器的名字,然后单击OK按钮即可,J2MEWTK就会把这种模拟器认为是缺省的模拟器。你也可以在开发环境中 每次手动选择模拟器,不过这样做显然比较麻烦。J2MEWTK在初始情况下,

默认的模拟器是Default Color Phone,说实在话,我从来没有看见过这么难看的手机。

标 题: J2ME学习札记(6)-----创建项目

现在开始尝试开发J2ME程序。我的选择是Windows XP 2600 Professional+J2SE SDK 1.3.0+Tomcat 4.0+J2MEWTK 1.0.3Beta。从开始菜单中选择
J2MEWTK---->Ktoolbar。Ktoolbar是J2MEWTK提供的一个简陋的IDE工具。

进入Ktoolbar以后,观察窗口,在菜单的下面有四个按钮,分别是:New Project:
创建一个新的项目。

Open Project:打开一个项目。
Setting:对当前项目的环境进行设置。
Build:编译项目中的所有Java文件。
Run:启动缺省的模拟器,将当前项目载入,运行。
Clear Console:清除控制台输出。

在上述按钮的下面,有一个下拉列表框,在这里你可以设置当前项目所使用的模拟器,这个设置可以覆盖缺省的模拟器。在此下拉列表框下面,是一个文本框,这就 是所谓的控制台了。所有编译、运行信息都会在这个控制台中输出。你可以使用Clear Console按钮将控制台中的信息完全清除。

现在来看看菜单。Ktoolbar的菜单极其简单,没有什么可说的。Project菜单的package菜单项的作用是将当前项目打包输出。这个菜单项特 别有用,当你完成项目开发之后,使用这个菜单项可以产生一个jar文件,这样就完成的项目的初步发布。

现在来创建一个新的项目,单击New Project按钮,或者是使用File菜单的同名菜单项。出现一个新窗口。这个新窗口有两个文本框,第一个文本框是ProjectName,输入 fancy。第二个文本框是MIDlet Class Name,输入fancy.test.HelloWorld。

然后单击OK,又出现一个对话框,要你配置项目的环境,不理,单击OK按钮关闭该窗口,回到Ktoolbar的主窗口。经过上述步骤,你已经创建了一个名 为fancy的J2ME项目。

标 题: J2ME学习札记(7)-----J2MEWTK的目录结构

安装J2MEWTK以后,你应该仔细浏览一下J2MEWTK的目录结构,这是一个很好的习惯。J2MEWTK的目录结构如下:

appdb文件夹:里面有duke的一些靓照。
apps文件夹:里面有J2MEWTK的一些例子程序,我们建立的项目文件也存放在里面。
bin文件夹:里面全部是exe程序。
docs文件夹:不用多说了,是地球人都知道。
lib文件夹:存放MIDP API。
wtklib文件夹:存放J2MEWTK用到的类库,以及一些资源文件,比如按钮的图标等等。

在这些文件夹中,apps文件夹特别需要留意,这个文件夹有下列子文件夹:
example:存放J2MEWTK的例子,侧重于图形方面,例子比较大,复杂,难看懂。
lib:空,不知道放什么东西。
tmplib:空,不知道放什么东西。
UIDemo:存放J2MEWTK的例子,侧重于用户界面设计方面。例子不大,难度中等。
fancy:这个文件夹原来是没有的,当我们创建fancy项目的时候,J2MEWTK自动为我们创建的文件夹。

进入fancy文件夹,它里面又有很多子文件夹,如下所示:
bin:存放项目的打包输出文件。
classes:存放编译器产生的class文件。
lib:空,不知道有什么用。
res:存放资源文件,例如项目中用到的图片。
src:存放项目的源代码。
tmpclasses:存放编译器产生的class文件,是classes文件夹的镜像。
tmplib:空,不知道有什么用,是lib文件夹的镜像。


标 题: J2ME学习札记(8)-------HelloWorld!

现在该是使用J2ME说Hello World的时候了。选择你最喜欢的文本编辑器,例如Editplus

输入下面的代码:

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class HelloWorld extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public HelloWorld() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

然后将该文件保存在J2MEWTK_HOMEappsfancysrcfancy est目录下面,文件名为HelloWorld.java。注意:你需要在fancysrc目录下面创建fancy文件夹,然后再在fancy文件夹下面 创建test文件夹。最后才保存HelloWorld.java文件。
现在转到J2MEWTK的主窗口,单击Build按钮,编译整个项目,查看控制台的输出信息,一切无误,再单击Run按钮,运行此程序

1) package fancy.test;
这行代码声明当前类所在的包。这是有必要的。而且这个包名必须和src文件夹中的目录结构对应。

2)
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
  
这两行代码导入必要的Java包,这两个包的作用在后面会提及,这里就不多说了。


3)public class HelloWorld extends MIDlet implements CommandListener
J2ME程序一般应该继承MIDlet,实现CommandListener。就如Applet必须继承Applet,可能实现Runnable接口一 样。

4)private Display display;
private Form props;
定义两个私有对象,Display代表屏幕,显示区域。Form是容器的一种。在J2ME程序中,不但有容器的概念,还有画布(Canvas)的概念。这 个程序在Form容器中显示文本。

5)private Command exitCommand = new Command("Exit", Command.EXIT, 1);
声明一个Command对象。J2ME的事件处理机制和J2SE的事件处理机制不太一样。在J2ME程序中,必须预先定义一些Command对象,注册到 程序中。当设备发生了某个事件,会产生相应的Command对象,并把它传递给一个事件处理函数----commandAction(),由它对所产生的 事件做统筹处理。


6)public HelloWorld()
{
  display = Display.getDisplay(this);
}
这个是构造函数,函数内部,调用Display对象的静态方法---getDisplay(),获取
屏幕对象,实例化display变量。这个调用是必要的。你可以在构造函数中做这个工作,也可以在startApp()方法中做这个工作。推荐在构造函数 中完成。

7)MIDlet程序的运行流程

构造函数---->startApp()------>侦听事件,接受命令------->commandAction()方法 ------->调用别的方法----------->如果是exit命令 --------->pauseApp()--------->destroyA
pp()方法。实际上MIDlet程序的运行流程和Applet程序的运行流程差不多。

8)

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");

props.addCommand(exitCommand);
props.setCommandListener(this);

display.setCurrent(props);
}

这是startApp()方法。这个方法是父类的抽象方法,在子类中必须予以覆盖。首先实例化Form对象 ----------props,Form的构造函数的参数(Hello World)就是屏幕的标题。

Form对象是一容器,在里面可以包含别的东西,props.append("…..");的作用就是在这个容器中存放一个字符串。这个字符串会在屏幕中 显示出来。

接下来的三行代码分别做这样的工作:
将Exit命令注册到Form对象(props)中,这样Form对象(props)可以对该命令作出响应。
设置Form对象(props)的命令监听者。
将Form对象设置为屏幕显示的对象。
你可以试着注释掉这三行代码,再编译运行这个程序,看看会发生什么情况。

9)

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

这个方法是事件处理的中枢,它接受各种命令,并对其进行分析,再分别调用合适
的处理方法。在这个例子中,当接收到Exit命令以后,马上销毁程序,退出。

10)destroyApp()方法的作用是退出程序并销毁程序对象。pauseApp()方法的作用是暂停程序,并销毁容器对象或者是画布对象。 手机屏幕将会是一片空白。

javax.microedition.lcdui:用户界面包,主要用于构造程序的用户界面。
Command、Form都是这个包的类。
javax.microedition.rms:这个包实现了对手机数据的存取功能。
javax.microedition.midlet:这个包是MIDlet程序的声明周期包,主要定义了MIDlet类,MIDlet类是一个抽象类, 里面声明了startApp()、destroyApp()、pauseApp()等抽象方法。
javax.microedition.io:网络IO包。有HttpConnection接口和Connection接口、Datagram接口。
java.io.*
java.lang.*
java.util.*
上面这三个包属于J2ME核心包,J2ME中的核心包和J2SE中的同名核心包有些差别,主要是功能大大简化了,许多类、方法都没有了,只能实现一些最基 本的功能。

J2ME开发中有中文问题吗?可能有,但是我目前没有遇到。因为我没有手机,只能在模拟器上运行J2ME程序,真实的情况是什么样子我也不知道。在 水木上有人说已经出现了中文问题,在模拟器上好好的,到了真正的手机上却是一团乱码。我现在也没有办法,只有在遇到的时候再补上这一节。我写的测试程序如 下所示,这个程序是在HelloWorld.java的基础上改进而来的。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class Poem extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public Poem() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("影落寒潭的签名档");
props.append("小楼一夜听春雨 ");
props.append("深巷明朝卖杏花 ");
props.append("虹虹的签名档 ");
props.append("鸳鸯独宿何曾惯 ");
props.append("化作西楼一缕云 ");
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

使用普通的编辑器编辑好上述文件以后,保存为Poem.java,保存路径为

srcfancy est。然后在Ktoolbar中编译,一切无误之后,单击Setting按钮,出现一个配置窗口,选择MIDlets面板,单击Add按钮,依次输入 Poem、fancy.png、fancy.test.Poem三项。单击OK按钮,再单击OK按
钮,关闭配置窗口,回到Ktoolbar的主界面,再次编译。一切无误之后,单击Run按钮运行程序。
注意:每新编写一个程序,都要按照这个步骤进行配置,再编译运行,我以后就不再重复描述这个步骤了。

请看下面的代码(Prop.java):

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class Prop extends MIDlet implements CommandListener {
private Display display;
private Form props;


private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public Prop() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("System Properties");
props.append("Hello World! ");
long time = System.currentTimeMillis();
props.append("current time:" + time + " ");
props.append("microedition.configuration:" +
System.getProperty("microedition.configuration") + " ");
props.append("microedition.profiles:" +
System.getProperty("microedition.profiles") + " ");
props.append("microedition.platform:" +
System.getProperty("microedition.platform") + " ");
props.append("microedition.locale:" +
System.getProperty("microedition.locale") + " ");
props.append("microedition.encoding:" +
System.getProperty("microedition.encoding") + " ");
props.append("java.version:" + System.getProperty("java.version") + " "); // null
props.append("java.vendor:" + System.getProperty("java.vendor") + " "); //null
props.append("java.vm.name:" + System.getProperty("java.vm.name") + " "); // null
props.append("java.vm.version:" + System.getProperty("java.vm.version ") +
""); //null
props.append("os.name:" + System.getProperty("os.name") + " "); //null
props.append("os.arch:" + System.getProperty("os.arch") + " "); //null

props.append("os.version:" + System.getProperty("os.version") + " "); //null
props.append("user.name:" + System.getProperty("user.name") + " "); //null
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional)

{
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

这个程序的作用是输出系统中各个环境属性的值。诀窍是使用System类的
getProperty()方法。请注意,J2ME核心包的System类已经不支持getProperties()方法了,而且很多环境属性都不再支持 了,比如java.version、java.vendor等等。

--请看程序(Memory.java):

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class Memory extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public Memory() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Runtime Information");
long total = Runtime.getRuntime().totalMemory();
long free = Runtime.getRuntime().freeMemory();
props.append("total memory:" + total + " ");
props.append("free memory:" + free + " ");
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

这个程序的诀窍是利用Runtime类的totalMemory()方法以及freeMemory()方法。

J2ME中的
Runtime类不再具有执行外部程序的功能了,这是很显然的。

List属于javax.microedition.lcdui包,它和Form一样,同样属于容器类型的对象。属于容器类型的对象还有 TextBox和Alert。我们在下面还会介绍这两个类的用法。此处介绍List的用法。请看下面的程序(FormList.java):

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class FormList extends MIDlet implements CommandListener {
private Display display;
private List list;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);

public FormList() {
display = Display.getDisplay(this);
}

public void startApp() {
list = new List("Choose URL", Choice.EXCLUSIVE);
list.append("www.pku.edu.cn", null);
list.append("www.yahoo.com", null);
list.append("该邮件地址已受到反垃圾邮件插件保护。要显示它需要在浏览器中启用 JavaScript。", null);
list.addCommand(exitCommand);
list.setCommandListener(this);
display.setCurrent(list);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {

destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
list = null;
}
}

请大家留意startApp()方法的内部:
list= new List("Choose URL", Choice.EXCLUSIVE);
list.append("www.pku.edu.cn",null);
list.append("www.yahoo.com",null);
list.append("该邮件地址已受到反垃圾邮件插件保护。要显示它需要在浏览器中启用 JavaScript。",null);

list.addCommand(exitCommand);
list.setCommandListener(this);
display.setCurrent(list);

其逻辑流程如下:首先调用构造函数实例化一个List对象(list),List对象实际上代表一个选择列表。List类的构造函数的第一个参数是 选择列表的名字,第二个参数是选择列表的形式, Choice.EXCLUSIVE表示这个选择列表只能够单选。如果是Choice.MULTIPLE,则表示这个选择列表可以多选。List类的 append()方法有两个参数,第一个参数是选择项的描述,第二个参数是一个Image对象,代表每个选择项前面的小图标。第二个参数可以是null 值,但是
第一个参数是必须的。我们同样可以使用addCommand()方法往List中注册命令,也可以使用setCommandListener()方法指定 命令监听者,这和Form是一样的。在startApp()方法的最后,使用Display对象的setCurrent()方法将List对象设定为当前 的屏幕显示对象。

在前面我们其实已经使用过Command对象了。J2ME的事件系统比较特殊,你必须首先定义一系列的命令,然后注册到容器对象中,例如(Form、 Alert、List、TextBox),再设定命令监听者,编写好commandAction()方法即可。当系统发送某个命令,便由 commandAction()方法进行统筹处理。下面的程序演示了如何定义多个命令以及如何编写commandAction()方法。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class CMD extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Command backCommand = new Command("BACK", Command.BACK, 2);
private Command cancelCommand = new Command("CANCEL", Command.CANCEL, 1);
private Command exitCommand = new Command("EXIT", Command.EXIT, 1);
private Command helpCommand = new Command("HELP", Command.HELP, 1);
private Command itemCommand = new Command("ITEM", Command.ITEM, 1);
private Command okCommand = new Command("OK", Command.OK, 1);
private Command screenCommand = new Command("SCREEN", Command.SCREEN,
1);
private Command stopCommand = new Command("STOP", Command.STOP, 1);
public CMD() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
props.addCommand(backCommand);
props.addCommand(cancelCommand);
props.addCommand(exitCommand);
props.addCommand(helpCommand);
props.addCommand(itemCommand);
props.addCommand(okCommand);
props.addCommand(screenCommand);
props.addCommand(stopCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void showScreen(String cmd) {
Form form = new Form("show cmd");
form.append(cmd);
form.addCommand(exitCommand);
form.setCommandListener(this);
display.setCurrent(form);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
} else if (c == helpCommand) {
showScreen("help");
} else if (c == backCommand) {
showScreen("back");
} else if (c == cancelCommand) {
showScreen("cancel");
} else if (c == itemCommand) {
showScreen("item");
} else if (c == okCommand) {
showScreen("ok");
} else if (c == screenCommand) {
showScreen("screen");
}
if (c == stopCommand) {
showScreen("stop");
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}
在上面的程序(CMD.java)中定义了八个命令。如果commandAction()方法接到这八个命令,多半是调用 showScreen()方法,将这几个命令输出。showScreen()方法会产生一个新的容器对象(Form),作为当前的屏幕,并把截获的命令显 示在屏幕中。


CMD.java的运行效果如下2图所示(当屏幕出现Hello World字样的时候,你需要按下退出键,命令菜单就会出现了,你可以依次执行各个命令)。

TextBox是一个容器类型的对象(和Form的性质一样)。用法如下所示:

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowTextBox extends MIDlet implements CommandListener {
private Display display;
private TextBox txtBox;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public ShowTextBox() {
display = Display.getDisplay(this);
}

public void startApp() {
// or :
// String str="hello world";
// txtBox = new TextBox("Text Box",str,str.length(),0);

// the follow code is wrong:
// txtBox = new TextBox("Text Box",str,any number here,0);
txtBox = new TextBox("Text Box", null, 200, 0);
txtBox.addCommand(exitCommand);
txtBox.setCommandListener(this);
display.setCurrent(txtBox);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
txtBox = null;
}
}

请注意TextBox类的构造函数,第一个参数实际上是窗口的名称(因为TextBox是一个容器,可能是当前屏幕的显示对象),第二个参数是缺省 值,第三个参数是输入字符的总长度。如果你设置了文本框的缺省值,那么第三个参数必须是缺省字符的长度。如果第三个参数的值和缺省字符的长度不一样,那么 程序运行不成功(编译可以通过)。如果你将第二个参数置为null值,那么第三个参数可以任意设。

TextField和TextBox有点相似,不过TextBox是多行的,而TextField是单行的。而且TextBox是容器类型的对象, 但是TextField是项目类型的对象,只能够被容器包含,不能够单独显示。
TextField文本域对象的用法如下所示:

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowTextField extends MIDlet implements CommandListener {
private Display display;
private Form props;
private TextField txtField;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public ShowTextField() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
txtField = new TextField("EMail:", "", 15, TextField.EMAILADDR);
props.append(txtField);
props.addCommand(exitCommand);
props.setCommandListener(this);

display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}

}



 

请注意startApp()方法,我们使用Form对象作为当前屏幕的显示对象,而将TextField对象作为Form的一个子项目显示。下面来 介绍TextField类的构造函数,第一个参数是文本域的名称,第二个参数是缺省值,第三个参数是长度,第四个参数是文本域的类型,可选的值 有:TextField.PASSWORD、TextField.EMAILADDR、TextField.PHONENUMBER、 TextField.URL、TextField. NUMERIC等等。构造好TextField对象之后,调用Form的append()方法将它添加到Form对象的子项目中。

DateField对象和TextField对象一样同属于项目类型的对象,不能够单独显示,必须作为容器对象的子项目显示。DateField对象的作 用是显示一个日期,它和Windows控制面板中的时间和日期设置程序有点近似。DateField对象的用法如下所示:

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowDateField extends MIDlet implements CommandListener {
private Display display;
private Form props;
private DateField datField;


private Command exitCommand = new Command("Exit", Command.EXIT, 1);

public ShowDateField() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
// change:
// datField=new DateField("Date:",DateField.DATE_TIME);
// datField=new DateField("Date:",DateField.TIME);
datField = new DateField("Date:", DateField.DATE);
props.append(datField);
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}


public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}

}


StringItem对象和TextField、DateField对象类似,同样属于项目类型的对象。它的作用就是在容器对象中显示一条字符串。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowStringItem extends MIDlet implements CommandListener {
private Display display;


private Form props;
private StringItem strItem;
private StringItem strItem2;

private Command exitCommand = new Command("Exit", Command.EXIT, 1);

public ShowStringItem() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
strItem = new StringItem("signature:", "小楼一夜听春雨");
strItem2 = new StringItem("signature:", "三教明天考物化");

props.append(strItem);
props.append(strItem2);
props.addCommand(exitCommand);
props.setCommandListener(this);

display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

ImageItem对象是一个项目类型的对象,他的作用是在容器中显示图片。那么如何使用ImageItem对象呢?请按照下面三个步骤进行:

1.构造一个Image对象,相关代码如下所示:

Image img=Image.createImage("/fancy/test/JavaPowered-8.png");

createImage()方法是Image类的静态方法,它的作用是根据图形文件创建一个Image对象。
J2ME程序中所用到的图片文件必须存放在appsfancy es文件夹下面。

2.构造ImageItem对象,相关代码如下所示:
imgItem=new ImageItem("Default Layout",img,ImageItem.LAYOUT_DEFAULT,
"Image Cannot be shown");
ImageItem类的构造函数有三个参数,第一个参数的作用是显示一个标签,第二个参数指明图片的对齐方式,第三个参数的作用是显示图片的tip。

3.利用容器类对象的append()方法将ImageItem对象添加进去。如:
props.append(imgItem);
下面我们来看一个比较完整的例子。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowImageItem extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Image img;
private ImageItem imgItem;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public ShowImageItem() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
// props.append("Hello World! ");
try {
img = Image.createImage("/fancy/test/JavaPowered-8.png");
imgItem = new ImageItem("Default Layout",
img, ImageItem.LAYOUT_DEFAULT,
"Image Cannot be shown ");
props.append(imgItem);
props.append(new ImageItem("Left Layout",
img, ImageItem.LAYOUT_LEFT,
"Image Cannot be shown "));
props.append(new ImageItem("Center Layout",
img, ImageItem.LAYOUT_CENTER,
"Image Cannot be shown "));
} catch (Exception fe) {
// to do nothing
}
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

ChoiceGroup也是一个项目类型的对象,它代表一个选择列表,它的作用和List对象类似,不过后者是一个容器,而前者是一个项目。
我们需要特别注意ChoiceGroup类的构造函数,它有四个参数,第一个参数是标签,第二个参数是此选择列表的类型,例如多选还是单选。第三个参数是 一个字符串数组,代表每个选项的标签,第四个选项是一个Image类型的数组,代表每个选项前面的小图标。下面是一个比较完整的例子。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowChoiceGroup extends MIDlet implements CommandListener {
private Display display;
private Form props;

private Image duke;
private Image[] imageArray;
private ChoiceGroup choice;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public ShowChoiceGroup() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
// props.append("Hello World! ");
try {
Image duke = Image.createImage("/fancy/test/Icon.png");
imageArray = new Image[] {duke, duke, duke};
String[] stringArray = {"Option A", "Option B", "Option C"};
choice = new ChoiceGroup("choice group", ChoiceGroup.MULTIPLE,
stringArray, imageArray);
props.append(choice);
} catch (Exception fe) {
// to do nothing.
}
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}

public void destroyApp(boolean unconditional)

{
}
public void pauseApp() {
display.setCurrent(null);
props = null;
}
}
}


Gauge对象是一个项目类型的对象,它的作用是显示一个进度条。请看下面的源代码。
Gauge类的构造函数的后面两个参数分别是进度条的最大值和初始值。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowGauge extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public ShowGauge() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
// props.append("Hello World! ");
Gauge gauge = new Gauge("show gauge", true, 100, 50);
props.append(gauge);
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

Ticker对象是一个项目类型的对象,它的作用相当于一个滚动消息栏,在屏幕的上方显示滚动的信息。 Ticker类的构造函数仅有一个参数,那就是需要滚动显示的消息。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ShowTicker extends MIDlet implements CommandListener {
private Display display;
private Form props;

private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public ShowTicker() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");

props.append("Hello World! ");
Ticker ticker = new Ticker("ticker");
props.setTicker(ticker);
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

在前面的例子中,我们已经演示了如何构造J2ME程序的用户界面。现在有一个问题,那就是如何与用户界面交互呢?亦即如何获取用户通过用户界面输入 的值呢?请看下面的例子。

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class GetTextBoxvalue extends MIDlet implements CommandListener {
private Display display;
private TextBox txtBox;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
private Command getCommand = new Command("GETvalue", Command.OK, 1);
public GetTextBoxvalue() {
display = Display.getDisplay(this);
}

public void startApp() {
// or :
// String str="hello world";
// txtBox = new TextBox("Text Box",str,str.length(),0);
// the follow code is wrong:
// txtBox = new TextBox("Text Box",str,any number here,0);
txtBox = new TextBox("Text Box", null, 200, 0);
txtBox.addCommand(exitCommand);
txtBox.addCommand(getCommand);
txtBox.setCommandListener(this);
display.setCurrent(txtBox);
}

public void valueScreen() {
Form props = new Form("get text box value");
props.append(txtBox.getString());
props.addCommand(exitCommand);
props.setCommandListener(this);

display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
if (c == getCommand) {
valueScreen();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {

display.setCurrent(null);
txtBox = null;
}
}

在上面的例子中(GetTextBoxvalue.java),当我们往文本框中输入文本,并按下退出按钮,接着选择GETvalue命令的时候, 将会调用valueScreen()方法。valueScreen()方法的源代码如下:

public void valueScreen() {
    Form props=new Form("get text box value");
    props.append(txtBox.getString());
    props.addCommand(exitCommand);
    props.setCommandListener(this);
    display.setCurrent(props);
}

valueScreen()方法的逻辑是:首先创建一个容器对象Form,然后调用TextBox对象的getString()方法,获取文本框中 的输入值,追加到容器对象中,最后将此Form对象作为屏幕的当前显示对象。

Date对象是属于java.util包的,它的作用是返回当前的时间。请看下面的代码:

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

import java.util.*;

public class GetDate extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Date date;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public GetDate() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
date = new Date();
props.append("Now Time:" + date.getTime() + " ");

props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;

}
}

TimeZone对象也是属于java.util包的。这个对象的作用是提供关于时区的信息。TimeZone类有一个静态方法 ----getDefault(),可以获取与当前系统相关的时区对象。getAvailableIDs()方法可以获取系统中所有可用的时区的ID 号,getID()方法可以获取系统当前所设置的时区。具体的例子如下所示

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class GetTimeZone extends MIDlet implements CommandListener {
private Display display;
private Form props;
// private Date date;
private TimeZone zone;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public GetTimeZone() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
// date=new Date();
// props.append("Now Time:"+date.getTime()+" ");
zone = TimeZone.getDefault();
String[] zoneid = zone.getAvailableIDs();
for (int i = 0; i < zoneid.length; i++) {
props.append(zoneid[i] + " ");
}
props.append("Current Time Zone:" + zone.getID() + " ");
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {

destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

Calendar对象归属于java.util包,它可以提供更为详尽的时间信息。具体的例子如下所示

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class GetCalendar extends MIDlet implements CommandListener {
private Display display;
private Form props;
// private Date date;
// private TimeZone zone;
// private Calendar calendar;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public GetCalendar() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Hello World");
props.append("Hello World! ");
Calendar rightNow = Calendar.getInstance();
props.append("YEAR:" + rightNow.get(Calendar.YEAR) + " ");
props.append("MONTH:" + rightNow.get(Calendar.MONTH) + " ");
props.append("DAY OF MONTH:"
+ rightNow.get(Calendar.DAY_OF_MONTH) + " ");
props.append("HOUR OF DAY:"
+ rightNow.get(Calendar.HOUR_OF_DAY) + " ");
props.append("MINUTE:"
+ rightNow.get(Calendar.MINUTE) + " ");
props.append("SECOND:"
+ rightNow.get(Calendar.SECOND) + " ");
props.append("MILLISECOND:"
+ rightNow.get(Calendar.MILLISECOND) + " ");
// date=new Date();
// props.append("Now Time:"+date.getTime()+" ");
// zone=TimeZone.getDefault();
// String []zoneid=zone.getAvailableIDs();
// for(int i=0;i

在J2ME程序中,可以利用HttpConnection接口建立HTTP连接,访问远程服务器上的资源。具体的代码如下所示:

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;

public class GetHttpConnection extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public GetHttpConnection() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Get Http Connection");
// props.append("Hello World! ");
try {
HttpConnection conn = (HttpConnection) Connector.open
("http://rainbow:8080/index.html"; );
// conn.setRequestProperty("user","fancy");
props.append("Date:" + conn.getDate() + " ");
props.append("Expiration:" +
conn.getExpiration() + " ");
props.append(conn.getHost() + " ");
props.append("Last Modified:" +
conn.getLastModified() + " ");
props.append("Port:" + conn.getPort() + " ");
props.append("Protocol:" +
conn.getProtocol() + " ");
props.append("Request Method:" +
conn.getRequestMethod() + " ");
props.append("Response Code:" +
conn.getResponseCode() + " ");
props.append("Encoding:" +
conn.getEncoding() + " ");
props.append("Length:" + conn.getLength() + " ");
props.append("Type:" + conn.getType() + " ");
props.append("URL:" + conn.getURL() + " ");
props.append("Response Message:" +
conn.getResponseMessage() + " ");
} catch (Exception fe) {
props.append("Error:" + fe.getMessage());
}
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

Connector类提供了open()方法,可以和各种各样的远程资源建立连接。open()方法的参数就是远程资源的URL地址。open() 方法的返回值是一个Connection接口。为了建立HTTP连接,我们应该将它强制转换为HttpConnection接口的形式。一旦获取 HttpConnection接
口的实例对象,就可以调用HttpConnection接口的各种方法,得到关于HTTP连接的各种信息。

我的想法是建立一个Web服务器,使用Tomcat 4.0,支持JSP技术。再配置一个Mail服务器,使用的软件是Imail 7.0.4。首先编写一个可以发送电子邮件的JSP程序(post.jsp),
具体代码
如下所示:
 <%@ page import="java.net.*" %>
<%@ page import="java.io.*" %>
<%@ page import="javax.activation.*" %>
<%@ page import="java.util.*" %>
<%@ page import="javax.mail.*" %>
<%@ page import="javax.mail.internet.*" %>

<%!
public class SmtpAuthenticator extends javax.mail.Authenticator

{
    public javax.mail.PasswordAuthentication
    getPasswordAuthentication()
    {
        return new javax.mail.PasswordAuthentication("fancy",
        "fancy");
    }
}
%>

<%
String username="fancyrainbow";
String password="fancy";
String from="fancy@rainbow";
String to="fancy@rainbow";
String cc="fancy@rainbow";
String subject="J2ME Mail Test";
//String content="J2ME Mail Test";
String content=request.getParameter("content");

Properties props = System.getProperties();
props.put("mail.smtp.auth","true");

props.put("mail.smtp.host","rainbow"); //263

SmtpAuthenticator sa=new SmtpAuthenticator();
Session sess = Session.getInstance(props, sa);
sess.setDebug(true);

Message msg = new MimeMessage(sess);

msg.setFrom(new InternetAddress(from));
msg.setRecipients(Message.RecipientType.TO,InternetAddress.parse(to, false));
msg.addRecipients(Message.RecipientType.CC,InternetAddress.parse(cc, false));
msg.setSubject(subject);
msg.setSentDate(new Date());
msg.setText(content);
Transport.send(msg);
%>
Send Message OK!
我将post.jsp程序保存在Tomcat 4.0的ROOT目录下面,然后使用Web浏览器测试此程序成功。然后再将上一个J2ME程序(GetHttpConnection.java)改一改, 让它与Tomcat 4.0
服务器建立连接,请求post.jsp程序。相关代码如下所示:

package fancy.test;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;

public class SendMail extends MIDlet implements CommandListener {
private Display display;
private Form props;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
public SendMail() {
display = Display.getDisplay(this);
}

public void startApp() {
props = new Form("Get Http Connection");
// props.append("Hello World! ");
try {
HttpConnection
conn = (HttpConnection) Connector.open(
"http://rainbow:8080/post.jsp?content=Hello World");
// conn.setRequestProperty("user","fancy");
props.append("Date:" + conn.getDate() + " ");
props.append("Expiration:" + conn.getExpiration() + " ");
props.append(conn.getHost() + " ");
props.append("Last Modified:" + conn.getLastModified() + " ");
props.append("Port:" + conn.getPort() + " ");
props.append("Protocol:" + conn.getProtocol() + " ");
props.append("Request Method:" + conn.getRequestMethod() + " ");
props.append("Response Code:" + conn.getResponseCode() + " ");
props.append("Encoding:" + conn.getEncoding() + " ");
props.append("Length:" + conn.getLength() + " ");
props.append("Type:" + conn.getType() + " ");
props.append("URL:" + conn.getURL() + " ");
props.append("ResponseMessage:" + conn.getResponseMessage() + " ");
// InputStream is=conn.openInputStream();
// DataInputStream dis=new DataInputStream(is);
// props.append("System Info:"+dis.readUTF()+" ");
props.append("Send Mail OK!" + " ");
} catch (Exception fe) {
props.append("Error:" + fe.getMessage());
}
props.addCommand(exitCommand);
props.setCommandListener(this);
display.setCurrent(props);
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}

public void destroyApp(boolean unconditional) {
}

public void pauseApp() {
display.setCurrent(null);
props = null;
}
}

启动Tomcat 4.0、Imail 7.0.4、J2MEWTK。在J2MEWTK中运行SendMail.java程序,事后检查邮箱,发现已经成功的收到了邮件。

有两点需要说明一下:
首先,按照上面的模式,我们可以编写出功能更为强大的程序来,例如使用J2ME+JDBC+JSP访问远程的数据库系统,访问EJB组件等等。

其次,如果要读取post.jsp程序的输出信息,你可以从HttpConnection接口中获取一个输入流对象,逐个读取输入流中的数据即可。具体的 代码我就不举了,你可以参考J2ME的DOC。

终于写完啦!

自从大一接触Java以来,四年了,每天不Java一下就手痒,每天不Java一下就觉得有些失落,Java已经成为我的生活的一个部分。Java给予我 很多的乐趣,我想我应该为Java作些什么。四天前,当我开始接触J2ME,当我使用J2MEWTK成功运行HelloWorld程序的时候,这个念头再 次浮现在我的脑海里。我应该为Java做些自己力所能及的事情,所以才会有这篇不算太长的文章《J2ME学习札记》。不管别人的看法怎么样,我总算是了了 自己的一番心事。我之所以写这篇文章,还有另一个目的,那就是将我从Java中得到乐趣与你分享,希望你也能够将你从Java中获取的乐趣写出来,与大家 分享。

from pku
Ahthor:javalover