雅典之夏的小站
知人者智 自知者明 Fighting!!
一.介绍
1.eclipse官方网站: 
   http://www.eclipse.org
2.赛迪网   
   http://tech.ccidnet.com/pub/series/s102.html
3.Eclipse平台入门 
   http://www-900.ibm.com/developerWorks/cn/linux/opensource/os-ecov/index.shtml
 
 

二.教程
 
 3Plus4 Software
    http://www.3plus4software.de/eclipse/index_en.html
 
Omondo
    http://www.tutorial-omondo.com/
 
Tutorial for building J2EE Applications using JBOSS and ECLIPSE
    http://www.tusc.com.au/tutorial/html/index.html
 
Getting Started with Eclipse and the SWT   
    http://www.cs.umanitoba.ca/~eclipse/
 
我认为最好的教程还是eclipse软件自带的帮助文件(现在有了中文帮助)。
  

三.插件
三.插件
 
Eclipse Plugin Resource Center and Marketplace   
   http://www.eclipseplugincentral.com/

eclipse-plugins  
   http://www.eclipse-plugins.info/eclipse/index.jsp
 
eclipse-workbench   
   http://www.eclipse-workbench.com/jsp/index.jsp
 
以上3个网站都是综合性插件网站,你可以查询自己所需的插件,而且还有各种排名。
 
 
Jigloo SWT/Swing GUI Builder       
      http://cloudgarden.com/jigloo/
 
 
Lomboz  
    http://forge.objectweb.org/project/showfiles.php?group_id=97 
 
Matrix网站介绍eclipse plugins  
    http://www.matrix.org.cn/subjects/Wiki.jsp?page=EclipsePlugins
 
TomcatPlugin
   http://www.sysdeo.com/eclipse/tomcatPlugin.html

EasyStruts
    http://easystruts.sourceforge.net

EclipseColorer plugin 
   http://colorer.sf.net 
   
CVS-SSH2 Plug-in for Eclipse 
   http://www.jcraft.com/eclipse-cvsssh2/
posted @ 2005-10-27 12:55 rkind 阅读(142) | 评论 (0)编辑 收藏
一、通过ResultSet对象对结果集进行处理



从前面的学习中,我们掌握了通过Statement类及其子类传递SQL语句,对数据库管理系统进行访问。一般来说,对数据库的操作大部分都是执行查询语句。这种语句执行的结果是返回一个ResultSet类的对象。要想把查询的结果返回给用户,必须对ResultSet对象进行相关处理。今天,我们就来学习对结果集的处理方法。

按照惯例,让我们先来看一个例子:



package com.rongji.demo;



import java.sql.*;



public class dataDemo {

public dataDemo() {

}



public static void main(String[] args) {



try {

Class.forName("oracle.jdbc.driver.OracleDriver");

//建立连接

//第二步是用适当的驱动程序连接到DBMS,看下面的代码[自行修改您所连接的数据库相关信息]:

String url = "jdbc:oracle:thin:@192.168.4.45:1521:oemrep";

String username = "ums";

String password = "rongji";

//用url创建连接

Connection con = DriverManager.getConnection(url, username, password);

Statement sta = con.createStatement();

String sql = "select * from rbac_application ";

ResultSet resultSet = sta.executeQuery(sql);

while (resultSet.next()) {

int int_value = resultSet.getInt(1);

String string_value = resultSet.getString(2);

String a = resultSet.getString(3);

String b = resultSet.getString(4);

//从数据库中以两种不同的方式取得数据。

System.out.println(int_value + " " + string_value + " " + a + " " +

b);

//将检索结果在用户浏览器上输出。

}

//获取结果集信息

ResultSetMetaData resultSetMD = resultSet.getMetaData();

System.out.println("ColumnCount:" + resultSetMD.getColumnCount());

for (int i = 1; i < resultSetMD.getColumnCount(); i++) {

System.out.println("ColumnName:" + resultSetMD.getColumnName(i) + " " +

"ColumnTypeName:" +

resultSetMD.getColumnTypeName(i));

System.out.println("isReadOnly:" + resultSetMD.isReadOnly(i)

+ " isWriteable:" + resultSetMD.isWritable(i)

+ " isNullable:" + resultSetMD.isNullable(i));

System.out.println("tableName:" + resultSetMD.getTableName(i));

}



//关闭

con.close();

}

catch (Exception ex) {

ex.printStackTrace();

}

}

}



1、ResultSet类的基本处理方法



一个ResultSet对象对应着一个由查询语句返回的一个表,这个表中包含所有的查询结果,实际上,我们就可以将一个ResultSet对象看成一个表。对ResultSet对象的处理必须逐行进行,而对每一行中的各个列,可以按任何顺序进行处理。

ResultSet对象维持一个指向当前行的指针。最初,这个指针指向第一行之前。Result类的next()方法使这个指针向下移动一行。因此,第一次使用next()方法将指针指向结果集的第一行,这时可以对第一行的数据进行处理。处理完毕后,使用next()方法,将指针移向下一行,继续处理第二行数据。next()方法的返回值是一个boolean型的值,该值若为true, 说明结果集中还存在下一条记录,并且指针已经成功指向该记录,可以对其进行处理;若返回值是false,则说明没有下一行记录,结果集已经处理完毕。

在对每一行进行处理时,可以对各个列按任意顺序进行处理。不过,按从左到右的顺序对各列进行处理可以获得较高的执行效率.ResultSet类的getXXX()方法可以从某一列中获得检索结果。其中XXX是JDBC中的Java数据类型,如int, String ,Date等,这与PreparedStatement类和CallableStatement类设置SQL语句参数值相类似。 Sun公司提供的getXXX() API提供两种方法来指定列名进行检索:一种是以一个int值作为列的索引,另一种是以一个String对象作为列名来索引。大家可以参照一下上面的例子。



2、获取结果集的信息



在对数据库的表结构已经了解的前提下,可以知道返回的结果集中各个列的一些情况,如:列名数据类型等等。有时并不知道结果集中各个列的情况,这时可以使用Resultset类的getMetaData方法来获取结果集的信息。如上面的例子:

ResultSetMetaData resultSetMD = resultSet.getMetaData();

GetMetaData()方法返回一个ResultSetMetaData类的对象,使用该类的方法,得到许多关于结果集的信息,下面给出几个常用的方法:

(1) getColumnCount()返回一个int值,指出结果集中的列数。

(2) getTableName(int column)返回一个字符串,指出参数中所代表列的表的名称。

(3) getColumnLabel(int column)返回一个String对象,该对象是column所指的列的显示标题。

(4) getColumnName(int column)返回的是该列在数据库中的名称。可以把此方法返回的String对象作为Resultset类的getXXX()方法的参数。不过,并没有太大的实际意义。

(5) getColumnType(int comlumn)返回指定列的SQL数据类型。他的返回值是一个int值。在java.sql.Types类中有关于各种SQL数据类型的定义。

(6) getColumnTypeName(int comlumn)返回指定列的数据类型在数据源中的名称。他的返回值是一个String对象。

