以前经常会在List的index处理上犯微小的错误,或大一,或小一,但结果都是一样:wrong!所以在这里简要总结一下最容易出错的两个方法。
1. List的indexOf(Object obj)方法:
这个方法返回的是List中一个节点的序号。例如:第一个节点返回0,最后一个节点返回list.size()-1。
2. List的subList(int begin, int end)方法:
这个方法是取出一个List的一个子List,在两个参数上很容易犯错误。
以一个size为5的List为例:
subList(0, 2);取出的是第一个到第二个节点;
subList(0, 1);取出第一个节点;
subList(1, 1);不取出任何节点;
subList(2, 1);则会出参数错误的异常。
可以看出,begin为起始节点的序号,end为所要取的最后一个节点的序号+1。
jdk中的这些类有很多细节都需要非常注意,不然实际的coding中会遇到很多麻烦。
在一个有关Jdom的讨论中提到:
Elliotte Rusty Harold: XML documents must be well-formed. There are,
depending on how you count, anywhere from a hundred to several thousand
different rules. These "well-formedness" rules are the minimum requirements
for an XML document. The rules cover things like what characters are allowed
in element names: The letter 'a'
is OK. The letter omega is OK.
The asterisk character is not OK. White space is not OK. The rules say that
every start-tag has to have a matching end-tag. Elements can nest, but they
cannot overlap. Processing instructions have the form <
,
?
, a target, white space, the data, ?
, and a
>
. Comments cannot contain a double hyphen. There are
many such rules governing well-formedness of XML documents.
Validity talks about which elements and attributes are allowed where. Well-formedness
only talks about the structure of any XML document, irrespective of
what the names are. Validity says, we're only going to allow these elements
with these names in these positions. Validity is not required. Well-formedness
is.
JDOM, and for that matter DOM, allows you to create malformed documents.
They do not check everything they can possibly check. For instance, they do not
currently check that the text content of a text node does not contain the null
character, which is completely illegal in an XML document.
这是发生在2003年的讨论:http://www.artima.com/intv/jdom2.html。
现在,使用JDOM可以完成这两件事:
1. 验证XML文件的Wellformedness:
The
build() method of SAXBuilder throws
an
IOException
if an I/O error such as a broken socket prevents the document
from being completely read. It throws a
JDOMException if the document
is malformed. This is the generic superclass for most things
that can go wrong while working with JDOM other than I/O
errors.
Example:
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.IOException;
public class JDOMChecker {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java JDOMChecker URL");
return;
}
SAXBuilder builder = new SAXBuilder();
// command line should offer URIs or file names
try {
builder.build(args[0]);
// If there are no well-formedness errors,
// then no exception is thrown
System.out.println(args[0] + " is well-formed.");
}
// indicates a well-formedness error
catch (JDOMException e) {
System.out.println(args[0] + " is not well-formed.");
System.out.println(e.getMessage());
}
catch (IOException e) {
System.out.println("Could not check " + args[0]);
System.out.println(" because " + e.getMessage());
}
}
}
2. 验证XML文件的Validity:
SAXBuilder only checks
documents for well-formedness, not validity.
If you want to validate as well, then pass the boolean
true to the
SAXBuilder() constructor.
Then any validity errors will also cause
JDOMExceptions.
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.IOException;
public class JDOMValidator {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java JDOMValidator URL");
return;
}
SAXBuilder builder = new SAXBuilder(true);
// ^^^^
// Turn on validation
// command line should offer URIs or file names
try {
builder.build(args[0]);
// If there are no well-formedness or validity errors,
// then no exception is thrown.
System.out.println(args[0] + " is valid.");
}
// indicates a well-formedness or validity error
catch (JDOMException e) {
System.out.println(args[0] + " is not valid.");
System.out.println(e.getMessage());
}
catch (IOException e) {
System.out.println("Could not check " + args[0]);
System.out.println(" because " + e.getMessage());
}
}
}
参考文献:http://www.ibiblio.org/xml/books/xmljava/chapters/ch14s07.html
MSSQLServer2000
<data-sources>
<data-source key="dataSource" type="org.apache.commons.dbcp.BasicDataSource">
<set-property property="driverClassName" value="com.microsoft.jdbc.sqlserver.SQLServerDriver" />
<set-property property="url" value="jdbc:microsoft:sqlserver://localhost:1433;databaseName=ClassManageSys" />
<set-property property="username" value="sa" />
<set-property property="password" value="" />
<set-property property="maxActive" value="10" />
<set-property property="maxWait" value="5000" />
<set-property property="defaultAutoCommit" value="false" />
<set-property property="defaultReadOnly" value="false" />
</data-source>
</data-sources>
MySQL
<struts-config>
<data-sources>
<data-source key="dataSource" type="org.apache.commons.dbcp.BasicDataSource">
<set-property property="driverClassName" value="org.gjt.mm.mysql.Driver" />
<set-property property="url" value="jdbc:mysql://localhost/test" />
<set-property property="username" value="root" />
<set-property property="password" value="" />
<set-property property="maxActive" value="10" />
<set-property property="maxWait" value="5000" />
<set-property property="defaultAutoCommit" value="false" />
<set-property property="defaultReadOnly" value="false" />
</data-source>
</data-sources>
花了一天时间学习Log4j,现在差不多已经达到了熟练运用的程度了。
关于Log4j的文章很多,而且已经写得很好了,我在这里写也只能是copy & paste那些大牛们的作品。在此推荐我觉得讲得最清楚的一篇文章:
https://www.qos.ch/ac2001/F11-10.html
希望能对Log4j的学习者有所帮助。我看了几篇中文的文章,其实都是对这篇文章进行的翻译,其中还有一些不当的地方,建议还是学习英文原文为上。
此外,Log4j的官方主页http://logging.apache.org/log4j/docs/index.html上可以下载到相关的资源。
最后谈一下个人心得。对于一个web项目来说,可以构建一个Debugger类,专门服务于其他的类和JSP,这样就免去了在每一需要进行Debuging的地方都定义一个Logger实例,使用起来很方便。下面提供参考代码:
1 /*
2 * Created on 2006-3-14
3 *
4 * TODO To change the template for this generated file go to
5 * Window - Preferences - Java - Code Style - Code Templates
6 */
7 package util;
8
9 import org.apache.log4j.Logger;
10
11 /**
12 * @author dirkchen
13 *
14 * TODO To change the template for this generated type comment go to
15 * Window - Preferences - Java - Code Style - Code Templates
16 */
17 public class Debugger
18 {
19 protected static Logger logger = Logger.getLogger("ChildrenProject");
20
21 static
22 {
23 logger.setAdditivity( false );
24 }
25
26 public static void error( Throwable e )
27 {
28 logger.error("", e);
29 }
30
31 public static void debug( Object obj )
32 {
33 logger.debug( obj );
34 }
35 }
在其他地方只需要编写如下代码就可以进行debuging或者其他操作:
util.Debugger.debug(...);
当然,这时针对debug级的log信息。
Log4j基本使用方法
Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、
INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显
示内容。
1.定义配置文件
其实您也可以完全不使用配置文件,而是在代码中配置Log4j环境。但是,使用配置文件将使您的应用程序更加灵活。
Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:
配置根Logger,其语法为:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
其中,level
是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优
先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定
义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。
appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。
配置日志信息输出目的地Appender,其语法为
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN其中,Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
配置日志信息的格式(布局),其语法为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN其中,Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
2.在代码中使用Log4j
下面将讲述在程序代码中怎样使用Log4j。
2.1.得到记录器
使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:
public static Logger getLogger( String name),
通过指定的名字获得记录器,如果必要的话,则为这个名字创建一个新的记录器。Name一般取本类的名字,比如:
static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () ) ;
2.2.读取配置文件
当获得了日志记录器之后,第二步将配置Log4j环境,其语法为:
BasicConfigurator.configure (): 自动快速地使用缺省Log4j环境。
PropertyConfigurator.configure ( String configFilename) :读取使用Java的特性文件编写的配置文件。
DOMConfigurator.configure ( String filename ) :读取XML形式的配置文件。
2.3.插入记录信息(格式化日志信息)
当上两个必要步骤执行完毕,您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方,其语法如下:
Logger.debug ( Object message ) ;
Logger.info ( Object message ) ;
Logger.warn ( Object message ) ;
Logger.error ( Object message ) ;
在Java中这样定义一个整型常量:
static
final int aImmutableInt = 10;
上边的语句定义了一个常量,并赋初值为10。定义一个常量用到了
final的关键词。如果在程序的任何地方试图改变这个常量的值,都会遭到编译器的拒绝。
那么,我们能否定义一个
常量对象呢?做如下的尝试:
首先定义了一个Circle类:
class Circle
{
private double radius;
public Circle(double r)
{
radius = r;
}
public void setRadius(double r)
{
radius = r;
}
public double getRadius()
{
return radius;
}
}
下面,定义一个用final修饰的对象wheel,并试图改变它:
public class FinalTest
{
private static final Circle wheel = new Circle(5.0);
public static void main(String args[])
{
System.out.println("Radius of wheel is " + wheel.radius());
wheel = setRadius(7.4);
System.out.println("Radius of wheel is now " + wheel.radius());
}
}
这时编译器会很爽快地答应,并输出如下结果:
Radius of wheel is 5.0
Radius of wheel is now 7.4
这是为什么呢?
在实践一中谈到了,在java中定义一个对象,这个对象名就是这个对象的一个引用,这个引用的值是内存中的一个地址。在上边的例子中,用final修饰的
是对象的引用的值,而引用类似于一个地址,这里好像就是在对这个引用说:嘿,哥们,你只能指向这个对象了,不能再指向其他的对象了!
而在我们的程序中,这个引用的值确实没有改变,被改变的只是对象的属性值,所以不会报错!
如果把
wheel = setRadius(7.4);换成wheel = new Circle(7.4);则肯定不能编译通过。不信?你就试试!
Java中的参数传递传的是值value,而非对象的引用reference.
Java中,我们通过如下方法得到一个对象:
ArrayList aList = new ArrayList();
在此过程中,实际上生成了两个对象,一个是new ArrayList(),另一个是aList.Java中的对象名就是一个引用.什么是引用?从某种意义上讲,它与C中的指针有着相似之处,在我探讨的这个话题中,就可以把它看成指向某一内存地址的指针。
先看下面一个Java参数传递的例子:
import java.awt.Point;
class PassByValue
{
public static void modifyPoint(Point pt, int j)
{
pt.setLocation(5,5);
//1
j = 15;
System.out.println("During modifyPoint " + "pt = " + pt +" and j = " + j);
}
public static void main(String args[])
{
Point p = new
Point(0,0);
//2
int i = 10;
System.out.println("Before modifyPoint " + "p = " + p +" and i = " + i);
modifyPoint(p,
i);
//3
System.out.println("After modifyPoint " + "p = " + p +" and i = " + i);
}
}
在输出的结果中,Point对象的属性改变了,而int型数据i没有改变.于是一些人就草率地下结论说:Java中产数传递传的是引用!
仔细一想,会发现其中的问题.Java中定义的对象名就相当于一个指针,把它作为一个参数赋给一个方法的argument,实际上传的就是这个引用的
值.在这个过程中并没有构建新的对象,而是直接把原来指向对象的引用传到了方法中,因此方法中通过这个argument进行的改变都会影响到调用这个方法的主函数中对象的属性值.
引子:日前在研究Java对各种文件的处理方法,由于要帮助亲亲处理一批用于心理学分析数据,于是又动力十足地充满学习了Java操作Excel表格的方法。
刚开始我试着像处理通常的文本文件一样读入一个Excel文件的内容。采用这种方式毫无疑问地会抛出异常,因为Excel中除了每个单元格存储的内容之外还包含有单元格的格式信息,当然不是预期的结果。
于是我在网上查了处理Excel的方法。一下就发现了Java Excel
API。这是一个开源项目的研究内容。这个页面提供了这个API的说明,以及下载链接。借助于它提供的样例,我试着编写了用于处理Excel的程序。现引
用一下Rubber对API的使用方法的总结:
1 从Excel文件读取数据表
Java Excel
API既可以从本地文件系统的一个文件(.xls),也可以从输入流中读取Excel数据表。读取Excel数据表的第一步是创建Workbook(术语:工作薄),下面的代码片段举例说明了应该如何操作:(完整代码见ExcelReading.java)
import java.io.*; import jxl.*; … … … … try { //构建Workbook对象, 只读Workbook对象 //直接从本地文件创建Workbook //从输入流创建Workbook InputStream is = new FileInputStream(sourcefile); jxl.Workbook rwb = Workbook.getWorkbook(is); } catch (Exception e) { e.printStackTrace(); }
|
一旦创建了Workbook,我们就可以通过它来访问Excel Sheet(术语:工作表)。参考下面的代码片段:
//获取第一张Sheet表 Sheet rs = rwb.getSheet(0);
|
我们既可能通过Sheet的名称来访问它,也可以通过下标来访问它。如果通过下标来访问的话,要注意的一点是下标从0开始,就像数组一样。
一旦得到了Sheet,我们就可以通过它来访问Excel Cell(术语:单元格)。参考下面的代码片段:
//获取第一行,第一列的值 Cell c00 = rs.getCell(0, 0); String strc00 = c00.getContents();
//获取第一行,第二列的值 Cell c10 = rs.getCell(1, 0); String strc10 = c10.getContents();
//获取第二行,第二列的值 Cell c11 = rs.getCell(1, 1); String strc11 = c11.getContents();
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType()); System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType()); System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
|
如果仅仅是取得Cell的值,我们可以方便地通过getContents()方法,它可以将任何类型的Cell值都作为一个字符串返回。示例代码中Cell(0,
0)是文本型,Cell(1, 0)是数字型,Cell(1,1)是日期型,通过getContents(),三种类型的返回值都是字符型。
如果有需要知道Cell内容的确切类型,API也提供了一系列的方法。参考下面的代码片段:
String strc00 = null; double strc10 = 0.00; Date strc11 = null;
Cell c00 = rs.getCell(0, 0); Cell c10 = rs.getCell(1, 0); Cell c11 = rs.getCell(1, 1);
if(c00.getType() == CellType.LABEL) { LabelCell labelc00 = (LabelCell)c00; strc00 = labelc00.getString(); } if(c10.getType() == CellType.NUMBER) { NmberCell numc10 = (NumberCell)c10; strc10 = numc10.getValue(); } if(c11.getType() == CellType.DATE) { DateCell datec11 = (DateCell)c11; strc11 = datec11.getDate(); }
System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType()); System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType()); System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
|
在得到Cell对象后,通过getType()方法可以获得该单元格的类型,然后与API提供的基本类型相匹配,强制转换成相应的类型,最后调用相
应的取值方法getXXX(),就可以得到确定类型的值。API提供了以下基本类型,与Excel的数据格式相对应,如下图所示:
每种类型的具体意义,请参见Java Excel API Document。
当你完成对Excel电子表格数据的处理后,一定要使用close()方法来关闭先前创建的对象,以释放读取数据表的过程中所占用的内存空间,在读取大量数据时显得尤为重要。参考如下代码片段:
//操作完成时,关闭对象,释放占用的内存空间 rwb.close();
|
Java Excel API提供了许多访问Excel数据表的方法,在这里我只简要地介绍几个常用的方法,其它的方法请参考附录中的Java Excel API
Document。
Workbook类提供的方法
1. int getNumberOfSheets()
获得工作薄(Workbook)中工作表(Sheet)的个数,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); int sheets = rwb.getNumberOfSheets();
|
2. Sheet[] getSheets()
返回工作薄(Workbook)中工作表(Sheet)对象数组,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); Sheet[] sheets = rwb.getSheets();
|
3. String getVersion()
返回正在使用的API的版本号,好像是没什么太大的作用。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); String apiVersion = rwb.getVersion();
|
Sheet接口提供的方法
1) String getName()
获取Sheet的名称,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); String sheetName = rs.getName();
|
2) int getColumns()
获取Sheet表中所包含的总列数,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); int rsColumns = rs.getColumns();
|
3) Cell[] getColumn(int column)
获取某一列的所有单元格,返回的是单元格对象数组,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell[] cell = rs.getColumn(0);
|
4) int getRows()
获取Sheet表中所包含的总行数,示例:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); int rsRows = rs.getRows();
|
5) Cell[] getRow(int row)
获取某一行的所有单元格,返回的是单元格对象数组,示例子:
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell[] cell = rs.getRow(0);
|
6) Cell getCell(int column, int row)
获取指定单元格的对象引用,需要注意的是它的两个参数,第一个是列数,第二个是行数,这与通常的行、列组合有些不同。
jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile)); jxl.Sheet rs = rwb.getSheet(0); Cell cell = rs.getCell(0, 0);
|
2 生成新的Excel工作薄
下面的代码主要是向大家介绍如何生成简单的Excel工作表,在这里单元格的内容是不带任何修饰的(如:字体,颜色等等),所有的内容都作为字符串写入。(完整代码见ExcelWriting.java)
与读取Excel工作表相似,首先要使用Workbook类的工厂方法创建一个可写入的工作薄(Workbook)对象,这里要注意的是,只能通过
API提供的工厂方法来创建Workbook,而不能使用WritableWorkbook的构造函数,因为类WritableWorkbook的构造函
数为protected类型。示例代码片段如下:
import java.io.*; import jxl.*; import jxl.write.*; … … … … try { //构建Workbook对象, 只读Workbook对象 //Method 1:创建可写入的Excel工作薄 jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));
//Method 2:将WritableWorkbook直接写入到输出流 /* OutputStream os = new FileOutputStream(targetfile); jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os); */ } catch (Exception e) { e.printStackTrace(); }
|
API提供了两种方式来处理可写入的输出流,一种是直接生成本地文件,如果文件名不带全路径的话,缺省的文件会定位在当前目录,如果文件名带有全路
径的话,则生成的Excel文件则会定位在相应的目录;另外一种是将Excel对象直接写入到输出流,例如:用户通过浏览器来访问Web服务器,如果
HTTP头设置正确的话,浏览器自动调用客户端的Excel应用程序,来显示动态生成的Excel电子表格。
接下来就是要创建工作表,创建工作表的方法与创建工作薄的方法几乎一样,同样是通过工厂模式方法获得相应的对象,该方法需要两个参数,一个是工作表的名称,另一个是工作表在工作薄中的位置,参考下面的代码片段:
//创建Excel工作表 jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
|
"这锅也支好了,材料也准备齐全了,可以开始下锅了!",现在要做的只是实例化API所提供的Excel基本数据类型,并将它们添加到工作表中就可以了,参考下面的代码片段:
//1.添加Label对象 jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell"); ws.addCell(labelC);
//添加带有字型Formatting的对象 jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true); jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf); jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF); ws.addCell(labelCF);
//添加带有字体颜色Formatting的对象 jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED); jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc); jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC); ws.addCell(labelCF);
//2.添加Number对象 jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926); ws.addCell(labelN);
//添加带有formatting的Number对象 jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##"); jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf); jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN); ws.addCell(labelNF);
//3.添加Boolean对象 jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false); ws.addCell(labelB);
//4.添加DateTime对象 jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date()); ws.addCell(labelDT);
//添加带有formatting的DateFormat对象 jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss"); jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df); jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF); ws.addCell(labelDTF);
|
这里有两点大家要引起大家的注意。第一点,在构造单元格时,单元格在工作表中的位置就已经确定了。一旦创建后,单元格的位置是不能够变更的,尽管单元格的内容是可以改变的。第二点,单元格的定位是按照下面这样的规律(column,
row),而且下标都是从0开始,例如,A1被存储在(0, 0),B1被存储在(1, 0)。
最后,不要忘记关闭打开的Excel工作薄对象,以释放占用的内存,参见下面的代码片段:
//写入Exel工作表 wwb.write();
//关闭Excel工作薄对象 wwb.close();
|
这可能与读取Excel文件的操作有少少不同,在关闭Excel对象之前,你必须要先调用write()方法,因为先前的操作都是存储在缓存中的,所以要通过该方法将操作的内容保存在文件中。如果你先关闭了Excel对象,那么只能得到一张空的工作薄了。
3
拷贝、更新Excel工作薄
接下来简要介绍一下如何更新一个已经存在的工作薄,主要是下面二步操作,第一步是构造只读的Excel工作薄,第二步是利用已经创建的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段:(完整代码见ExcelModifying.java)
//创建只读的Excel工作薄的对象 jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));
//创建可写入的Excel工作薄对象 jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile), rw); //读取第一张工作表 jxl.write.WritableSheet ws = wwb.getSheet(0);
//获得第一个单元格对象 jxl.write.WritableCell wc = ws.getWritableCell(0, 0); //判断单元格的类型, 做出相应的转化 if(wc.getType() == CellType.LABEL) { Label l = (Label)wc; l.setString("The value has been modified."); }
//写入Excel对象 wwb.write();
//关闭可写入的Excel对象 wwb.close();
//关闭只读的Excel对象 rw.close();
|
之所以使用这种方式构建Excel对象,完全是因为效率的原因,因为上面的示例才是API的主要应用。为了提高性能,在读取工作表时,与数据相关的
一些输出信息,所有的格式信息,如:字体、颜色等等,是不被处理的,因为我们的目的是获得行数据的值,既使没有了修饰,也不会对行数据的值产生什么影响。
唯一的不利之处就是,在内存中会同时保存两个同样的工作表,这样当工作表体积比较大时,会占用相当大的内存,但现在好像内存的大小并不是什么关键因素了。
一旦获得了可写入的工作表对象,我们就可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()方法,因为单元格已经于工作表当中,所以我们只需要调用相应的setXXX()方法,就可以完成更新的操作了。
尽单元格原有的格式化修饰是不能去掉的,我们还是可以将新的单元格修饰加上去,以使单元格的内容以不同的形式表现。
新生成的工作表对象是可写入的,我们除了更新原有的单元格外,还可以添加新的单元格到工作表中,这与示例2的操作是完全一样的。
最后,不要忘记调用write()方法,将更新的内容写入到文件中,然后关闭工作薄对象,这里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的。
用例图描述了系统的外部接口、系统与参与者的交互行为;类图则描述系统的运行机制与内部协作关系。
分析类图的步骤:
一、识别类及其属性
从用例文档中抽取名词,分析这些名词,从中识别出哪些名词是类、哪些名词是类的属性。
分析中需要注意甄别:去除冗余的同义词;去除系统责任之外的名次;明晰模糊名词;排除行为性名词。
二、审查属性
(1)属性要在系统责任之内
(2)某一个类的属性应该只描述这个类的对象的特征
(3)同一个类的属性之间不能出现交叠
(4)对于具有复杂结构的属性可考虑建立新的类,再与原类形成关联
三、确定类之间的关系
1. 泛化
类之间的关系包括泛化(——|>)、关联(——)和依赖(------à)三种。
泛化指的就是继承关系,它是从父类(超类)的角度来讲的。
泛化的特征:
(1)is-a原则:子类是一种超类。所有子类都必须是超类的成员。
(2)100%原则:超类的定义(包括方法和属性)都不必须100%符合与所有子类。
泛化的缺点:
(1)超类的改变会影响到子类;(2)多重继承会引起命名的冲突。
怎样识别泛化?
(1)根据领域知识:如,生物>动物>哺乳动物>猫科动物>虎
(2)自上而下
含义:现在已经定义了一个类,分析发现这个类的对象有的具备一部分属性,而有些对象又具备另一些属性而没有这部分属性。这时可分解只属于部分对象的属性和操作形成子类。自上而下就是指由父类分解得到子类。
(3)自下而上
含义:与自上而下相反,自下而上指从子类中提取共有的属性和操作形成父类。
(4)考虑领域范围内的复用
如现钞取款机类,为了方便复用,可从中抽取出取款机类,再让现钞取款机类继承取款机类。
审查泛化
(1)是否同处一个领域
例如,温度、质量、高度、压力等量都由实数表示,但是不能把它们设计成实数的子类。
这是一种典型的泛化误用,“见人就叫老爸”!这样的后果:两个领域的类高度耦合,父类的子类无数,子类里的属性和操作无数,隐藏着巨大的危险;如果需要再用另外的操作时,还得再认一个老爸。
(2)子类之间的差异是否可以超类的属性值的不同来加以区别
这可以避免定义过多的子类而是问题复杂化。例如,人作为父类,再定义了中国人、美国人,其实这种差别可以通过在人这个类里添加一个国籍属性就可以实现了。
(3)如果子类没有自己特有的属性和操作,则取消这个子类的定义(子类上移);如果超类只有一个子类,则取消掉超类(超类下移)。
2. 类之间的关联
关联的含义:通过属性来表示一个对象对另一个对象的静态依赖关系。
首先,关联是对象间的;另外,关联是一种静态的关系,而不是通过操作。
关联的三种表现形式:
连接:—— 最弱的关联,表示两个类的对象之间有导航关系,即。
聚合:A◇——B 表示对象A包含一个对象B。
组合:◆—— 强语义耦合,如果整体消失则部分也消失。
聚合与组合比较:
(还没有找到合适的例子来解释)
审查聚合或组合
(1)作为部分的对象的属性有多少。比如汽车,单独定义一个轮胎类,汽车◇——轮胎。这里轮胎类里的属性过少,反而使设计不够清晰。应把轮胎合并到汽车类中。
(2)业务上是否有真正的聚合关系?
用例与需求
1. 系统边界和参与者的确定
系统边界指的是所涉及的系统涵盖的范围到达的边界。
参与者指位于系统边界、与系统有着功能或行为上的关系的任何事物。
(参与者是指:在系统之外,透过系统边界与系统进行有意义交互的任何事物http:/www.umlchina.com)
需要注意的是:
a.参与者位于系统之外,不是系统的成分。
b.参与者可担当多个角色。
c.系统存在着直接参与者和间接参与者。从业务建模角度考虑间接,而系统建模则只考虑直接参与者与系统之间的有意义的交互行为(这里的有意义指所涉及的系统所需要关注的东西)。
参与者的确定思路:
..谁使用系统的主要功能?
..谁改变系统的数据
..谁从系统获取信息
..谁需要系统的支持以完成日常工作任务?
..谁负责维护、管理并保持系统正常运行?
..系统需要应付(处理)哪些硬设备?
..系统需要和哪些外部系统交互?
..谁(或什么)对系统运行产生的结果(值)感兴趣?
..时间、气温等内部外部条件
考虑以上各个方面基本能够确定所设计的系统所有的参与者。
2. 在参与者基础上列出事件
参与者确定了,那么参与者会通过系统执行什么样的操作,或者会发生什么样的行为?
事件分为外部事件和内部事件。
外部事件往往由于参与者的主动行为导致;内部行为往往是当时间、温度等变量达到某一条件时由系统触发。
列出事件可采用头脑风暴法。
表述的格式通常为:
“主语+动词(+宾语)” 。如:会员+提交+用户名、密码
3. 识别用例
用例:用例实例是系统执行的一系列动作,这些动作将生成特定主角(参与者)可观测的结果值。一个用例定义一组用例实例。
用例的要点:
..可观测-->用例止于系统边界
..结果值-->用例是目标导向的
..系统执行-->结果值由系统生成
..由参与者观测-->业务语言,用户观点:从用户的角度考虑。
..一组用例实例-->用例的粒度
用例的命名:
(状语+)动词+(定语+)宾语
用例确定时常见错误:
..把交互的某个步骤当作用例
..把系统活动当作用例(而非用户视角)
..“四轮马车的错误”:CRUD:Create,Read,Update,Delete
如:把管理员的用户管理划分为四个用例,添加、修改、删除、查询。
系统建模蜕变成关系数据库的建模。“系统就是数据的增删改查”。这是常犯的错误,先关心数据的存储和维护,反而忽略了用户的目的。
注意粒度适度原则,如果CRUD不涉及复杂的交互,一个用例“管理××”即可。如果存在比较复杂的部分,如添加操作,可以把它独立成一个用例,extends-->管理用户用力。
4. 书写用例文档
略
5. 识别用例间的关系
用例间的三种关系:
(1)扩展(extends):用例B extends 用例A,表示用例B是用例A在某种特定情况下可能会出现的扩展用例。例如:老王进城办事,2小时就可以回去,在这2小时内内急时就会去上厕所。上厕所用例是进城用例的扩展,因为不上厕所老王进城办事也可完成。
(2)包含(includes):用例A includes 用例B,表示没有了用例B,用例A本身也就不完整了。例如:还是老王进城,他从海南来北京办事,3天才能回去,那么这种情况下进城用例与上厕所用例的关系就应该是包含关系了。
(3)泛化:泛化关系指的是同一业务目的的不同技术实现。例如:老王进城,他可以坐飞机,可以坐火车,还可以走路,那么进城用例就泛化为坐飞机、坐火车和走路三个用例了,它们之间存在层级关系。
总的来看,扩展可以“冻结”基本用例以保持稳定(因为扩展用例通常是不确定的);包含可以提供公共交互,提高“复用”;泛化是同一业务目的的不同技术实现。用例之间除了上述三种关系不再有其他关系,用例之间不能通讯。
6. 对用例进行优先级排序
略