??xml version="1.0" encoding="utf-8" standalone="yes"?> 1 引子 2 基础知识 2.1 相关概念 2.3 异常处理关键?br />
Java的异常处理是通过5个关键字来实现的QtryQcatchQthrowQthrowsQ?span class="hilite1">finally。JB的在U帮助中对这几个关键字是q样解释的: 3 关键字及其中语句程详解 3.1 try的嵌?br />
你可以在一个成员函数调用的外面写一个try语句Q在q个成员函数内部Q写另一个try语句保护其他代码。每当遇C个try语句Q异常的框架放到堆栈上面,直到所有的try语句都完成。如果下一U的try语句没有ҎU异常进行处理,堆栈׃展开Q直到遇到有处理q种异常的try语句。下面是一个try语句嵌套的例子? 3.2 try-catchE序块的执行程以及执行l果 3.3 try-catch-finallyE序块的执行程以及执行l果 HashMap通过hashcode对其内容q行快速查找,?span class="hilite1">TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得C个有序的l果你就应该使用TreeMapQHashMap中元素的排列序是不固定的)?img border="0" alt="" align="bottom" src="http://frontfree.net/articles/pages/0000000695/container.gif" /> 集合框架”提供两种常规?span lang="EN-US">Map实现Q?span lang="EN-US">HashMap?span lang="EN-US">TreeMap (TreeMap实现SortedMap接口)。在Map 中插入、删除和定位元素Q?span lang="EN-US">HashMap 是最好的选择。但如果您要按自焉序或自定义顺序遍历键Q那?span lang="EN-US">TreeMap会更好。?span lang="EN-US">HashMap要求d的键cL定义了hashCode()?span lang="EN-US">equals()的实现?/font> q个TreeMap没有调优选项Q因树d于^衡状态?/font> 2、两个对象值相?x.equals(y) == true)Q但却可有不同的hash codeQ这句话对不?hash code是什么意?/p>
hashcode是给一pdhash法用的Q比如hashtable。不同的对象应该有不同的hashcodeQ同一个对象应该有同样的hashcode 更正Q不是同一个对象,而是相等的对象,应该有相同的hashcode hash法是什么啊Q作用? hash法基本是Z一个对象和一个整数对应v来,不同的对象对应不同的整数? 3、String a="abc";String b=new String("abc");String c="abc"; System.out.println(a==b);f 4、a=0;b=0; 非静态内部类没有默认的构造函敎ͼ非静态内部类的构造函数都有一个外围类对象的引用?br />
内部cȝҎ语法规则Q?br />
1.相对内部c,引用其外部类隐式对象的Ş式:OuterClass.this 内部cL一U编译器现象与虚拟机无关。编译器内部类译为用$分隔外部cd和内部类名的常规cLӞ虚拟机对此ƈ无所知?br />
使用javap -private OuterClass$InnerClass。javapq个工具实Z错的Q对分析字节码和源码都有很大的帮助?br />
可以看出详细的内部类源码清单Q其中包括了~译器自动添加的部分Q?br />
public class Outer 当内部类是静态内部类Ӟ class Inner{ //那么权限是如何控制的呢?当内部类中的Ҏ讉K到外部类的私有数据时(注意如果内部cL有方法去讉K外部cȝU有数据不会生成该静态方法static int access$000(Outer);) class Inner 相应的外部类详细源码如下Q?br />
public class Outer public void methodOne(); 内部c访问外部类的private数据的这U方式很可能D危险Q虽然access$000不是一个合法的JavaҎ名,但是熟悉class文gl构的黑客可以用十六进制编辑器L创徏一个用虚拟机指令调用那个方法的cL件。由于隐U的讉KҎ需要拥有包可见性,所以攻M码需要与被攻ȝ攑֜同一个包中。MQ如果内部类讉K了外部类的私有数据域Q就有可能通过附加在外部类所在包中的其他c访问私有数据?/p>
局部内部类Q定义在Ҏ之中Q因此局部类没有public和private修饰W。对外界是完全隐藏的?br />
局部类不仅能够讉K外部c,q能讉KҎ中的局部变?或方法的参数)。不q那些局部变量要声明为final的?/p>
public void method(); final Outer this$0; 嵌套c:当内部类不需要访问外部类的数据成员时应该使用嵌套cR注意只有内部类可以声明为static的其他不行?br />
在接口中声明的内部类自动转换为public static的,在接口中定义的成员自动都是public static的?/p>
内部cL造函数的可见性与其类的可见性相同?/p>
try…catch…finally恐怕是大家再熟悉不q的语句了,而且感觉用v来也是很单,逻辑上似乎也是很Ҏ理解。不q,我亲自体验的“教训”告诉我,q个东西可不是想象中的那么简单、听话。不信?那你看看下面的代码,“猜猜”它执行后的结果会是什么?不要往后看{案、也不许执行代码看真正答案哦。如果你的答案是正确Q那么这文章你׃用浪Ҏ间看啦?br />
public class TestException
{
public TestException()
{
}
boolean testEx() throws Exception
{
boolean ret = true;
try
{
ret = testEx1();
}
catch (Exception e)
{
System.out.println("testEx, catch exception");
ret = false;
throw e;
}
finally
{
System.out.println("testEx, finally; return value=" + ret);
return ret;
}
}
boolean testEx1() throws Exception
{
boolean ret = true;
try
{
ret = testEx2();
if (!ret)
{
return false;
}
System.out.println("testEx1, at the end of try");
return ret;
}
catch (Exception e)
{
System.out.println("testEx1, catch exception");
ret = false;
throw e;
}
finally
{
System.out.println("testEx1, finally; return value=" + ret);
return ret;
}
}
boolean testEx2() throws Exception
{
boolean ret = true;
try
{
int b = 12;
int c;
for (int i = 2; i >= -2; i--)
{
c = b / i;
System.out.println("i=" + i);
}
return true;
}
catch (Exception e)
{
System.out.println("testEx2, catch exception");
ret = false;
throw e;
}
finally
{
System.out.println("testEx2, finally; return value=" + ret);
return ret;
}
}
public static void main(String[] args)
{
TestException testException1 = new TestException();
try
{
testException1.testEx();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
你的{案是什么?是下面的{案吗?
i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, catch exception
testEx1, finally; return value=false
testEx, catch exception
testEx, finally; return value=false
如果你的{案真的如上面所_那么你错啦。^_^Q那徏议你仔细看一看这文章或者拿上面的代码按各种不同的情况修攏V执行、测试,你会发现有很多事情不是原来想象中的那么简单的?br />
现在公布正确{案Q?br />
i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
testEx, finally; return value=false
例外是在E序q行q程中发生的异常事gQ比如除0溢出、数l越界、文件找不到{,q些事g的发生将LE序的正常运行。ؓ了加强程序的鲁棒性,E序设计Ӟ必须考虑到可能发生的异常事gq做出相应的处理。C语言中,通过使用if语句来判断是否出C例外Q同Ӟ调用函数通过被调用函数的q回值感知在被调用函C产生的例外事件ƈq行处理。全E变量ErroNo常常用来反映一个异怺件的cd。但是,q种错误处理机制会导致不问题?br />
Java通过面向对象的方法来处理例外。在一个方法的q行q程中,如果发生了例外,则这个方法生成代表该例外的一个对象,q把它交l运行时pȝQ运行时pȝL相应的代码来处理q一例外。我们把生成例外对象q把它提交给q行时系l的q程UCؓ抛弃(throw)一个例外。运行时pȝ在方法的调用栈中查找Q从生成例外的方法开始进行回朔,直到扑ֈ包含相应例外处理的方法ؓ止,q一个过E称为捕?catch)一个例外?br />
2.2 Throwablecd其子c?br />
用面向对象的Ҏ处理例外Q就必须建立cȝ层次。类 Throwable位于q一cdơ的最层Q只有它的后代才可以做ؓ一个例外被抛弃。图1表示了例外处理的cdơ?br />
从图中可以看出,cThrowable有两个直接子c:Error和Exception。Errorcd象(如动态连接错误等Q,由Java虚拟机生成ƈ抛弃Q通常QJavaE序不对q类例外q行处理Q;Exceptioncd象是JavaE序处理或抛弃的对象。它有各U不同的子类分别对应于不同类型的例外。其中类RuntimeException代表q行时由Java虚拟机生成的例外Q如术q算例外ArithmeticException(由除0错等D)、数l越界例外ArrayIndexOutOfBoundsException{;其它则ؓ非运行时例外Q如输入输出例外IOException{。Java~译器要求JavaE序必须捕获或声明所有的非运行时例外Q但对运行时例外可以不做处理?br />
Throws: Lists the exceptions a method could throw.
Throw: Transfers control of the method to the exception handler.
TryQ?nbsp; Opening exception-handling statement.
CatchQ?nbsp; Captures the exception.
FinallyQ?Runs its code before terminating the program.
2.3.1 try语句
try语句用大括号{}指定了一D代码,该段代码可能会抛弃一个或多个例外?br />
2.3.2 catch语句
catch语句的参数类gҎ的声明,包括一个例外类型和一个例外对象。例外类型必MؓThrowablecȝ子类Q它指明了catch语句所处理的例外类型,例外对象则由q行时系l在try所指定的代码块中生成ƈ被捕P大括号中包含对象的处理,其中可以调用对象的方法?br />
catch语句可以有多个,分别处理不同cȝ例外。Javaq行时系l从上到下分别对每个catch语句处理的例外类型进行检,直到扑ֈcd相匹配的catch语句为止。这里,cd匚w指catch所处理的例外类型与生成的例外对象的cd完全一致或者是它的父类Q因此,catch语句的排列顺序应该是从特D到一般?br />
也可以用一个catch语句处理多个例外cdQ这时它的例外类型参数应该是q多个例外类型的父类Q程序设计中要根据具体的情况来选择catch语句的例外处理类型。
2.3.3 finally语句
try所限定的代码中Q当抛弃一个例外时Q其后的代码不会被执行。通过finally语句可以指定一块代码。无论try所指定的程序块中抛弃或不抛弃例外,也无论catch语句的例外类型是否与所抛弃的例外的cd一_finally所指定的代码都要被执行Q它提供了统一的出口。通常?span class="hilite1">finally语句中可以进行资源的清除工作。如关闭打开的文件等?br />
2.3.4 throws语句
throwsL出现在一个函数头中,用来标明该成员函数可能抛出的各种异常。对大多数Exception子类来说QJava ~译器会你声明在一个成员函C抛出的异常的cd。如果异常的cd是Error?RuntimeExceptionQ?或它们的子类Q这个规则不起作用, 因ؓq在E序的正帔R分中是不期待出现的?如果你想明确地抛Z个RuntimeExceptionQ你必须用throws语句来声明它的类型?br />
2.3.5 throw语句
throwL出现在函C中,用来抛出一个异常。程序会在throw语句后立即终止,它后面的语句执行不到Q然后在包含它的所有try块中Q可能在上层调用函数中)从里向外L含有与其匚w的catch子句的try块?/p>
class MultiNest {
static void procedure() {
try {
int a = 0;
int b = 42/a;
} catch(java.lang.ArithmeticException e) {
System.out.println("in procedure, catch ArithmeticException: " + e);
}
}
public static void main(String args[]) {
try {
procedure();
} catch(java.lang. Exception e) {
System.out.println("in main, catch Exception: " + e);
}
}
}
q个例子执行的结果ؓQ?br />
in procedure, catch ArithmeticException: java.lang.ArithmeticException: / by zero
成员函数procedure里有自己的try/catch控制Q所以main不用d?ArrayIndexOutOfBoundsExceptionQ当然如果如同最开始我们做试的例子一P在procedure中catch到异常时使用throw e;语句异常抛出,那么main当然q是能够捕捉q处理这个procedure抛出来的异常。例如在procedure函数的catch中的System.out语句后面增加throw e;语句之后Q执行结果就变ؓQ?br />
in procedure, catch ArithmeticException: java.lang.ArithmeticException: / by zero
in main, catch Exception: java.lang.ArithmeticException: / by zero
相对于try-catch-finallyE序块而言Qtry-catch的执行流E以及执行结果还是比较简单的?br />
首先执行的是try语句块中的语句,q时可能会有以下三种情况Q?br />
1Q如果try块中所有语句正常执行完毕,那么׃会有其他?#8220;动做”被执行,整个try-catchE序块正常完成?br />
2Q如果try语句块在执行q程中碰到异常VQ这时又分ؓ两种情况q行处理Q?br />
-->如果异常V能够被与try相应的catch块catch刎ͼ那么W一个catch到这个异常的catch块(也是try最q的一个与异常V匚w的catch块)被执行Q如果catch块执行正常,那么try-catchE序块的l果是“正常完成”Q如果该catch块由于原因RH然中止Q那么try-catchE序块的l果是“׃原因RH然中止Qcompletes abruptlyQ?#8221;?br />
-->如果异常V没有catch块与之匹配,那么q个try-catchE序块的l果是“׃抛出异常V而突然中止(completes abruptlyQ?#8221;?br />
3Q?如果try׃其他原因RH然中止Qcompletes abruptlyQ,那么q个try-catchE序块的l果是“׃原因RH然中止Qcompletes abruptlyQ?#8221;?/p>
try-catch-finallyE序块的执行程以及执行l果比较复杂?br />
首先执行的是try语句块中的语句,q时可能会有以下三种情况Q?br />
1Q如果try块中所有语句正常执行完毕,那么finally块的居于׃被执行,q时分ؓ以下两种情况Q?br />
-->如果finally块执行顺利,那么整个try-catch-finallyE序块正常完成?br />
-->如果finally块由于原因RH然中止Q那么try-catch-finallyE序块的l局?#8220;׃原因RH然中止Qcompletes abruptlyQ?#8221;
2Q如果try语句块在执行q程中碰到异常VQ这时又分ؓ两种情况q行处理Q?br />
-->如果异常V能够被与try相应的catch块catch刎ͼ那么W一个catch到这个异常的catch块(也是try最q的一个与异常V匚w的catch块)被执行Q这时就会有两种执行l果Q?br />
-->如果catch块执行正常,那么finally块将会被执行Q这时分ZU情况:
-->如果finally块执行顺利,那么整个try-catch-finallyE序块正常完成?br />
-->如果finally块由于原因RH然中止Q那么try-catch-finallyE序块的l局?#8220;׃原因RH然中止Qcompletes abruptlyQ?#8221;
-->如果catch块由于原因RH然中止Q那?span class="hilite1">finally模块被执行Q分ZU情况:
-->如果如果finally块执行顺利,那么整个try-catch-finallyE序块的l局?#8220;׃原因RH然中止Qcompletes abruptlyQ?#8221;?br />
-->如果finally块由于原因SH然中止Q那么整个try-catch-finallyE序块的l局?#8220;׃原因SH然中止Qcompletes abruptlyQ?#8221;Q原因R被抛弃?br />
Q注意,q里正好和我们的例子相W合Q虽然我们在testEx2中用throw e抛出了异常,但是׃testEx2中有finally块,?span class="hilite1">finally块的执行l果是complete abruptly的(别小看这个用得最多的returnQ它也是一U导致complete abruptly的原因之一啊——后文中有关于导致complete abruptly的原因分析)Q所以整个try-catch-finallyE序块的l果?#8220;complete abruptly”Q所以在testEx1中调用testEx2时是捕捉不到testEx1中抛出的那个异常的,而只能将finally中的returnl果获取到?br />
如果在你的代码中期望通过捕捉被调用的下函数的异常来l定q回|那么一定要注意你所调用的下U函C?span class="hilite1">finally语句Q它有可能会使你throw出来的异常ƈ不能真正被上U调用函数可见的。当然这U情冉|可以避免的,以testEx2ZQ如果你一定要使用finally而且又要catch中throw的e在testEx1中被捕获刎ͼ那么你去掉testEx2中的finally中的return可以了?br />
q个事情已经在OMC2.0的MIB中出现过啦:服务器的异常不能完全被反馈到客户端。)
-->如果异常V没有catch块与之匹配,那么finally模块被执行Q分ZU情况:
-->如果finally块执行顺利,那么整个try-catch-finallyE序块的l局是“׃抛出异常V而突然中止(completes abruptlyQ?#8221;?br />
-->如果finally块由于原因SH然中止Q那么整个try-catch-finallyE序块的l局?#8220;׃原因SH然中止Qcompletes abruptlyQ?#8221;Q异常V被抛弃?br />
3Q如果try׃其他原因RH然中止Qcompletes abruptlyQ,那么finally块被执行Q分ZU情况:
-->如果finally块执行顺利,那么整个try-catch-finallyE序块的l局?#8220;׃原因RH然中止Qcompletes abruptlyQ?#8221;?br />
-->如果finally块由于原因SH然中止Q那么整个try-catch-finallyE序块的l局?#8220;׃原因SH然中止Qcompletes abruptlyQ?#8221;Q原因R被抛弃?br />
3.4 try-catch-finallyE序块中的return
从上面的try-catch-finallyE序块的执行程以及执行l果一节中可以看出无论try或catch中发生了什么情况,finally都是会被执行的,那么写在try或者catch中的return语句也就不会真正的从该函C跛_了,它的作用在这U情况下变成了控制权Q语句流E)转到finally块中Q这U情况下一定要注意q回值的处理?br />
例如Q在try或者catch中return false了,而在finally中又return trueQ那么这U情况下不要期待你的try或者catch中的return false的返回值false被上U调用函数获取到Q上U调用函数能够获取到的只?span class="hilite1">finally中的q回|因ؓtry或者catch中的return语句只是转移控制权的作用?br />
3.5 如何抛出异常
如果你知道你写的某个函数有可能抛出异常,而你又不惛_q个函数中对异常q行处理Q只是想把它抛出去让调用q个函数的上U调用函数进行处理,那么有两U方式可供选择Q?br />
W一U方式:直接在函数头中throws SomeExceptionQ函C中不需要try/catch。比如将最开始的例子中的testEx2改ؓ下面的方式,那么testEx1p捕捉到testEx2抛出的异怺?br />
boolean testEx2() throws Exception{
boolean ret = true;
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
}
return true;
}
W二U方式:使用try/catchQ在catch中进行一定的处理之后Q如果有必要的话Q抛出某U异常。例如上面的testEx2改ؓ下面的方式,testEx1也能捕获到它抛出的异常:
boolean testEx2() throws Exception{
boolean ret = true;
try{
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
}
return true;
}catch (Exception e){
System.out.println("testEx2, catch exception");
Throw e;
}
}
W三U方法:使用try/catch/finallyQ在catch中进行一定的处理之后Q如果有必要的话Q抛出某U异常。例如上面的testEx2改ؓ下面的方式,testEx1也能捕获到它抛出的异常:
boolean testEx2() throws Exception{
boolean ret = true;
try{
int b=12;
int c;
for (int i=2;i>=-2;i--){
c=b/i;
System.out.println("i="+i);
throw new Exception("aaa");
}
return true;
}catch (java.lang.ArithmeticException e){
System.out.println("testEx2, catch exception");
ret = false;
throw new Exception("aaa");
}finally{
System.out.println("testEx2, finally; return value="+ret);
}
}
4 关于abrupt completion
前面提到了complete abruptlyQ暂且理解ؓ“H然中止”或?#8220;异常l束”吧)Q它主要包含了两U大的情形:abrupt completion of expressions and statementsQ下面就分两U情况进行解释?br />
4.1 Normal and Abrupt Completion of Evaluation
每一个表辑ּQexpressionQ都有一U得其包含的计得以一步步q行的正常模式,如果每一步计都被执行且没有异常抛出Q那么就U这个表辑ּ“正常l束Qcomplete normallyQ?#8221;Q如果这个表辑ּ的计抛Z异常Q就UCؓ“异常l束Qcomplete abruptlyQ?#8221;。异常结束通常有一个相兌的原因(associated reasonQ,通常也就是抛Z个异常V?br />
与表辑ּ、操作符相关的运行期异常有:
-->A class instance creation expression, array creation expression , or string concatenation operatior expression throws an OutOfMemoryError if there is insufficient memory available.
-->An array creation expression throws a NegativeArraySizeException if the value of any dimension expression is less than zero.
-->A field access throws a NullPointerException if the value of the object reference expression is null.
-->A method invocation expression that invokes an instance method throws a NullPointerException if the target reference is null.
-->An array access throws a NullPointerException if the value of the array reference expression is null.
-->An array access throws an ArrayIndexOutOfBoundsException if the value of the array index expression is negative or greater than or equal to the length of the array.
-->A cast throws a ClassCastException if a cast is found to be impermissible at run time.
-->An integer division or integer remainder operator throws an ArithmeticException if the value of the right-hand operand expression is zero.
-->An assignment to an array component of reference type throws an ArrayStoreException when the value to be assigned is not compatible with the component type of the array.
4.2 Normal and Abrupt Completion of Statements
正常情况我们׃多说了,在这里主要是列出了abrupt completion的几U情况:
-->break, continue, and return 语句导致控制权的{换,从而得statements不能正常地、完整地执行?
-->某些表达式的计算也可能从java虚拟机抛出异常,q些表达式在上一节中已lȝq了Q一个显式的的throw语句也将D异常的抛出。抛出异怹是导致控制权的{换的原因Q或者说是阻止statement正常l束的原因)?
如果上述事g发生了,那么q些statement有可能使得其正常情况下应该都执行的语句不能完全被执行到Q那么这些statement也就是被UCؓ是complete abruptly.
Dabrupt completion的几U原因:
-->A break with no label
-->A break with a given label
-->A continue with no label
-->A continue with a given label
-->A return with no value
-->A return with a given value A
-->throw with a given value, including exceptions thrown by the Java virtual machine
5 关于我们的编E的一点徏?br />
弄清楚try-catch-finally的执行情况后我们才能正确使用它?br />
如果我们使用的是try-catch-finally语句块,而我们又需要保证有异常时能够抛出异常,那么?span class="hilite1">finally语句中就不要使用return语句了(finally语句块的最重要的作用应该是释放甌的资源)Q因?span class="hilite1">finally中的return语句会导致我们的throw e被抛弃,在这个try-catch-finally的外面将只能看到finally中的q回|除非?span class="hilite1">finally中抛出异常)。(我们需要记住:不仅throw语句是abrupt completion 的原因,return、break、continue{这些看h很正常的语句也是Dabrupt completion的原因。)
1QDOMQJAXP Crimson解析器)
DOM是用与^台和语言无关的方式表CXML文的官方W3C标准。DOM是以层次l构l织的节Ҏ信息片断的集合。这个层ơ结构允许开发h员在树中L特定信息。分析该l构通常需要加载整个文和构造层ơ结构,然后才能做Q何工作。由于它是基于信息层ơ的Q因而DOM被认为是Z树或Z对象的。DOM以及q义的基于树的处理具有几个优炏V首先,׃树在内存中是持久的,因此可以修改它以便应用程序能Ҏ据和l构作出更改。它q可以在M时候在树中上下DQ而不是像SAX那样是一ơ性的处理。DOM使用h也要单得多?
2QSAX
SAX处理的优炚w常类g媒体的优点。分析能够立卛_始,而不是等待所有的数据被处理。而且Q由于应用程序只是在d数据时检查数据,因此不需要将数据存储在内存中。这对于大型文来说是个巨大的优炏V事实上Q应用程序甚至不必解析整个文;它可以在某个条g得到满时停止解析。一般来_SAXq比它的替代者DOM快许多?
选择DOMq是选择SAXQ?对于需要自q写代码来处理XML文的开发h员来_ 选择DOMq是SAX解析模型是一个非帔R要的设计决策?DOM采用建立树Şl构的方式访问XML文Q而SAX采用的事件模型?
DOM解析器把XML文转化Z个包含其内容的树Qƈ可以Ҏq行遍历。用DOM解析模型的优Ҏ~程ҎQ开发h员只需要调用徏树的指oQ然后利用navigation APIs讉K所需的树节点来完成Q务。可以很Ҏ的添加和修改树中的元素。然而由于用DOM解析器的时候需要处理整个XML文Q所以对性能和内存的要求比较高,其是遇到很大的XML文g的时候。由于它的遍历能力,DOM解析器常用于XML文需要频J的改变的服务中?
SAX解析器采用了Z事g的模型,它在解析XML文的时候可以触发一pd的事Ӟ当发现给定的tag的时候,它可以激zM个回调方法,告诉该方法制定的标签已经扑ֈ。SAX对内存的要求通常会比较低Q因为它让开发h员自己来军_所要处理的tag.特别是当开发h员只需要处理文中所包含的部分数据时QSAXq种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难Q而且很难同时讉K同一个文中的多处不同数据?
3QJDOM http://www.jdom.org
JDOM的目的是成ؓJava特定文模型Q它化与XML的交互ƈ且比使用DOM实现更快。由于是W一个Java特定模型QJDOM一直得到大力推q和促进。正在考虑通过“Java规范hJSR-102”它最l用?#8220;Java标准扩展”。从2000q初已l开始了JDOM开发?
JDOM与DOM主要有两斚w不同。首先,JDOM仅用具体类而不使用接口。这在某些方面简化了APIQ但是也限制了灵zL。第二,API大量使用了Collectionsc,化了那些已经熟悉q些cȝJava开发者的使用?
JDOM文声明其目的是“使用20%Q或更少Q的_֊解决80%Q或更多QJava/XML问题”Q根据学习曲U假定ؓ20%Q。JDOM对于大多数Java/XML应用E序来说当然是有用的Qƈ且大多数开发者发现API比DOMҎ理解得多。JDOMq包括对E序行ؓ的相当广泛检查以防止用户做Q何在XML中无意义的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作Q或者甚至理解某些情况下的错误)。这也许是比学习DOM或JDOM接口都更有意义的工作?
JDOM自n不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文Q尽它q可以将以前构造的DOM表示作ؓ输入Q。它包含一些{换器以将JDOM表示输出成SAX2事g、DOM模型或XML文本文。JDOM是在Apache许可证变体下发布的开放源码?
4QDOM4J http://dom4j.sourceforge.net
虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一U智能分支。它合ƈ了许多超出基本XML文表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文或化文的基于事件的处理。它q提供了构徏文表示的选项Q它通过DOM4J API和标准DOM接口hq行讉K功能。从2000下半q开始,它就一直处于开发之中?
为支持所有这些功能,DOM4J使用接口和抽象基本类Ҏ。DOM4J大量使用了API中的Collectionsc,但是在许多情况下Q它q提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是Q虽然DOM4J付出了更复杂的API的代P但是它提供了比JDOM大得多的灉|性?
在添加灵zL、XPath集成和对大文处理的目标ӞDOM4J的目标与JDOM是一LQ针对Java开发者的易用性和直观操作。它q致力于成ؓ比JDOM更完整的解决ҎQ实现在本质上处理所有Java/XML问题的目标。在完成该目标时Q它比JDOM更少防止不正的应用E序行ؓ?
DOM4J是一个非帔R怼U的Java XML APIQ具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的Y件。如今你可以看到来多的Java软g都在使用DOM4J来读写XMLQ特别值得一提的是连Sun的JAXM也在用DOM4J.
2。比?
1QDOM4J性能最好,qSun的JAXM也在用DOM4J.目前许多开源项目中大量采用DOM4JQ例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文g。如果不考虑可移植性,那就采用DOM4J.
2QJDOM和DOM在性能试时表C佻I在测?0M文时内存溢出。在文情况下q值得考虑使用DOM和JDOM.虽然JDOM的开发者已l说明他们期望在正式发行版前专注性能问题Q但是从性能观点来看Q它实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现q泛应用于多U编E语a。它q是许多其它与XML相关的标准的基础Q因为它正式获得W3C推荐Q与Z非标准的Java模型相对Q,所以在某些cd的项目中可能也需要它Q如在JavaScript中用DOMQ?
3QSAX表现较好Q这要依赖于它特定的解析方式Q事仉动。一个SAX即到来的XML,但ƈ没有载入到内存(当然当XML被dӞ会有部分文暂时隐藏在内存中Q?
3. 四种xml操作方式的基本用方?
xml文gQ?
E序代码:
q行l果:
String s = "I AM an Object!";
boolean isObject = s instanceof Object;
我们声明了一个String对象引用Q指向一个String对象Q然后用instancof来测试它所指向的对象是否是Objectcȝ一个实例,昄Q这是真的,所以返回trueQ也是isObject的gؓTrue?br />
instanceof有一些用处。比如我们写了一个处理̎单的pȝQ其中有q样三个c:
public class Bill {//省略l节}
public class PhoneBill extends Bill {//省略l节}
public class GasBill extends Bill {//省略l节}
在处理程序里有一个方法,接受一个Billcd的对象,计算金额。假设两U̎单计方法不同,而传入的Bill对象可能是两U中的Q何一U,所以要用instanceof来判断:
public double calculate(Bill bill) {
if (bill instanceof PhoneBill) {
//计算电话账单
}
if (bill instanceof GasBill) {
//计算燃气账单
}
...
}
q样可以用一个方法处理两U子cR?br />
然而,q种做法通常被认为是没有好好利用面向对象中的多态性。其实上面的功能要求用方法重载完全可以实玎ͼq是面向对象变成应有的做法,避免回到l构化编E模式。只要提供两个名字和q回值都相同Q接受参数类型不同的Ҏ可以了Q?br />
public double calculate(PhoneBill bill) {
//计算电话账单
}
public double calculate(GasBill bill) {
//计算燃气账单
}
所以,使用instanceof在绝大多数情况下q不是推荐的做法Q应当好好利用多态?/div>
如果在JavaE序中你使用Java Native Interface(JNI) 来调用某个特定^C的本地库文gQ你׃发现q个q程很单调、乏呟뀂Jeff Friesen一直在介绍一个知名度很低的Java开源项目:Java Native Access---它能够避免因使用JNID的错误和乏味Q同时它q能让你通过~程的方式调用C语言库?br />
在Java语言没有提供必要的APIs的情况下QJavaE序使用Java Native Interface (JNI)来调用特定^C的本地库是必要的。例如:在Windows XPq_中,我用过JNI来调用通用串行ȝ和基于TWAIN的扫描A器的库;在更古老的Windows NTq_中,调用q智能卡的库?br />
我按照一个基本的、乏味的程来解册些问题:首先Q我创徏一个Javacȝ来蝲入JNI-friendly库(q个库能q访问其他的库)q且声明q个cȝ本地Ҏ。然后,在用JDK中的javah工具为JNI-friendly库中的函?--函数和这个类中的本地Ҏ一一对应---创徏一个代理。最后,我用C语言写了一个库q用C~译器编译了q些代码?br />
管完成q些程q不是很困难Q但是写C代码是一个很~慢的过E?--例如Q?C语言中的字符串处理是通过指针来实现的Q这会很复杂的。而且Q用JNI很容易出现错误,D内存泄漏、很难找到程序崩溃的原因?br />
?a target="_new">Java开源系?/a>的第二篇文章中,我要介绍一个更单、更安全的解x法:Todd Fast and Timothy Wall?a target="_new">Java Native Access (JNA) 目。JNA能够让你在JavaE序中调用本地方法时避免使用C和Java Native Interface。在q篇文章中,让我以简要的介绍 JNA和运行示例必需的Y件来开始下面的内容。然后,向你展示如何使用JNA?个Windows本地库中的有用代码移植到JavaE序中?br />
Get started with JNAQJNA入门Q?/span>
Java Native Access 目 在Java.net上,你可以到q个|站上现在这个项目的代码和在U帮助文。虽然在下蝲?个相关的jar文gQ在本文中你仅仅需要下载其中的jna.jar和example.jar?br />
Jna.jar提供基本的、运行这些示例文件必需的jnaq行环境。这个jna.jar文g除了有Unix、Linux、Windows和Mac OS Xq_相关的JNT-friendly本地库外Q还包含其他几个cd。每一个本地库都是用来讉K相对应^C的本地方法的?br />
example.jar包含了不同的CZ来表明JNA的用途。其中的一个例子是使用JNA来实C个在不同q_下的透明视窗技术的API。在文章最后的CZ中将要展C如何用这个API修复上个月的文章关于VerifyAge2应用中L认透明效果的问题?br />
获取本地旉QGet local timeQ?/span>
如果你在Java Native Access 首页 看过“JNA如何入门”Q你׃知道一个很单的关于调用Windows q_下的API函数QGetSystemTimeQ) 的JNACZ。这个不完整的例子只是展CZJNA的基本特炏V(在例子的基础上,我做了一个更完整的基于Windows的例子来介绍JNAQ我在Windowsq_下完善了q个例子来介lJNA?br />
W一例子ZWindows GetLocalTime() API函数q回本地当前的时间和日期。和GetSystemTime()不同的是Q返回的旉/日期?a target="_new">协调通用旉(UTC)格式的,GetLocalTime()q回的时?日期信息的格式是Ҏ当前时区来表C?br />
在一个JavaE序中用JNA调用GetLocalTimeQ你需要知道这个函数所在的Windowsq_下的动态链接库QDLLQ的名称Q和可能所在的地理区域Q。我们发现GetLocalTime()和GetSystemTime在同一个DLL文g中:kernel32.dll。你q需要知道GetLocalTime()在C语言环境中的x。申明如下Listing 1Q?br />
Listing 1. GetLocalTime在C语言中的x
typedef struct
{
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
}
SYSTEMTIME, *LPSYSTEMTIME;
VOID GetLocalTime(LPSYSTEMTIME lpst);
q个ZC语言的申明表明传到这个函数的参数数目和类型。在q个例子中,只有一个参?--一个指向Windows SYSTEMTIMEl构体的指针。而且Q每个结构体成员的类型是16bit长度的无W号整型。根据这些信息,你能够创Z个完全描qGetLocalTime()函数的接口,如Listing 2中所C:
Listing 2. Kernel32.java
// Kernel32.java
import com.sun.jna.*;
import com.sun.jna.win32.*;
public interface Kernel32 extends StdCallLibrary
{
public static class SYSTEMTIME extends Structure
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}
void GetLocalTime (SYSTEMTIME result);
}
Kernel32 接口QThe Kernel32 interfaceQ?/span>
因ؓJNA使用通过一个接口来讉K某个库中的函敎ͼListing 2表示了一个描qGetLocalTime()的接口。根据约定,我把接口命名为Kernel32是因为GetLocalTime()在Windows的kernel32.dll库?br />
q个接口必须l承com.sun..jna.Library接口。因为Windows API函数遵@stdcall调用协议Qstdcall calling conventionQ,为Windows APIx的接口也必须l承com.sun.jna.win32. StdCallLibrary接口。因此这个接口共l承了Library ?com.sun.jna.win32.StdCall两个接口?br />
在前面,你已l知道了GetLocalTime() 需要一个指向SYSTEMTIMEl构体的指针作ؓ它唯一的参数。因为Java不支持指针,JNA是通过x一个com.sun.jna.Structure的子cL代替的。根据java文中抽象类的概念,在参数环境中QStructure相当于C语言的struct*?br />
在SYSTEMTIMEcM的字D和Cl构体中的相对应的属性字D늚序是一一对应的。保证字D顺序的一致性是非常重要的。例如,我发C换wYear和wMonth会导致wYear和wMonthg换?br />
每个字段在java中是short integercd的。按照JNA首页?“默认cd映射”章节l出的提C,q个short integer分配cd是正。然而,我们应该知道一个重要的区别QWindowsq_下的WORDcd{同于C语言环境中的16-bit的无W号的short integerQ而java中short integer?6-bit有符Lshort integer?br />
一个类型映的问题
通过比较一个API 函数q回的整型|你会发现Windows/C 语言的无W号整型和Java语言的有W号整型的JNAcd映射是有问题的。在比较的过E中Q如果你不细心,那么错误的执行过E可能导致决定性情c导致这U后果是因ؓ忘记M数值的W号位的定是根据:在无W号整型的情况下会被解释为正P而在有符h型的q制中被理解L?br />
通过Kernel32获取本地旉QAccess the local time with Kernel32Q?/span>
JNA首页上的GetSystemTime()CZ已经表明必须使用预先x的接口ؓ本地库分配一个实例对象。你可以通过com.sun.jna.NativecM静态公用方法loadLibrary(String name, Class interfaceClass)来完成上q的目标。Listing 3 所C:
Listing 3. LocalTime.java
// LocalTime.java
import com.sun.jna.*;
public class LocalTime
{
public static void main (String [] args)
{
Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32",
Kernel32.class);
Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();
lib.GetLocalTime (time);
System.out.println ("Year is "+time.wYear);
System.out.println ("Month is "+time.wMonth);
System.out.println ("Day of Week is "+time.wDayOfWeek);
System.out.println ("Day is "+time.wDay);
System.out.println ("Hour is "+time.wHour);
System.out.println ("Minute is "+time.wMinute);
System.out.println ("Second is "+time.wSecond);
System.out.println ("Milliseconds are "+time.wMilliseconds);
}
}
Listing 3 执行Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32", Kernel32.class);来分配一个Kernel32实例对象q且装蝲kernel32.dll。因为kernel32.dll是Windowsq_下标准的dll文gQ所以不要指定访问这个库的\径。然而,如果找不到这个dll文gQloadLibrary()会抛Z个UnsatisfiedLinkError异常?br />
Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();创徏了一个SYSTEMTIMEl构体的CZ。初始化后下面是lib.GetLocalTime (time);Q这句话使用本地的时?日期来给q个实例赋倹{几个System.out.println()语句是输些倹{?br />
~译和运行这个应用(Compile and run the applicationQ?/span>
q部分很Ҏ。假设jna.jar、Kernel32.java和LocalTime.java是放在当前文件夹中,调用java –cp jna.jar;. LocalTime.java来编译这个应用的源代码。如果在Windowsq_下,调用invoke java –cp jna.jar;. LocalTime 来运行这个应用。你可以得到cM与Listing 4的输出结果:
Listing 4. 从LocalTime.java生成的输?/span>
Year is 2007
Month is 12
Day of Week is 3
Day is 19
Hour is 12
Minute is 35
Second is 13
Milliseconds are 156
获取操纵杆信息(Accessing joystick device infoQ?/span>
上面的例子已l介l了JNAQ但是这个获取本地时间和日期的例子ƈ没有很好的利用这个技术,甚至也没有体现JNI的h倹{Java语言中的System.currentTimeMillis()函数已经以毫U的格式q回了这些信息。因为Java语言没有为游戏控制器提供APIQ所以获取操U|的信息更适合JNA的用?br />
例如Q你要构Z个^台无关的Java库,而且q些库用JNA调用Linux, Mac OS X, Windwos和Unixq_中本地的操纵杆API。ؓ了简z和方便赯Q这个例子仅仅是调用Windowsq_下的操纵杆API。而且我将重点介绍q个API很小的一部分?br />
cMGetLocalTime()Q第一步是辨别出操作杆API的DLLQ这个DLL是winmm.dllQ和kernel32.dll在同一个文件夹中,它包含了操作杆的API和其他的多媒体APIs。还需知道要被使用的操作杆函数ZC语言的声明。这些函数声明已l在Listing 5中列出来了?br />
Listing 5. C-based declarations for some Joystick API functions
#define MAXPNAMELEN 32
typedef struct
{
WORD wMid; // manufacturer identifier
WORD wPid; // product identifier
TCHAR szPname MAXPNAMELEN ; // product name
UINT wXmin; // minimum x position
UINT wXmax; // maximum x position
UINT wYmin; // minimum y position
UINT wYmax; // maximum y position
UINT wZmin; // minimum z position
UINT wZmax; // maximum z position
UINT wNumButtons; // number of buttons
UINT wPeriodMin; // smallest supported polling interval when captured
UINT wPeriodMax; // largest supported polling interval when captured
}
JOYCAPS, *LPJOYCAPS;
MMRESULT joyGetDevCaps(UINT IDDevice, LPJOYCAPS lpjc, UINT cbjc);
UINT joyGetNumDevs(VOID);
操作杆API的函敎ͼFunctions of the Joystick APIQ?/span>
在Windowsq_下是通过以joy作ؓ函数名开始的函数以及被各U函数调用的l构体来实现操作杆API的。例如,joyGetNumDevs()q回的是q个q_下支持的操作杆设备最多的数目QjoyGetDevCaps()q回的是每个q接上的操纵杆的质量?br />
joyGetDevCaps()函数需?个参敎ͼ
* 处在0到joyGetNumDevs()-1之间的操作杆ID
* 保存q回的质量信息的JOYCAPSl构体的地址
* JOYCAPSl构体的字节大小
虽然它的l果不同Q这个函数返回的是一?2位的无符h型结果,而且0表示一个已l连接的操纵杆?br />
JOYCAPSl构体有3U类型。Windowsq_下的WORDQ?6位无W号短整型)cd对应的是Java语言?6位有W号短整型。除此之外,Windows下的UINTQ?2位无W号整型Q类型是和Java语言?2位有W号整型相对应的。而Windowsq_上的text character是TCHARcd?br />
微Y通过TCHARcd使开发h员能够从ASCIIcd的函数参数^滑的转移到Unicode字符cd的函数参C。而且Q拥有textcd参数的函数的实现是通过宏{变ؓ对应的ASCII或者wide-character的函数。例如,joyGetDevCaps()是一个对应joyGetDevCapsA() ?joyGetDevCapsW()的宏?br />
使用TCHARQWorking with TCHARQ?/span>
使用TCHAR和将TCHAR转变的宏会导致基于C语言的申明向ZJNA接口的{?br />
变得有点复杂—你在用ASCII或者wide-character版本的操U|函数吗?两种版本都在如下的接口中展示了:
Listing 6. WinMM.java
// WinMM.java
import com.sun.jna.*;
import com.sun.jna.win32.*;
public interface WinMM extends StdCallLibrary
{
final static int JOYCAPSA_SIZE = 72;
public static class JOYCAPSA extends Structure
{
public short wMid;
public short wPid;
public byte szPname [] = new byte [32];
public int wXmin;
public int wXmax;
public int wYmin;
public int wYmax;
public int wZmin;
public int wZmax;
public int wNumButtons;
public int wPeriodMin;
public int wPeriodMax;
}
int joyGetDevCapsA (int id, JOYCAPSA caps, int size);
final static int JOYCAPSW_SIZE = 104;
public static class JOYCAPSW extends Structure
{
public short wMid;
public short wPid;
public char szPname [] = new char [32];
public int wXmin;
public int wXmax;
public int wYmin;
public int wYmax;
public int wZmin;
public int wZmax;
public int wNumButtons;
public int wPeriodMin;
public int wPeriodMax;
}
int joyGetDevCapsW (int id, JOYCAPSW caps, int size);
int joyGetNumDevs ();
}
Listing 6没有介绍JNA的新Ҏ。实际上QJNA了对本地库的接口命名规则。同Ӟq展CZ如何TCHAR映射到Java语言中的byte和char数组。最后,它揭CZ以常量方式声明的l构体的大小。Listing 7展示了当调用joyGetDevCapsA() ?joyGetDevCapsW()时如何用这些常量?br />
Listing 7. JoystickInfo.java
// JoystickInfo.java
import com.sun.jna.*;
public class JoystickInfo
{
public static void main (String [] args)
{
WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);
int numDev = lib.joyGetNumDevs ();
System.out.println ("joyGetDevCapsA() Demo");
System.out.println ("---------------------\n");
WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA ();
for (int i = 0; i < numDev; i++)
if (lib.joyGetDevCapsA (i, caps1, WinMM.JOYCAPSA_SIZE) == 0)
{
String pname = new String (caps1.szPname);
pname = pname.substring (0, pname.indexOf ('\0'));
System.out.println ("Device #"+i);
System.out.println (" wMid = "+caps1.wMid);
System.out.println (" wPid = "+caps1.wPid);
System.out.println (" szPname = "+pname);
System.out.println (" wXmin = "+caps1.wXmin);
System.out.println (" wXmax = "+caps1.wXmax);
System.out.println (" wYmin = "+caps1.wYmin);
System.out.println (" wYmax = "+caps1.wYmax);
System.out.println (" wZmin = "+caps1.wZmin);
System.out.println (" wZmax = "+caps1.wZmax);
System.out.println (" wNumButtons = "+caps1.wNumButtons);
System.out.println (" wPeriodMin = "+caps1.wPeriodMin);
System.out.println (" wPeriodMax = "+caps1.wPeriodMax);
System.out.println ();
}
System.out.println ("joyGetDevCapsW() Demo");
System.out.println ("---------------------\n");
WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();
for (int i = 0; i < numDev; i++)
if (lib.joyGetDevCapsW (i, caps2, WinMM.JOYCAPSW_SIZE) == 0)
{
String pname = new String (caps2.szPname);
pname = pname.substring (0, pname.indexOf ('\0'));
System.out.println ("Device #"+i);
System.out.println (" wMid = "+caps2.wMid);
System.out.println (" wPid = "+caps2.wPid);
System.out.println (" szPname = "+pname);
System.out.println (" wXmin = "+caps2.wXmin);
System.out.println (" wXmax = "+caps2.wXmax);
System.out.println (" wYmin = "+caps2.wYmin);
System.out.println (" wYmax = "+caps2.wYmax);
System.out.println (" wZmin = "+caps2.wZmin);
System.out.println (" wZmax = "+caps2.wZmax);
System.out.println (" wNumButtons = "+caps2.wNumButtons);
System.out.println (" wPeriodMin = "+caps2.wPeriodMin);
System.out.println (" wPeriodMax = "+caps2.wPeriodMax);
System.out.println ();
}
}
}
管和LocalTimeq个CZcMQJoystickInfo执行WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);q句话来获取一个WinMM的实例,q且载入winmm.dll。它q执行WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA (); ?WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();初始化必需的结构体实例?br />
~译和运行这个程序(Compile and run the applicationQ?/span>
假如jna.jarQWinMM.java和JoystickInfo.java在同一个文件夹中,调用 javac -cp jna.jar;. JoystickInfo.java 来编译这个应用的源代码?br />
在windowsq_下,调用java -cp jna.jar;. JoystickInfo可以运行这个应用程序了。如果没有操U|讑֤Q你应该得到Listing 8中的输出?br />
C语言中的stringcd转换为Java语言的Stringcd
pname = pname.substring (0, pname.indexOf ('\0')); q段代码一个C string 转换成了Java string. 如果不用这个{换,C语言的stringl束W?#8217;\0’和string后面的无用字W都会成为Java语言中String实例对象的内宏V?
Listing 8. 输出操纵杆信息(Output of JoystickInfoQ?/span>
joyGetDevCapsA() Demo
---------------------
joyGetDevCapsW() Demo
---------------------
上面的输出是因ؓ每次调用joyGetDevCap()q回的是一个非I|q表C没有操U|/游戏控制器设备或者是出现错误。ؓ了获取更多有意思的输出Q将一个设备连接到你的q_上ƈ且再ơ运行JoystickInfo。如下,一个微软SideWinderx即用游戏触摸板设备联上之后我获取了如下的输出Q?br />
Listing 9. 操纵杆连接上之后的运行结果(Output after running JoystickInfo with a joystick attachedQ?/span>
joyGetDevCapsA() Demo
---------------------
Device #0
wMid = 1118
wPid = 39
szPname = Microsoft PC-joystick driver
wXmin = 0
wXmax = 65535
wYmin = 0
wYmax = 65535
wZmin = 0
wZmax = 65535
wNumButtons = 6
wPeriodMin = 10
wPeriodMax = 1000
joyGetDevCapsW() Demo
---------------------
Device #0
wMid = 1118
wPid = 39
szPname = Microsoft PC-joystick driver
wXmin = 0
wXmax = 65535
wYmin = 0
wYmax = 65535
wZmin = 0
wZmax = 65535
wNumButtons = 6
wPeriodMin = 10
wPeriodMax = 1000
H口透明度(Transparent windowsQ?/span>
在这pd文章中上文章是关于Bernhard Pauler's 气提示QballoontipQ工E的。我构徏了一个叫做VerifyAge的、包含有一个气泡提C的GUI应用。Figure 1中显CZq个GUI应用的一个小问题Q这个气泡提C的没有l过修饰的对话框部分遮住了应用窗口的一部分Ҏ,D了无法点击这个边框的最化和最大化按钮Qƈ且整个GUI很难?
管未修饰部分的对话框不能显C气泡提C的透明度,java语言不支持窗口透明度。幸q的是,我们可以通过使用com.sun.jna.examples.WindowUtilsc调用JNA的examples.jar文g来解册个问题?
WindowUtils提供在UnixQLinuxQMac OS X和Windowsq_上用JNA’s来实现窗口透明的工h法。例如, public static void setWindowMask(final Window w, Icon mask) 让你Ҏ像素而不是通过预定的掩|(maskQ参数来选取某部分的H口。这个功能将在Listing 10中展C:
Listing 10. Using JNA to render a window transparent
// Create a mask for this dialog. This mask has the same shape as the
// dialog's rounded balloon tip and ensures that only the balloon tip
// part of the dialog will be visible. All other dialog pixels will
// disappear because they correspond to transparent mask pixels.
// Note: The drawing code is based on the drawing code in
// RoundedBalloonBorder.
Rectangle bounds = getBounds ();
BufferedImage bi = new BufferedImage (bounds.width, bounds.height,
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics ();
g.fillRoundRect (0, 0, bounds.width, bounds.height-VERT_OFFSET,
ARC_WIDTH*2, ARC_HEIGHT*2);
g.drawRoundRect (0, 0, bounds.width-1, bounds.height-VERT_OFFSET-1,
ARC_WIDTH*2, ARC_HEIGHT*2);
int [] xPoints = { HORZ_OFFSET, HORZ_OFFSET+VERT_OFFSET, HORZ_OFFSET };
int [] yPoints = { bounds.height-VERT_OFFSET-1, bounds.height-VERT_OFFSET
-1, bounds.height-1 };
g.fillPolygon (xPoints, yPoints, 3);
g.drawLine (xPoints [0], yPoints [0], xPoints [2], yPoints [2]);
g.drawLine (xPoints [1], yPoints [1], xPoints [2], yPoints [2]);
g.dispose ();
WindowUtils.setWindowMask (this, new ImageIcon (bi));
在Listing 10中的代码D|从本文代码文(code archiveQ里的加强版的VerifyAge2 应用中的TipFrame的构造函数结N分摘录的。这个构造函数定义了围绕提示气的掩|(maskQ的形状Q在q个形状范围里描l不透明的像素?br />
假如你当前文件夹中有examples.jar, jna.jar, ?VerifyAge2.java,调用 javac -cp examples.jar;balloontip.jar VerifyAge2.java 来编译源文g.然后调用java -Dsun.java2d.noddraw=true -cp examples.jar;balloontip.jar;. VerifyAge2q行q个应用. Figure 2 展示了透明CZ.
ȝQIn conclusionQ?/span>
JNA目有很长的历史了(q溯?999q_Q但是它W一ơ发布是?006q?1月。从此以后它慢慢的被需要将本地C代码整合到Java工程中的开发者注意到了。因为JNA能够用来解决JuRuby中常见一个问题:~Z对POSIX调用的支持(lack of support for POSIX callsQ,它也在JRubyE序员中掀起些波浪。JNA也同栯作ؓ实现用低UC代码l承Ruby的一U解x案(extending Ruby with low-level C codeQ?br />
我喜Ƣ用JNA来工作,怿你也会发现它比用JNI来访问本C码更单、更安全。无需多言QJNAq有更多的特性在本文中没有体现出来。查阅它的资源部分:获取q个开源java目更多的信息(learn more about this open source Java projectQ。用它做demoQ而且在论坛(discussion forum Q上׃n你的l验?下一个月我会带着另一个开源项目回来的Q这个开源项目会l你每天的java开发带来益处?br />
附录QWindowUtils.setWindowMask()的替代品
在刚刚写完这文章后Q我发现java语言支持?u10版本中支持窗口的透明和Ş状定制。读完Kirill Grouchnikov的博客后Q我用WindowUtils.setWindowMask()的替代品修改了VerifyAge2Q如下:
// Create and install a balloon tip shape to ensure that only this part
// of the dialog will be visible.
Rectangle bounds = getBounds ();
GeneralPath gp;
gp = new GeneralPath (new RoundRectangle2D.Double (bounds.x, bounds.y,
bounds.width,
bounds.height-
VERT_OFFSET,
ARC_WIDTH*2-1,
ARC_HEIGHT*2-1));
gp.moveTo (HORZ_OFFSET, bounds.height-VERT_OFFSET);
gp.lineTo (HORZ_OFFSET, bounds.height);
gp.lineTo (HORZ_OFFSET+VERT_OFFSET+1, bounds.height-VERT_OFFSET);
AWTUtilities.setWindowShape (this, gp);
q段代码使用新类AWTUtilitiesQ在com.sun.awt包中Q,而且public void setWindowShape(Window w, Shape s)函数TipFrame和JDialogH口讄气形状?br />
清单 1. 一l属性示?
foo=bar
fu=baz
清?1 装蝲?Properties 对象中后Q您可以找C个键Q?foo ?fu Q和两个| foo ?bar ?fu ?baz Q了。这个类支持?\u 的嵌?Unicode 字符Ԍ但是q里重要的是每一内定w当作 String ?
清单 2 昄了如何装载属性文件ƈ列出它当前的一l键和倹{只需传递这个文件的 InputStream l?load() ҎQ就会将每一个键-值对d?Properties 实例中。然后用 list() 列出所有属性或者用 getProperty() 获取单独的属性?
清单 2. 装蝲属?
q行 LoadSample E序生成如清?3 所C的输出。注?list() Ҏ的输Z?值对的顺序与它们在输入文件中的顺序不一栗?Properties cd一个散列表QhashtableQ事实上是一?Hashtable 子类Q中储存一l键-值对Q所以不能保证顺序?
清单 3. LoadSample 的输?
-- listing properties --
fu=baz
foo=bar
The foo property: bar
XML 属性文?
q里没有什么新内容?Properties cLLq样工作的。不q,新的地方是从一?XML 文g中装载一l属性。它?DTD 如清?4 所C?
清单 4. 属?DTD
dtd 写道
如果不想l读 XML DTDQ那么可以告诉您它其实就是说在外?<properties> 标签中包装的是一?<comment> 标签Q后面是L数量?<entry> 标签。对每一?<entry> 标签Q有一个键属性,输入的内容就是它的倹{清?5 昄?清单 1中的属性文件的 XML 版本是什么样子的?
清单 5. XML 版本的属性文?
如果清单 6 所C,d XML 版本?Properties 文g与读取老格式的文g没什么不同?
清单 6. d XML Properties 文g
关于资源l定的说?
虽然 java.util.Properties cȝ在除了支持键-值对Q还支持属性文件作?XML 文gQ不q的是,没有内置的选项可以?ResourceBundle 作ؓ一?XML 文g处理。是的, PropertyResourceBundle 不?Properties 对象来装载绑定,不过装蝲Ҏ的用是编码到cM的,而不使用较新?loadFromXML() Ҏ?
q行清单 6 中的E序产生与原来的E序相同的输出,?清单 2所C?
保存 XML 属?
新的 Properties q有一个功能是属性存储到 XML 格式的文件中。虽?store() Ҏ仍然会创Z个类?清单 1 所C的文gQ但是现在可以用新的 storeToXML() Ҏ创徏?清单 5 所C的文g。只要传递一?OutputStream 和一个用于注释的 String 可以了。清?7 展示了新?storeToXML() Ҏ?
清单 7. ?Properties 存储?XML 文g
q行清单 7 中的E序产生的输出如清单 8 所C?
清单 8. 存储?XML 文g
l束?
使用 XML 文gq是使用老式?a=b cd的文件完全取决于您自己。老式文g从内存的角度看肯定是轻量U的。不q,׃ XML 的普遍用,Z会期?XML 格式行hQ因为它已经被广泛用了Q只不过没有用到 Properties 对象。选择完全在您。分析Y件包 private XMLUtils cȝ源代码以获得关于所使用?XML 解析的更多信息?
conf.properties
Result
SIZE:4
author : bbflyerwww
school : WuHan University
date : 2006-08-02
Cost : 0
(x.equals(y) == true)那这个的话就是去比较它们所对应的整敎ͼ
不是。有一个equals()函数Q和一个hashcode()函数
System.out.println(a==c);t
System.out.println(b==c);f
System.out.println(a.equals(b));
输出l果是什么?
Z么?
if((a=3)>0|(b=3)>0){}
if((a=3)>0||(b=3)>0){}分别说出a,b的?/p>
2.调用内部cȝ构造函敎ͼouterObject.new InnerClass(construction parameters);
3.外部cd面引用内部类QOuterClass.InnerClass
{
public class Inner
{
}
}
当内部类是非静态内部类时相应的内部cȝ详细源码如下Q?br />
Compiled from "Outer.java"
public class Outer$Inner extends java.lang.Object{
final Outer this$0; //~译器自动在内部c里面添加了指向外部cd象的引用
public Outer$Inner(Outer); //内部cȝ构造函数默认有一个外部类对象作ؓ参数?br />
}
Compiled from "Outer.java"
public class Outer$Inner extends java.lang.Object{
public Outer$Inner(); //没有了对外部cd象的引用
}
如下代码模拟了上面内部类的情形唯一不同的是q里的Inner没有讉KOuterU有数据的权限:
class Outer{
Inner in = new Inner(this);
}
public Inner(Outer outer){
this.outer = outer;
}
private Outer outer;
}
public class Outer
{
private int i;
public void methodOne()
{
}
{
public void print(){
System.out.println(Outer.this.i);
}
}
}
{
public Outer();
static int access$000(Outer); //q译器合成的用于内部类对外部类q行Ҏ讉K权限的控Ӟq也?br />
//Z么内部类能够讉K外部cM的私有数据的原因?br />
private int i;
}
匿名内部c:用外部类+$+数字的Ş式表C。一个外部类中有多个匿名内部cLQ其生成的class文g对应有Outer$(1??)的Ş式表C。注意到该匿名内部类是final的?br />
final class Outer$1
{
Outer$1(Outer);
}
多数设计模式的内容讲解的都是如何设计接口?
接口如何产生呢?如果在客户代码(cd的用者称之ؓ客户Q中直接使用具体c,那么失M接口的意义。因为接口的使用目的Q就是要降低客户对具体类的依赖程度。如果在客户代码中直接用接口,那么造成了客户对具体cdU的依赖。(客户最l需要以某种方式指明所需要的具体c,如配|文件或代码Q但是只需要指Zơ,所以说降低对具体类的依赖程度)。要使客户代码不依赖具体c,唯一的方法,是让客户代码不依赖具体cȝ部分不知道具体类的名U。知道具体类名称的部分,仅仅是配|部分。(配置文g或者配|代码)?
依赖不可能完全消除,除非二者毫无联pR但是可以将q种依赖的程度降到最低?
既然不能直接创徏具体c,那么需要通过一个创类来创建接口的实现cR这样就产生了工厂类?
那么现在已经知道工厂cd在的理由Q抽象创建接口的q程?/strong>
q样Q就可以使用单工厂?
单工厂,一般是两l构。工厂类创徏接口?
随着接口创徏复杂性的增强Q可能在接口创徏的过E中Q一个创类Q无法承担创建所有的接口cȝ职责?
可能会有q样的情况,我们定义了一个接口,?个实现类分别?23456受但是,q六个实现类不可能用一个工厂创建出来,因ؓ123h windows下的实现Q?56hlinux上的实现。(假设我们使用的语a不是q大人民众热爱的java语言Q,那么q个时候,我还需要客h用相同的方式来创个借口Q而不是在代码中到处写
public Client {
事g监听机制在java~程中有很重要的应用Q一般我们在处理GUI~程Ӟ只是重写一下监听接口的perform函数卛_。但事g监听在底层是如何q行的?通过下面的例子我们可以有个清楚地了解?br />
1Q? 首先写一个事件类 |
tomcat5下jsp出现getOutputStream() has already been called for this response异常的原因和解决Ҏ
在tomcat5下jsp中出现此错误一般都是在jsp中用了输出(如输出图片验证码Q文件下载等Q,
没有妥善处理好的原因?br />
具体的原因就?br />
在tomcat中jsp~译成servlet之后在函数_jspService(HttpServletRequest request, HttpServletResponse response)的最?br />
有一D这L代码
finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
q里是在释放在jsp中用的对象Q会调用response.getWriter(),因ؓq个Ҏ是和
response.getOutputStream()相冲H的Q所以会出现以上q个异常?/p>
然后当然是要提出解决的办法,其实挺简单的Qƈ不是和某些朋友说的那?-
jsp内的所有空格和回RW号所有都删除掉)Q?/p>
在用完输出以后调用以下两行代码即可:
out.clear();
out = pageContext.pushBody();
最后这里是一个输出彩色验证码例子Q这L例子几乎随处可见Q?br /> imag.jsp
<%@ page import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
<%@ page import="java.io.OutputStream" %>
<%!
Color getRandColor(int fc,int bc){
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
%>
<%
try{
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
OutputStream os=response.getOutputStream();
Graphics g = image.getGraphics();
Random random = new Random();
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x,y,x+xl,y+yl);
}
String sRand="";
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10));
sRand+=rand;
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
g.drawString(rand,13*i+6,16);
}
session.setAttribute("rand",sRand);
g.dispose();
ImageIO.write(image, "JPEG",os);
os.flush();
os.close();
os=null;
response.flushBuffer();
out.clear();
out = pageContext.pushBody();
}
catch(IllegalStateException e)
{
System.out.println(e.getMessage());
e.printStackTrace();
}%>
所谓回调,是客户E序C调用服务E序S中的某个函数AQ然后S又在某个时候反q来调用C中的某个函数BQ对于C来说Q这个B便叫做回调函数?/p>
例:
1.class A,class B
2.class A实现接口operate
3.class B拥有一个参Cؓoperate接口cd的函数test(operate o)
4.class Aq行时调用class B中test函数,以自w传入参?nbsp;
5.class B已取得AQ就可以随时回调A所实现的operate接口中的Ҏ
public interface InterestingEvent
{
// This is just a regular method so it can return something or
// take arguments if you like.
public void interestingEvent ();
}
public class EventNotifier
{
private InterestingEvent ie;
private boolean somethingHappened;
public EventNotifier (InterestingEvent event)
{
// Save the event object for later use.
ie = event;
// Nothing to report yet.
somethingHappened = false;
}
//
public void doWork ()
{
// Check the predicate, which is set elsewhere.
if (somethingHappened)
{
// Signal the even by invoking the interface's method.
ie.interestingEvent ();
}
//
}
//
}
public class CallMe implements InterestingEvent
{
private EventNotifier en;
public CallMe ()
{
// Create the event notifier and pass ourself to it.
en = new EventNotifier (this);
}
// Define the actual handler for the event.
public void interestingEvent ()
{
// Wow! Something really interesting must have occurred!
// Do something
}
//
}
1 使用Graphics.drawImage(Image img, int x, int y, ImageObserver observer)
Ҏ昄囑փ?/p>
2 使用Component.getToolkit.getImage(String path)语句获得Image实例对象?/p>
例:
package drawimage;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class DrawImage extends Frame
{
private static final long serialVersionUID = 1L;
public DrawImage()
{
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
dispose();
System.exit(0);
}
});
}
public static void main(String[] args)
{
System.out.println("Starting DrawImage.");
DrawImage mainFrame = new DrawImage();
Image img = mainFrame.getToolkit().getImage("love.jpg");
mainFrame.setSize(400,400);
mainFrame.setTitle("DrawImage");
mainFrame.setVisible(true);
Graphics g = mainFrame.getGraphics();
while(!g.drawImage(img,150,150,mainFrame));
}
}
囑փ重画
package drawimage;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class DrawImage extends Frame
{
Image img = getToolkit().getImage("love.jpg");
private static final long serialVersionUID = 1L;
public void paint(Graphics g)
{
g.drawImage(img,0,0,this);
}
public DrawImage()
{
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
dispose();
System.exit(0);
}
});
}
public static void main(String[] args)
{
System.out.println("Starting DrawImage.");
DrawImage mainFrame = new DrawImage();
mainFrame.setSize(400,400);
mainFrame.setTitle("DrawImage");
mainFrame.setVisible(true);
/*Graphics g = mainFrame.getGraphics();
while(!g.drawImage(img,150,150,mainFrame));
*/
}
}
双缓冲技术:
1 Component.createImageҎ创徏内存Image对象
2 在Image对象上进行绘制的l果成了一q图?br />
3 在Image对象上执行与lg表面同样的绘ӞImage
对象中的囑փ是lg表面内容的复Ӟ当组仉?nbsp; Ӟ只需要将内存中的Image对象在组件上dQ?/p>
package drawline;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
//import java.util.Enumeration;
import java.util.Vector;
public class DrawLine1 extends Frame
{
int orgX;
int orgY;
int endX;
int endY;
Image oimg = null;
Graphics og = null;
Vector<MyLine> vLines = new Vector<MyLine>();
public void paint(Graphics g)
{
if(oimg != null)
{
g.drawImage(oimg,0,0,this);
}
/*g.setColor(Color.RED);
g.setFont(new Font(null,Font.ITALIC|Font.BOLD,30));
Enumeration<MyLine> e = vLines.elements();
while(e.hasMoreElements())
{
MyLine line = (MyLine)e.nextElement();
line.drawMe(g);
}*/
//g.drawLine(orgX, orgY, endX, endY);
}
private static final long serialVersionUID = 9004940250333651314L;
public DrawLine1()
{
setSize(400,400);
setTitle("Drawline");
setVisible(true);
Dimension d = getSize();
oimg = createImage(d.width,d.height);
og = oimg.getGraphics();
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
this.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
orgX = e.getX();
orgY = e.getY();
}
public void mouseReleased(MouseEvent e)
{
endX = e.getX();
endY = e.getY();
Graphics g = getGraphics();
g.setColor(Color.RED);
g.setFont(new Font(null,Font.ITALIC|Font.BOLD,30));
g.drawString(orgX + "," + orgY,orgX,orgY);
g.drawString(e.getX() + "," + e.getY(), e.getX(),e.getY());
g.drawLine(orgX, orgY, e.getX(),e.getY());
og.setColor(Color.RED);
og.setFont(new Font(null,Font.ITALIC|Font.BOLD,30));
og.drawString(orgX + "," + orgY,orgX,orgY);
og.drawString(e.getX() + "," + e.getY(), e.getX(),e.getY());
og.drawLine(orgX, orgY, e.getX(),e.getY());
/*vLines.add(new MyLine(orgX,orgY,endX,endY));
repaint();*/
}//Z么用getGraphics().setColor(Color.RED)是不用导java.awt.Graphics?br />
//Enumeration<MyLine>???????????????????????????
});
}
public static void main( String[] args) {
System.out.println("String DrawLine");
new DrawLine1();
}
}
//构徏L架对象时是先调用paintQ)q是构造函?/p>
Graphics.drawLine(int x1,int y1,int x2, int y2)ҎQ?br />
Graphics.drawStringString strQint xQint yQ方法;
Graphics.drawStingҎ的坐标参数是以坐下方为参考点的;
例:
package drawline;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class DrawLine extends Frame {
private static final long serialVersionUID = 9004940250333651314L;
public DrawLine()
{
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
this.addMouseListener(new MouseAdapter()
{
int orgX;
int orgY;
public void mousePressed(MouseEvent e)
{
orgX = e.getX();
orgY = e.getY();
}
public void mouseReleased(MouseEvent e)
{
Graphics g = getGraphics();
g.setColor(Color.RED);
g.setFont(new Font(null,Font.ITALIC|Font.BOLD,30));
g.drawString(orgX + "," + orgY,orgX,orgY);
g.drawString(e.getX() + "," + e.getY(), e.getX(),e.getY());
g.drawLine(orgX, orgY, e.getX(),e.getY());
}//Z么用getGraphics().setColor(Color.RED)是不用导java.awt.Graphics?br />
});
}
public static void main( String[] args) {
System.out.println("String DrawLine");
DrawLine mainFrame = new DrawLine();
mainFrame.setSize(400,400);
mainFrame.setTitle("Drawline");
mainFrame.setVisible(true);
}
}
lg重徏的处理:
1 lg重绘的原?nbsp; 曝光
2 paint(Graphics g)的作?/p>
AWTlg重绘路线Q?br />
AWT Thread ->paint() ->AWT Thread
repaint()+AWT Thread ->update(清除lg表面内容调用paint()ҎQ?>paint();
例:
package drawline;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Enumeration;
import java.util.Vector;
public class DrawLine extends Frame
{
int orgX;
int orgY;
int endX;
int endY;
Vector<MyLine> vLines = new Vector<MyLine>();
public void paint(Graphics g)
{
g.setColor(Color.RED);
g.setFont(new Font(null,Font.ITALIC|Font.BOLD,30));
Enumeration<MyLine> e = vLines.elements();
while(e.hasMoreElements())
{
MyLine line = (MyLine)e.nextElement();
line.drawMe(g);
}
//g.drawLine(orgX, orgY, endX, endY);
}
private static final long serialVersionUID = 9004940250333651314L;
public DrawLine()
{
this.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
this.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
orgX = e.getX();
orgY = e.getY();
}
public void mouseReleased(MouseEvent e)
{
endX = e.getX();
endY = e.getY();
/*Graphics g = getGraphics();
g.setColor(Color.RED);
g.setFont(new Font(null,Font.ITALIC|Font.BOLD,30));
g.drawString(orgX + "," + orgY,orgX,orgY);
g.drawString(e.getX() + "," + e.getY(), e.getX(),e.getY());
g.drawLine(orgX, orgY, e.getX(),e.getY());
*/
vLines.add(new MyLine(orgX,orgY,endX,endY));
repaint();
}//Z么用getGraphics().setColor(Color.RED)是不用导java.awt.Graphics?br />
//Enumeration<MyLine>???????????????????????????
});
}
public static void main( String[] args) {
System.out.println("String DrawLine");
DrawLine mainFrame = new DrawLine();
mainFrame.setSize(400,400);
mainFrame.setTitle("Drawline");
mainFrame.setVisible(true);
}
}
package drawline;
import java.awt.Graphics;
public class MyLine
{
private int orgX;
private int orgY;
private int endX;
private int endY;
public MyLine(int orgX, int orgY, int endX, int endY)
{
this.orgX = orgX;
this.orgY = orgY;
this.endX = endX;
this.endY = endY;
}
public void drawMe(Graphics g)
{
System.out.println(orgX + "," + orgY + " "+endX + "," + endY);
g.drawString(orgX + "," + orgY,orgX,orgY);
g.drawString(endX + "," + endY,endX,endY);
g.drawLine(orgX, orgY, endX, endY);
}
}