(7) isReadOnly(int column) 返回一个boolean值,指出该列是否是只读的。

(8) isWriteable(int column) 返回一个boolean值,指出该列是否可写。

(9) isNullable(int column)返回一个boolean值,指出该列是否允许存入一个NULL 值。
posted @ 2005-10-27 12:53 rkind 阅读(156) | 评论 (0)编辑 收藏
Java和SQL各自有一套自己定义的数据类型(jsp的数据类型实际上就是Java的数据类型),我们要在Jsp程序和数据库管理系统之间正确的交换数据,必然要将二者的数据类型进行转换。先让我们来看两个表:

表SQL到Java数据类型影射表

SQL 数据类型
JAVA数据类型

CHAR
String

VARCHAR
String

LONGVARCHAR
String

NUMERIC
java.math.BigDecimal

DECIMAL
java.math.BigDecimal

BIT
Boolean

TINYINT
Byte

SMALLINT
Short

INTEGER
Int

BIGINT
Long

REAL
Float

FLOAT
Double

DOUBLE
Double

BINARY
byte[]

VARBINARY
byte[]

LONGVARBINARY
byte[]

DATE
java.sql.Date

TIME
java.sql.Time

TIMESTAMP
java.sql.Timestamp














Java到SQL数据类型影射表

JAVA数据类型
SQL 数据类型

String
VARCHAR or LONGVARCHAR

java.math.BigDecimal
NUMERIC

Boolean
BIT

Byte
TINYINT

Short
SMALLINT

Int
INTEGER

Long
BIGINT

Float
REAL

Double
DOUBLE

byte[]
VARBINARY or LONGVARBINARY

java.sql.Date
DATE

java.sql.Time
TIME

java.sql.Timestamp
TIMESTAMP




这里,大伙要注意了,并不是所有的数据类型在各种数据库管理系统中都被支持。下面,就几种常用的数据类型之间的转化进行说明:



(1) CHAR, VARCHAR, 和 LONGVARCHAR



在SQL语言中,有三种分别表示不同长度的字符类型CHAR, VARCHAR, 和 LONGVARCHAR,在Java/Jsp中并没有相应的三种不同的数据类型与之一一对应,JDBC的处理方法是将其与String或者char[]对应起来。在实际编程中不必对着三种SQL数据类型进行区分,全部将他们转化为Sting或者char[]就可以了。而且通常使用应用的非常普遍的String类型。我们还可以利用String类提供的方法将一个String对象转化为char[],或者用char[]为参数构造一个Stirng对象。

对于定长度的SQL数据类型CHAR(n),当从数据库管理系统中获得的结果集提取该类型的数据时,JDBC会为其构造一个长度为n的String对象来代表他,如果实际的字符个数不足’n’,系统会自动为String对象补上空格。当向数据库管理系统写入的数据类型应该是CHAR(n)时,JDBC也会将该String对象的末尾补上相应数量的空格。

一般情况下,CHAR, VARCHAR, LONGVARCHAR和String之间可以无差错的进行转换。但非常值得注意的是LONGVARCHAR,这种SQL的数据类型有时在数据库中代表的数据可能有几兆字节的大小,超过了String对象的承受范围。JDBC解决的办法是用Java的Input Stream来接受这种类型的数据[以后我们回涉及到]。Input Stream不仅支持ASCII,而且支持Unicode,我们可以根据需要进行选择。



(2) DECIMAL 和 NUMERIC



SQL的DECIMAL 和 NUMERIC通常用来表示需要一定精度的定点数。在Java的简单数据类型中,没有一种类型与之相对应。但从JDK1.1开始,Sun公司在java.math.*包中加入了一个新的类BigDecimal,该类的对象可以与DECIMAL 、NUMERIC进行转换。

另外,当从数据库管理系统中读取数据时,还可以用getString()方法来获取DECIMAL 和 NUMERIC。



(3) BINARY, VARBINARY, 和 LONGVARBINARY



在编程时无须精确区分这三种SQL数据类型,JDBC将他们统一影射为byte[]。其中LONGVARBINARY和LONGVARCHAR相似,可以代表几兆字节的数据,超出数组的承受范围。解决的办法依然是用Input Stream来接受数据。



(4) BIT



代表一个二进制位的BIT类型被JDBC影射为boolean型。



(5) TINYINT, SMALLINT, INTEGER, 和 BIGINT



SQL语言的TINYINT, SMALLINT, INTEGER, 和 BIGINT分别代表8位、16位、32位、64位的数据。他们分别被影射为Java的byte, short, int, 和 long



(6) REAL, FLOAT, 和 DOUBLE



SQL定义了REAL, FLOAT, DOUBLE来支持浮点数。JDBC将REAL影射到Java的float,将FLOAT,DOUBLE影射到java的double。



(7) DATE, TIME, 和 TIMESTAMP



SQL定义了三种和日期相关的数据类型。 DATE代表年、月、日,TIME代表时、分、秒,TIMESTAMP结合了DATE和TIME的全部信息,而且增加了更加精确的时间计量单位。

在java的标准类库中,java.util.*包中的Date类用来表示日期和时间。但是该类和SQL中的DATE, TIME, 和 TIMESTAMP直接影射关系并不清晰。并且,该类也不支持TIMESTAMP的精确时间计量单位。因此,Sun公司在java.sql.*中为java.util.Date增加了三个子类:java.sql.Date,java.sql.Time ,java.sql.Timestamp,分别与SQL中的三个日期数据类型相对应。



总之,关于SQL与JAVA之见数据类型的转化,还有很多细节方面的东西,这里就不一一介绍了,有需要的朋友自己可以去查一下相关文档。这里给大家介绍一个我常去的网站:

http://java.sun.com/docs/books/tutorial/jdbc。
posted @ 2005-10-27 12:52 rkind 阅读(295) | 评论 (0)编辑 收藏
二、通过PreparedStatement对象访问数据库



前面,我们讨论了用连接对象Connection产生Statement对象,然后用Statement与数据库管理系统进行交互。Statement对象在每次执行SQL语句时都将该语句传递给数据库。在多次执行同一语句时,这样做效率较低。解决这个问题的办法是使用PreparedStatement对象。如果数据库支持预编译,可以在创建PreparedStatement对象时将SQL语句传递给数据库做预编译,以后每次执行这个SQL语句时,速度就可以提高很多。如果数据库不支持预编译,则在语句执行时,才将其传给数据库。这对于用户来说是完全透明的。

PreparedStatement对象的SQL语句还可以接受参数。在语句中指出需要接受那些参数,然后进行预编译。在每一次执行时,可以将不同的参数传递给SQL语句,大大提高了程序的效率与灵活性。一般情况下,使用PreparedStatement对象都是带输入参数的。



为了更好的理解,请看下面这个例子:



package com.rongji.demo;



import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.Statement;

import java.sql.DatabaseMetaData;

import java.sql.PreparedStatement;





public class DataConn {

public DataConn() {

}

public static void main(String[] args) {

try

{

//加载驱动程序

//下面的代码为加载JDBD-ODBC驱动程序

Class.forName("oracle.jdbc.driver.OracleDriver");

//建立连接

//第二步是用适当的驱动程序连接到DBMS,看下面的代码[自行修改您所连接的数据库相关信息]:

String url="jdbc:oracle:thin:@192.168.4.45:1521:oemrep";

String user = "ums1";

String password = "rongji";

//用url创建连接

Connection con=DriverManager.getConnection(url,user,password);

//当前的表中有如下几个字段:ID,NAME,PASSWORD,TEXT,NOTE

PreparedStatement insertStatement = con.prepareStatement(

"INSERT INTO rbac_application values(?,?,?,?,?)");

insertStatement.setInt(1,10);

insertStatement.setString(2,"thinkersky");

insertStatement.setString(3,"88888");

insertStatement.setString(4,"这是个测试的应用程序");

insertStatement.setString(5,"备注");

int result = insertStatement.executeUpdate();

System.out.println("the result is" + result);

con.close();

}

catch (Exception e)

{

//输出异常信息

System.err.println("SQLException :"+e.getMessage());

e.printStackTrace();

}

}

}



相信通过这个例子,对PreparedStatement这个类的应用应该有了初步的了解。恩,让我详细介绍一下这个例子吧:)

1、 创建一个PreparedStatement对象



PreparedStatement类是Statement类的子类。同Statemetn类一样,PreparedStatement类的对象也是建立在Connection对象之上的。如下所示:

PreparedStatement insertStatement = con.prepareStatement(

"INSERT INTO rbac_application values(?,?,?,?,?)");

创建该PreparedStatement对象时,相应的插入记录的SQL语句已经被传递到数据库管理系统中进行预编译。



2、 为PreparedStatement对象提供参数



如果以带输入参数的SQL语句形式创建了一个PreparedStatement对象(绝大多数情况下都是如此)。在SQL语句被数据库管理系统正确执行之前,必须为参数(也就是SQL语句中是’?’的地方)进行初始化。初始化的方法是调用PreparedStatement类的一系列setXXX()方法。如果输入参数的数据类型是int型,则调用setInt()方法;如果输入参数是String型,则调用setString()方法。一般说来,Java中提供的简单和复合数据类型,都可以找到相应的setXXX()方法。

现在,让我们再回头看看我们例子中对几个参数的初始化情况:

insertStatement.setInt(1,10);

insertStatement.setString(2,"thinkersky");

insertStatement.setString(3,"88888");

insertStatement.setString(4,"这是个测试的应用程序");

insertStatement.setString(5,"备注");

这里,setXXX()方法一般有两个参数,第一个参数都是int型,该参数指示JDBC PreparedStatement对象的第几个参数将要被初始化。第二个参数的值就是PreparedStatemetn将要被初始化的参数取值,数据类型自然也就相同。这里要说明的是当PreparedStatement的一个对象的参数被初始化以后,该参数的值一直保持不变,直到他被再一次赋值为止。



3、 调用PreparedStatement对象的executeUpdate()方法



这里,要清楚PreparedStatement对象的executeUpdate()方法不同于Statement对象的executeUpdate()方法,前者是不带参数的,其所需要的SQL语句型的参数已经在实例化该对象时提供了。另外,executeUpdate()方法将返回一个整数,这个整数代表executeUpdate()方法执行后所更新的数据库中记录的行数。执行完上面的例子,其结果返回为1。那么什么情况下返回0呢?其实啊,当PreparedStatement对象的executeUpdate()方法的返回值是0时。有两种可能:

(1) 所执行的SQL语句是对数据库管理系统的记录进行操作;并且没有记录被 更新

(2) 所执行的SQL语句是对数据库管理系统的表、视图等对象进行操作的DDL语言,没有数据记录被直接修改。
posted @ 2005-10-27 12:52 rkind 阅读(1805) | 评论 (1)编辑 收藏
一、通过DatabaseMetaData对象了解数据库的信息



JSP通过JDBC对数据库管理系统进行连接以后,得到一个Connection 对象,可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列,数据类型,触发器,存储过程等各方面的信息。根据这些信息,JDBC可以访问一个实现事先并不了解的数据库。获取这些信息的方法都是在DatabaseMetaData类的对象上实现的,而DataBaseMetaData对象是在Connection对象上获得的。



按照惯例,让我们先来看看下面这个例子:



package com.rongji.demo;



import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.Statement;

import java.sql.DatabaseMetaData;





public class DataConn {

public DataConn() {

}

public static void main(String[] args) {

try

{

//加载驱动程序

//下面的代码为加载JDBD-ODBC驱动程序

Class.forName("oracle.jdbc.driver.OracleDriver");

//建立连接

//用适当的驱动程序连接到DBMS,看下面的代码[自行修改您所连接的数据库相关信息]:

String url="jdbc:oracle:thin:@192.168.4.45:1521:oemrep";

String user = "ums";

String password = "rongji";

//用url创建连接

Connection con=DriverManager.getConnection(url,user,password);



//获取数据库的信息

DatabaseMetaData dbMetaData = con.getMetaData();

//返回一个String类对象,代表数据库的URL

System.out.println("URL:"+dbMetaData.getURL()+";");

//返回连接当前数据库管理系统的用户名。

System.out.println("UserName:"+dbMetaData.getUserName()+";");

//返回一个boolean值,指示数据库是否只允许读操作。

System.out.println("isReadOnly:"+dbMetaData.isReadOnly()+";");

//返回数据库的产品名称。

System.out.println("DatabaseProductName:"+dbMetaData.getDatabaseProductName()+";");

//返回数据库的版本号。

System.out.println("DatabaseProductVersion:"+dbMetaData.getDatabaseProductVersion()+";");

//返回驱动驱动程序的名称。

System.out.println("DriverName:"+dbMetaData.getDriverName()+";");

//返回驱动程序的版本号。

System.out.println("DriverVersion:"+dbMetaData.getDriverVersion());

//关闭连接

con.close();

}

catch (Exception e)

{

//输出异常信息

System.err.println("SQLException :"+e.getMessage());

e.printStackTrace();

}

}

}



通过上面的例子,我们可以看出,DatabaseMetaData类的对象的实现,如下语句

<%

DatabaseMetaData datameta=con.getMetaData();

%>

DatabaseMetaData类中提供了许多方法用于获得数据源的各种信息,通过这些方法可以非常详细的了解数据库的信息。就如我们上面例子中所显示的几个信息[其他的方法请读者参考JDK API中的DatabaseMetaData类]:



getURL()

返回一个String类对象,代表数据库的URL。

getUserName()

返回连接当前数据库管理系统的用户名。

isReadOnly()

返回一个boolean值,指示数据库是否只允许读操作。

getDatabaseProductName()

返回数据库的产品名称。

getDatabaseProductVersion()

返回数据库的版本号。

getDriverName()

返回驱动驱动程序的名称。

getDriverVersion()

返回驱动程序的版本号。
posted @ 2005-10-27 12:51 rkind 阅读(75) | 评论 (0)编辑 收藏
JSP调用JDBC API访问数据库管理系统是通过以下五个步骤来实现的:



(1)加载特定的JDBC驱动程序



为了与特定的数据源连接,JDBC必须加载相应的驱动程序。这些驱动程序都是通过语句:Class.forName("Driver Name"); 来加载的。这里面有一个小技巧,我们可以在”Driver Name”出填入一系列的驱动程序名称,例如:“Class.forName("sun.jdbc.odbc.JdbcOdbcDriver:oracle.jdbc.driver.OracleDriver”); 中间用冒号隔开。JSP将按照列表顺序搜索驱动程序,并且加载第一个能与给定的URL相连的驱动程序。在搜索驱动程序列表时,JSP将跳过包含不可信任代码的驱动程序,除非他与要打开的数据库管理系统是来源于同一处.



(2)用已注册的驱动程序建立到数据库管理系统的连接



我们要做的第二步是用已经注册的驱动程序建立到数据库管理系统的连接,这要通过DriverManager类的getConncetion方法来实现。这里特别需要注意的是String类型 url 参数的取值,url代表一个将要连接的特定的数据库管理系统的数据源。使用不同的数据库驱动程序,url的取值方式是不同的。例程中加载了“com.mysql.jdbc.Driver”驱动,url的取值方式“jdbc:mysql://localhost:3306/ums_db?useUnicode=true&amp;characterEncoding=GB2312”,如果加载“oracle.jdbc.driver.OracleDriver”驱动,url的取值方式应该是“jdbc:oracle:thin:@host name:port number:service name”。其他驱动程序的url的取值方式,各位自行参阅相应的文挡。

例程中的GetConnection()方法只有一个参数String url,代表ODBC数据源,如果连接大型数据库,则需要三个参数:String url、Strng user、String password。User和password代表数据库管理系统的用户名和口令。一般的大型数据库如Oracle、MS SQL Server、DB2等用户名和口令是必须的。而小型的数据库如ACCESS、Foxpro等并不需要。

如果连接成功,则会返回一个Connection类的对象con。以后对数据库的操作都是建立在con对象的基础上的。GetConnection()方法是DriverManager类的静态方法,使用时不用生成DriverManager类的对象,直接使用类名DriverManager就可以调用。



(3)创建Statement声明,执行SQL语句



在实例化一个Connection类的对象con,成功建立一个到数据库管理系统的连接之后。我们要做的第三步是利用该con对象生成一个Statement类的对象stmt。该对象负责将SQL语句传递给数据库管理系统执行,如果SQL语句产生结果集,stmt对象还会将结果集返回给一个ResultSet类的对象。

Statement类的主要的方法有三个:

executeUpdate(String sql)

executeQuery(String sql)

execute(String sql)

executeUpdate(String sql)方法用于执行DDL类型的SQL语句,这种类型的SQL语句会对数据库管理系统的对象进行创建、修改、删除操作,一般不会返回结果集。



executeQuery(String sql)方法用于执行一条查询数据库的SELECT语句。如果有符合查询条件的数据存在,该方法将返回一个包含相应数据的ResultSet类对象,否则,该对象的next()方法将返回false。



execute(String sql)方法用于执行一个可能返回多个结果集的存储过程(Stored Procedure)或者一条动态生成的不知道结果集个数的SQL语句。如果存储过程或者SQL语句产生一个结果集,该方法返回false.如果产生多个结果集,该方法返回true。我们可以综合运用Statement类的getResultSet(), getUpdateCount(), getMoreResults()方法来检索不同的结果集。

服务器对JSP程序进行编译时,并不对将要执行的SQL语句作语法检查,只是将其作为一个String对象。只有当客户端发出HTTP请求,Java虚拟机对Servlet进行解释执行,将SQL语句传递给数据库管理系统时,才能知道他是否正确。对于错误的SQL语句,在执行时会产生SQLExcepion。其实,所有与JDBC操作的JSP语句都与数据库管理系统及相应的驱动程序有关,是超出JSP的控制范围的。这些语句只有在实际的解释执行中才能检验出是否能顺利执行,因此一定要声明并捕获例外:

try{

….

}catch(SQLException e)

{

Sytem.err.println(“SQLException:”+e.getMessage());

}

否则,JSP程序无法被编译成Servlet。



(4)关闭Statement对象



一个Statement对象在打开后可以多次调用executeQuery(String sql)、executeUpdate(String sql)、execute(String sql)方法来执行SQL语句,与数据库管理系统进行交互。但一个Statement对象在同一时间只能打开一个结果集,对第二个结果集的打开隐含着对第一个结果集的关闭。如果想对多个结果集同时进行操作,必须创建多个Statement对象,在每个Statement对象上执行SQL语句获得相应的结果集。



(5)关闭Connection对象



在处理完对数据库的操作后,一定要将Connection对象关闭,以释放JDBC占用的系统资源。在不关闭Connection对象的前提下再次用DriverManager静态类初始化新的Connection对象会产生系统错误。而一个已经建立连接的Connection对象可以同时初始化多个Statement对象。对应不同的数据库管理系统的Connection对象可以初始化Statement对象的个数是不同的。在Oracle中是50个。
posted @ 2005-10-27 12:51 rkind 阅读(180) | 评论 (0)编辑 收藏

首先加载mm.mysql2.0.4-bin.jar驱动,并把其加入classpath,注意一定要打开远程mysql的你的主机的访问权限

然后编写如下代码

          String name="org.gjt.mm.mysql.Driver";///加载jdbc driver
          String url="jdbc:mysql://172.20.0.73/jilei";  //连接到172.20.0.73上的jilei的mysql数据库
          Class.forName(name),//创建类
          con=DriverManager.getConnection(url,"你的username","你的密码")//创建连接

输入以下代码供测试使用

Statement stmt=con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
   String sql="select * from student";
  ResultSet rs=stmt.executeQuery(sql);
 while(rs.next()) {
System.out.println("您的第一个字段内容为:"+rs.getString(1));
System.out.println("您的第二个字段内容为:"+rs.getString(2) );
System.out.println("您的第三个字段内容为:"+rs.getString(3) );
}
  rs.close();
  stmt.close();
          con.close();

posted @ 2005-10-27 12:49 rkind 阅读(1044) | 评论 (0)编辑 收藏

孔子说:“学而不思则罔,思而不学则殆。”从我做项目的经历,我深深感叹古人的智慧。

去年我毕业设计时候做的一个项目是毕业设计管理系统。当时老师给我们的要求是用Jsp 和 Javabean来实现。我主要负责项目建模和Javabean部分,后来负责数据库的同学被SARS困在家不能返校,所以数据库部分也由我来完成了。当时对java的知识是非常贫乏的,只有简单的语法基础,连jsp、 javabean都是第一次听到过。不知道从哪里开始,于是就研究老师给的一个非常简单的演示项目,首先觉得javabean是个比较简单的东西,于是就先开始分析用户需求。

需求怎么来描述呢?由于学校教育的局限,加上自己研究了很长时间高数,政治,英语(干什么用大家都明白吧),造成了知识的严重匮乏。我就以参加毕业设计人员为中心,建立他们的需求列表。但是这样的需求描述是非常片面的,几乎都是静态的需求,而且很难设计几个用户的交互过程,和某些状态的改变。而且这种需求是否有真正符合需要是个问题,所以我立刻和同学一起设计web界面,目的很简单,因为用户的需求都是通过界面来实现的,对界面的设计能涵盖最终提交用户的功能,而且还能发掘很多非用户的需求。这样我在实现这些功能的时候会有数。在一年后当我仔细看过用例分析,uml后感觉是如获至宝。心头的结全部打开。

建模该怎么建?当时真是什么概念都没有,虽然知道uml,但是仅仅知道皮毛。于是我就很简单的从对象的角度考虑问题,首先找毕业设计管理系统中涉及的人员,这个是很明显找出来的,涉及到学生,教师,管理员,进一步提取出项目对象,各个人员之间通讯的消息对象,毕业设计文档对象,等等。接下来设计数据库表,由于学校教数据库的时候只有讲述了ER图到关系模式的转化,而根本没有讲ODL到关系模式的转化(现在看来,ODL应该更好,对理解O/R Mapping有更多的帮助。)于是就画ER图,建立各种表,设定主键,外键,等等。

我遇到的首要问题就是学生,教师,管理员该怎么办,每一个对象建一个表,似乎也没有什么问题。但是在建立消息对象的时候就有问题了,因为消息是要在学生,教师,管理员任意两者都能传送的,我建消息表的时候是这样设计的:一个消息的内容,消息的发送人,消息的接收人(消息内容和发送人一个表,为解决冗余接收人单独一个表),这样这个消息就有很大的灵活程度了,消息发送人可以是三种角色间的随便一种,更进一步,这样的消息可以是系统生成的消息,而且接收者可以是随便哪种角色。但随之带来的问题是:消息发送人、接收者该去参照谁呢?显然填一个发送者、接收者的id是不够的,必须连发送者的角色也填上。第二种方法,每个角色有一个收到消息的列表,让它去参照单一的消息表id,这样也能解决问题。第三种方法,把学生,教师,管理员合并成一个用户表,那么消息发送人,接收人只要简单的参照一个通用的用户id了。这种合并子类的方法会造成教师,管理员的行有些null值,但是考虑到教师和管理员的数目远远小于学生的数目,所以这样的冗余是忽略不计的。最后我还是选择了最后一种方法。不过我还是觉的是对象模型向关系模型的一种妥协。

接着我着手设计javabean,访问数据库用的是单纯的JDBC。因为我对前台界面要访问一个对象的哪些属性是不了解的,事实上也不该了解。所以我只能从数据库取出一个完整的对象,让他来决定究竟要访问哪些属性。而且对对象的修改也是这样,前台修改好新的对象的属性,我来进行更数据库的同步。于是我的javabean就出现了这样的方法:load()、 store()、 update()、delete()这样的对象和数据库表同步的方法。这在一年后的今天看来,实际上我已经自己做了一个O/R Mapping的工作了。当时自己做这样的O/R Mapping是相当痛苦的事情,而且用的方法也是最粗浅的,就是整个对象更数据库同步,即使没有修改的属性也同步,所以要同步的话,必须要先把对象从数据库取出来,修改后再写回。而且要求所有属性必须是nullable的non-primitive类型。第二个问题,对表中的外键怎么反应到bean中?比如每个学生有一个辅导教师,在数据库中很明显学生表需要一个外键参照教师id,但是在StudentBean中教师属性是写Teacher对象呢,还是Teacher id值呢,按照面向对象,很明显应该Teacher对象,但是我就觉得这是个多么heavy的事情呀!再想想教师情况,一个教师有一个Collection的Student对象是多么“重”!意味着load一个教师要load所有的students。于是我采用的方法是:还是用对象,比如teacher 的学生属性,还是一个collection  Student对象,同时提供了一个loadStudents()的方法,load teacher对象的时候并不load 学生属性,只有bean的用户显式调用loadStudents()的时候才会加载一组student对象,这在现在看来似乎我自觉不自觉的实现了lazy loading?

现在审视当时编写的javabean,犯的最大最大的错误就是把data access object和business workflow混在一起了。比如把教师所有要调用的功能都放在教师对象里面,而有些功能有时是要涉及几个bean的。现在看了简直是惨不忍睹的设计,虽然当时也困惑过,但是却没有动动脑筋来解决,我现在看了session bean 的思路时,觉得是那么的舒畅、自然。

当时困惑的还有关于jsp的处理,jsp只是负责显示的,但是为什么一个jsp提交的数据要给另外一个jsp去处理呢?这是很不舒畅的做法,于是有了最初的servlet做控制器的想法,但是当时顾不了研究那么多,也没有最终实现,毕竟前台不是我负责的。当我现在知道了Structs,MVC Model2的时候,我以前的困惑都随之解决。又一次感觉非常舒畅。

以上是我一个新手的第一个项目的一些情况,是在非常闭塞的环境里面做的,上网都是电话卡拨号,根本没有接触到主流技术,但是正是这种环境下积累的无数困惑,使我遇到EJB、Hibernate、Structs的时候如饮甘露,迅速的吸收了他们的营养。否则我想学习这些技术无疑是很耗时间的,因为根本没有更这种技术产生共鸣,不知道他们搞那么多框框究竟想干什么,而我产生了一种共鸣,他们这些框框,正是开我心头疑惑的锁。

又回到开头孔子的话,事实上我在做项目中,一直都是按自己的思考方式在“思”而没有去“学”,所以常常感到疲倦而无所得,即“殆”。但是如果不实际自己去动手做,而光光学j2ee,必然会很迷惑,即“罔”。我感觉先有困惑再有解决,是掌握技术的十分有效的而且巩固的方法,而且很有创造性,是属于归纳的思考方式。所以碰到问题,首先需要想想按常理该怎么去解决,再去寻找别人怎么解决的,这样自己提高会十分迅速。

现在我正在用EJB、Structs来重写那个项目。虽然我对整个需求已经相当清楚,毕竟有了第一个项目作为原型,但是我还是试图使用比较规范的方法来设计。首先分析了很多use case,并且使用Rational的RequisitePro来跟踪需求的变化。接着又使用Rose对use case画了use case diagram。对关键对象交互过程画了 sequence diagram,对某些对象的状态画了state diagram。最后很自然的导出了最重要的class diagram。

我现在最大的困惑在Entity Bean上面,建模的时候很自然会有一个User对象,Student, Teacher, Manager 对象继承自这个User对象。那数据库表怎么设计呢?第一种,三个对象三个表,对应StudentBean TeacherBean ManagerBean三个EntityBean。第二种,三个对象一个表,只有一个UserBean。第三种,把User单独一个对象,那么就是四个对象四个表,这样一个子对象,要映射两张表EntityBean不行吧。
暂时不管究竟用什么设计,来考察一下文档对像DocBean,我们主要看看它的cmr部分,一个cmr是author 属性,它应该是三种角色对象中的一种,问题来了,如果三个对象三个表,那么我这里的cmr怎么写?
public abstract StudentLocal  getAuthor(); ?
public abstract TeacherLocal  getAuthor(); ?
public abstract ManagerLocal  getAuthor(); ?
考察第二种,三个对象一个表,只有一个UserBean。那这个是相当简单的只需要
public abstract UserLocal getAuthor();
那最后只有第二种方式才能解决问题,就是只有一个User对象,于是我为了使用EntityBean不得不对我的模型进行修改,把原来清晰的三个对象揉合到一起,虽然说以后某个student变成了teacher 或者manager可以很方便的升级,但是这种情况在我这个例子里面是很少有的,而且数据库这种合并子类的方法给teacher,manager形成了很多的null值,虽然这是忽略不计的,但是总不是优雅的做法,最关键的是我的对象模型又一次向关系模型妥协了。

CMR的另外一个问题是,要CMR必须把所有相关的Bean放一个包里面,在一个xml文件里面配置,我的项目里面的entity bean都是要更user有关系的,那我都需要把这些bean打成一个包,用一个xml描述!我的还是小项目,如果一个项目有100个entity bean,这些entity bean都是相互关联的,那我要把这100个entity bean放在打成一个包,用一个xml配置!那这个xml文件该有多长!一个小小的错误全部完蛋。

看来Entity Bean确实如很多人说的那样不是十分灵活,我也开始倾向于用hibernate来做持久化了,但是我需要有足够的灵活性,我想继承和多态的支持还是很重要的,这样才能真正是“object-oriented domain models”方式,而不是以数据库表为中心的方式,引用Hibernate的一句话“Hibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the Java-based middle-tier”

我对AOP也是十分关注,因为它实在是太激动人心的概念了,有了AOP那么我们还要容器干什么,容器的功能完全可以通过AOP来实现,就像Spring框架,除了没有分布式外几乎都能支持吧。而jboss4.0也已经通过AOP实现了。考察我的项目,我发现有一个这样的需求需要满足,就是:在执行某些business logic之前必须要检测对这个方法的调用是否超过期限了,比如学生去选择研究课题,如果选择过了就不能再次选择,虽然这个方法可以由前台很简单的解决,但是我觉得在业务逻辑层防止这种行为的发生是业务完整性的一个部分。而通过AOP,把这样一个很多方法都要调用的公共功能作为一个aspect是很好的。我的问题是在容器中运行的EJB能够用AOP吗?在Jboss4.0里面各种EJB都是通过AOP方式来提供服务的,似乎我自己多加一层业务层面的服务应该是可行的(使用jboss aop),但是这样的EJB放在别的容器里面运行会怎么样?会影响到容器对EJB的干预吗?请各位前辈指点。

posted @ 2005-10-27 12:48 rkind 阅读(140) | 评论 (0)编辑 收藏
在这里我谈谈我在学习J2EE流程,并谈到在此过程中领会的经验和教训。以便后来者少走弯路。

Java发展到现在,按应用来分主要分为三大块:J2SE,J2ME和J2EE。这三块相互补充,应用范围不同。

J2SE就是Java2的标准版,主要用于桌面应用软件的编程;

J2ME主要应用于嵌入是系统开发,如手机和PDA的编程;

J2EE是Java2的企业版,主要用于分布式的网络程序的开发,如电子商务网站和ERP系统。

先学习J2SE

要学习J2EE就要先学习J2SE,刚开始学习J2SE先建议不要使用IDE,然后渐渐的过渡到使用IDE开发,毕竟用它方便嘛。学习J2SE推荐两本书,《java2核心技术一二卷》,《java编程思想》,《java与模式》。其中《java编程思想》要研读,精读。这一段时间是基本功学习,时间会很长,也可能很短,这要看学习者自身水平而定。

不要被IDE纠缠

学习java和J2EE过程中,你会遇到五花八门的IDE,不要被他们迷惑,学JAVA的时候,要学语言本身的东西,不要太在意IDE的附加功能,JAVA编程在不同IDE之间的转换是很容易的,过于的在意IDE的功能反而容易耽误对语言本身的理解。目前流行的IDE有jbuilder,eclipse和eclipse的加强版WSAD。用好其中一个就可以了,推荐从eclipse入手J2EE。因为Jbuilder更适合于写J2SE程序。

选择和学习服务器使用配置

当你有了J2SE和IDE的经验时,可以开始J2EE的学习了,web服务器:tomcat,勿庸置疑,tomcat为学习web服务首选。而应用服务器目前主要有三个:jboss、weblogic、websphere。有很多项目开始采用jboss,并且有大量的公司开始做websphere或weblogic向jboss应用服务器的移植(节省成本),这里要说的是,学习tomcat和jboss我认为是首选,也是最容易上手的。

学习服务器使用配置最好去询问有经验的人(有条件的话),因为他们或许一句话就能解决问题,你自己上网摸索可能要一两天(我就干过这种傻事),我们应该把主要时间放在学习原理和理论上,一项特定技术的使用永远代替不了一个人的知识和学问。

学习web知识

如果你是在做电子商务网站等时,你可能要充当几个角色,这是你还要学习:

1、html,可能要用到dreamwave等IDE。

2、Javascript,学会简单的数据校验,数据联动显示等等

J2EEAPI学习

学习J2EEAPI和学习服务器应该是一个迭代的过程。先学习jsp和servlet编程,这方面的书很多,我建立看oreilly公司的两本《jsp设计》和《java servlet编程》,oreilly出的书总是那么优秀,不得不佩服

。学习jdbc数据库编程,J2EE项目大多都是MIS系统,访问数据库是核心。这本应属于J2SE学习中,这里拿出来强调一下。学习jndi api,它和学习ejb可以结合起来。学习ejb api,推荐书《精通ejb》。经过上面的这些的学习,大概可以对付一般的应用了。有人说跟着sun公司的《J2EE tutorial》一路学下来,当然也可以。

学习ejb设计模式和看代码(最重要)

设计模式是练内功,其重要性可以这么说吧,如果你不会用设计模式的话,你将写出一堆使用了ejb的垃圾,有慢又是一堆bug,其结果不如不用ejb实现(ejb不等于J2EE)。无论学习什么语言,都应该看大量代码,你看的代码量不到一定数量,是学不好J2EE的。

目前有很多开源的工程可以作为教材:

jive论坛

petstore sun公司

dune sun公司等等,研读一个,并把它用到自己的工程中来。

J2EE其他学习

当你渐渐对J2EE了解到一定深度时,你要开始关注当前领域中的一些技术变化,J2EE是一块百家争鸣的领域,大家都在这里提出自己的解决方案,例如structs,hiberate,ofbiz等等,学习这些东西要你的项目和目标而定,预先补充一下未尝不可,但不用涉及太深,毕竟学习原理和理论是最最重要的事。

目前常见J2EEAPI

JavaServer Pages(JSP)技术1.2

Java Servlet技术2.3

JDBC API 2.0

Java XML处理API(JAXP)1.1

Enterprise JavaBeans技术2.0

Java消息服务(JMS)1.0

Java命名目录接口(JNDI)1.2

Java事务API(JTA) 1.0

JavaMail API 1.2

JavaBeans激活架构(JAF)1.0

J2EE连接器体系结构(JCA)1.0

Java认证和授权服务(JAAS)1.0

posted @ 2005-10-27 12:47 rkind 阅读(151) | 评论 (0)编辑 收藏
现在JDK1.4里终于有了自己的正则表达式API包,JAVA程序员可以免去找第三方提供的正则表达式库的周折了,我们现在就马上来了解一下这个SUN提供的迟来恩物- -对我来说确实如此。
1.简介: 
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。

它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表现模式。 
Matcher 一个Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查。 首先一个Pattern实例订制了一个所用语法与PERL的类似的正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作。

以下我们就分别来看看这两个类:

2.Pattern类: 
Pattern的方法如下: static Pattern compile(String regex)
将给定的正则表达式编译并赋予给Pattern类 
static Pattern compile(String regex, int flags)
同上,但增加flag参数的指定,可选的flag参数包括:CASE INSENSITIVE,MULTILINE,DOTALL,UNICODE CASE, CANON EQ 
int flags()
返回当前Pattern的匹配flag参数. 
Matcher matcher(CharSequence input)
生成一个给定命名的Matcher对象 
static boolean matches(String regex, CharSequence input)
编译给定的正则表达式并且对输入的字串以该正则表达式为模开展匹配,该方法适合于该正则表达式只会使用一次的情况,也就是只进行一次匹配工作,因为这种情况下并不需要生成一个Matcher实例。  
String pattern()
返回该Patter对象所编译的正则表达式。 
String[] split(CharSequence input)
将目标字符串按照Pattern里所包含的正则表达式为模进行分割。 
String[] split(CharSequence input, int limit)
作用同上,增加参数limit目的在于要指定分割的段数,如将limi设为2,那么目标字符串将根据正则表达式分为割为两段。 


一个正则表达式,也就是一串有特定意义的字符,必须首先要编译成为一个Pattern类的实例,这个Pattern对象将会使用matcher()方法来生成一个Matcher实例,接着便可以使用该 Matcher实例以编译的正则表达式为基础对目标字符串进行匹配工作,多个Matcher是可以共用一个Pattern对象的。

现在我们先来看一个简单的例子,再通过分析它来了解怎样生成一个Pattern对象并且编译一个正则表达式,最后根据这个正则表达式将目标字符串进行分割: 
import java.util.regex.*;
public class Replacement{
      public static void main(String[] args) throws Exception {
        // 生成一个Pattern,同时编译一个正则表达式
        Pattern p = Pattern.compile("[/]+");
        //用Pattern的split()方法把字符串按"/"分割
        String[] result = p.split(
"Kevin has seen《LEON》seveal times,because it is a good film."
+"/ 凯文已经看过《这个杀手不太冷》几次了,因为它是一部"
+"好电影。/名词:凯文。");
        for (int i=0; i<result.length; i++)
System.out.println(result[i]);
}
}




输出结果为:

Kevin has seen《LEON》seveal times,because it is a good film.
凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。
名词:凯文。

很明显,该程序将字符串按"/"进行了分段,我们以下再使用 split(CharSequence input, int limit)方法来指定分段的段数,程序改动为:
tring[] result = p.split("Kevin has seen《LEON》seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。/名词:凯文。",2);

这里面的参数"2"表明将目标语句分为两段。

输出结果则为:

Kevin has seen《LEON》seveal times,because it is a good film.
凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。/名词:凯文。

由上面的例子,我们可以比较出java.util.regex包在构造Pattern对象以及编译指定的正则表达式的实现手法与我们在上一篇中所介绍的Jakarta-ORO 包在完成同样工作时的差别,Jakarta-ORO 包要先构造一个PatternCompiler类对象接着生成一个Pattern对象,再将正则表达式用该PatternCompiler类的compile()方法来将所需的正则表达式编译赋予Pattern类:

PatternCompiler orocom=new Perl5Compiler();

Pattern pattern=orocom.compile("REGULAR EXPRESSIONS");

PatternMatcher matcher=new Perl5Matcher();

但是在java.util.regex包里,我们仅需生成一个Pattern类,直接使用它的compile()方法就可以达到同样的效果:
Pattern p = Pattern.compile("[/]+");

因此似乎java.util.regex的构造法比Jakarta-ORO更为简洁并容易理解。

3.Matcher类:
Matcher方法如下: Matcher appendReplacement(StringBuffer sb, String replacement)
将当前匹配子串替换为指定字符串,并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里。
StringBuffer appendTail(StringBuffer sb)
将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。
int end()
返回当前匹配的子串的最后一个字符在原目标字符串中的索引位置 。
int end(int group)
返回与匹配模式里指定的组相匹配的子串最后一个字符的位置。
boolean find()
尝试在目标字符串里查找下一个匹配子串。
boolean find(int start)
重设Matcher对象,并且尝试在目标字符串里从指定的位置开始查找下一个匹配的子串。
String group()
返回当前查找而获得的与组匹配的所有子串内容
String group(int group)
返回当前查找而获得的与指定的组匹配的子串内容
int groupCount()
返回当前查找所获得的匹配组的数量。
boolean lookingAt()
检测目标字符串是否以匹配的子串起始。
boolean matches()
尝试对整个目标字符展开匹配检测,也就是只有整个目标字符串完全匹配时才返回真值。
Pattern pattern()
返回该Matcher对象的现有匹配模式,也就是对应的Pattern 对象。
String replaceAll(String replacement)
将目标字符串里与既有模式相匹配的子串全部替换为指定的字符串。
String replaceFirst(String replacement)
将目标字符串里第一个与既有模式相匹配的子串替换为指定的字符串。
Matcher reset()
重设该Matcher对象。
Matcher reset(CharSequence input)
重设该Matcher对象并且指定一个新的目标字符串。
int start()
返回当前查找所获子串的开始字符在原目标字符串中的位置。
int start(int group)
返回当前查找所获得的和指定组匹配的子串的第一个字符在原目标字符串中的位置。


(光看方法的解释是不是很不好理解?不要急,待会结合例子就比较容易明白了)

一个Matcher实例是被用来对目标字符串进行基于既有模式(也就是一个给定的Pattern所编译的正则表达式)进行匹配查找的,所有往Matcher的输入都是通过CharSequence接口提供的,这样做的目的在于可以支持对从多元化的数据源所提供的数据进行匹配工作。

我们分别来看看各方法的使用:

★matches()/lookingAt ()/find():
一个Matcher对象是由一个Pattern对象调用其matcher()方法而生成的,一旦该Matcher对象生成,它就可以进行三种不同的匹配查找操作:

matches()方法尝试对整个目标字符展开匹配检测,也就是只有整个目标字符串完全匹配时才返回真值。
lookingAt ()方法将检测目标字符串是否以匹配的子串起始。
find()方法尝试在目标字符串里查找下一个匹配子串。

以上三个方法都将返回一个布尔值来表明成功与否。

★replaceAll ()/appendReplacement()/appendTail():
Matcher类同时提供了四个将匹配子串替换成指定字符串的方法:

replaceAll()
replaceFirst()
appendReplacement()
appendTail()

replaceAll()与replaceFirst()的用法都比较简单,请看上面方法的解释。我们主要重点了解一下appendReplacement()和appendTail()方法。

appendReplacement(StringBuffer sb, String replacement) 将当前匹配子串替换为指定字符串,并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里,而appendTail(StringBuffer sb) 方法则将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。

例如,有字符串fatcatfatcatfat,假设既有正则表达式模式为"cat",第一次匹配后调用appendReplacement(sb,"dog"),那么这时StringBuffer sb的内容为fatdog,也就是fatcat中的cat被替换为dog并且与匹配子串前的内容加到sb里,而第二次匹配后调用appendReplacement(sb,"dog"),那么sb的内容就变为fatdogfatdog,如果最后再调用一次appendTail(sb),那么sb最终的内容将是fatdogfatdogfat。

还是有点模糊?那么我们来看个简单的程序:
//该例将把句子里的"Kelvin"改为"Kevin"
import java.util.regex.*;
public class MatcherTest{
public static void main(String[] args)
throws Exception {
//生成Pattern对象并且编译一个简单的正则表达式"Kelvin"
Pattern p = Pattern.compile("Kevin");
//用Pattern类的matcher()方法生成一个Matcher对象
Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in Kelvin Chen's KelvinSoftShop company");
StringBuffer sb = new StringBuffer();
int i=0;
//使用find()方法查找第一个匹配的对象
boolean result = m.find();
//使用循环将句子里所有的kelvin找出并替换再将内容加到sb里
while(result) {
i++;
m.appendReplacement(sb, "Kevin");
System.out.println("第"+i+"次匹配后sb的内容是:"+sb);
//继续查找下一个匹配对象
result = m.find();
}
//最后调用appendTail()方法将最后一次匹配后的剩余字符串加到sb里;
m.appendTail(sb);
System.out.println("调用m.appendTail(sb)后sb的最终内容是:"+ sb.toString());
}
}



最终输出结果为:
第1次匹配后sb的内容是:Kevin
第2次匹配后sb的内容是:Kevin Li and Kevin
第3次匹配后sb的内容是:Kevin Li and Kevin Chan are both working in Kevin
第4次匹配后sb的内容是:Kevin Li and Kevin Chan are both working in Kevin Chen's Kevin
调用m.appendTail(sb)后sb的最终内容是:Kevin Li and Kevin Chan are both working in Kevin Chen's KevinSoftShop company.

看了上面这个例程是否对appendReplacement(),appendTail()两个方法的使用更清楚呢,如果还是不太肯定最好自己动手写几行代码测试一下。

★group()/group(int group)/groupCount():
该系列方法与我们在上篇介绍的Jakarta-ORO中的MatchResult .group()方法类似(有关Jakarta-ORO请参考上篇的内容),都是要返回与组匹配的子串内容,下面代码将很好解释其用法:
import java.util.regex.*;

public class GroupTest{
public static void main(String[] args)
throws Exception {
Pattern p = Pattern.compile("(ca)(t)");
Matcher m = p.matcher("one cat,two cats in the yard");
StringBuffer sb = new StringBuffer();
boolean result = m.find();
System.out.println("该次查找获得匹配组的数量为:"+m.groupCount());
for(int i=1;i<=m.groupCount();i++){
System.out.println("第"+i+"组的子串内容为: "+m.group(i));
}
}
}



输出为:
该次查找获得匹配组的数量为:2
第1组的子串内容为:ca
第2组的子串内容为:t

Matcher对象的其他方法因比较好理解且由于篇幅有限,请读者自己编程验证。

4.一个检验Email地址的小程序:
最后我们来看一个检验Email地址的例程,该程序是用来检验一个输入的EMAIL地址里所包含的字符是否合法,虽然这不是一个完整的EMAIL地址检验程序,它不能检验所有可能出现的情况,但在必要时您可以在其基础上增加所需功能。
import java.util.regex.*;
public class Email {
public static void main(String[] args) throws Exception {
String input = args[0];
//检测输入的EMAIL地址是否以 非法符号"."或"@"作为起始字符
Pattern p = Pattern.compile("^\\.|^\\@");
Matcher m = p.matcher(input);
if (m.find()){
System.err.println("EMAIL地址不能以'.'或'@'作为起始字符");
}
//检测是否以"www."为起始
p = Pattern.compile("^www\\.");
m = p.matcher(input);
if (m.find()) {
System.out.println("EMAIL地址不能以'www.'起始");
}
//检测是否包含非法字符
p = Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+");
m = p.matcher(input);
StringBuffer sb = new StringBuffer();
boolean result = m.find();
boolean deletedIllegalChars = false;
while(result) {
//如果找到了非法字符那么就设下标记
deletedIllegalChars = true;
//如果里面包含非法字符如冒号双引号等,那么就把他们消去,加到SB里面
m.appendReplacement(sb, "");
result = m.find();
}
m.appendTail(sb);
input = sb.toString();
if (deletedIllegalChars) {
System.out.println("输入的EMAIL地址里包含有冒号、逗号等非法字符,请修改");
System.out.println("您现在的输入为: "+args[0]);
System.out.println("修改后合法的地址应类似: "+input);
}
}
}



例如,我们在命令行输入:java Email www.kevin@163.net

那么输出结果将会是:EMAIL地址不能以'www.'起始

如果输入的EMAIL为@kevin@163.net

则输出为:EMAIL地址不能以'.'或'@'作为起始字符

当输入为:cgjmail#$%@163.net

那么输出就是:

输入的EMAIL地址里包含有冒号、逗号等非法字符,请修改
您现在的输入为: cgjmail#$%@163.net
修改后合法的地址应类似: cgjmail@163.net

5.总结:
本文介绍了jdk1.4.0-beta3里正则表达式库--java.util.regex中的类以及其方法,如果结合与上一篇中所介绍的Jakarta-ORO API作比较,读者会更容易掌握该API的使用,当然该库的性能将在未来的日子里不断扩展,希望获得最新信息的读者最好到及时到SUN的网站去了解。

6.结束语:
本来计划再多写一篇介绍一下需付费的正则表达式库中较具代表性的作品,但觉得既然有了免费且优秀的正则表达式库可以使用,何必还要去找需付费的呢,相信很多读者也是这么想的:,所以有兴趣了解更多其他的第三方正则表达式库的朋友可以自己到网上查找或者到我在参考资料里提供的网址去看看。
posted @ 2005-10-27 12:44 rkind 阅读(173) | 评论 (0)编辑 收藏
仅列出标题
共8页: 上一页 1 2 3 4 5 6 7 8 下一页