文字不清晰,K图也断断续续的了。这里icech找到一个缩小图片不失真的代码,还是不错的,测试成功!针对GIF和JPG的图片效果不错。

代码如下:

 

Java代码
  1. public static void reduceImg(String imgsrc, String imgdist, int widthdist,      
  2.         int heightdist) {      
  3.     try {      
  4.         File srcfile = new File(imgsrc);      
  5.         if (!srcfile.exists()) {      
  6.             return;      
  7.         }      
  8.         Image src = javax.imageio.ImageIO.read(srcfile);      
  9.      
  10.         BufferedImage tag= new BufferedImage((int) widthdist, (int) heightdist,      
  11.                 BufferedImage.TYPE_INT_RGB);      
  12.      
  13.         tag.getGraphics().drawImage(src.getScaledInstance(widthdist, heightdist,  Image.SCALE_SMOOTH), 00,  null);      
  14. //        tag.getGraphics().drawImage(src.getScaledInstance(widthdist, heightdist,  Image.SCALE_AREA_AVERAGING), 0, 0,  null);      
  15.               
  16.         FileOutputStream out = new FileOutputStream(imgdist);      
  17.         JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);      
  18.         encoder.encode(tag);      
  19.         out.close();      
  20.      
  21.     } catch (IOException ex) {      
  22.         ex.printStackTrace();      
  23.     }      
  24. }    

  代码审查是消灭Bug最重要的方法之一,这些审查在大多数时候都特别奏效。由于代码审查本身所针对的对象,就是俯瞰整个代码在测试过程中的问题和Bug。并且,代码审查对消除一些特别细节的错误大有裨益,尤其是那些能够容易在阅读代码的时候发现的错误,这些错误往往不容易通过机器上的测试识别出来。本文就常见的Java代码中容易出现的问题提出一些建设性建议,以便您在审查代码的过程中注意到这些常见的细节性错误。

  通常给别人的工作挑错要比找自己的错容易些。别样视角的存在也解释了为什么作者需要编辑,而运动员需要教练的原因。不仅不应当拒绝别人的批评,我们应该欢迎别人来发现并指出我们的编程工作中的不足之处,我们会受益匪浅的。

   正规的代码审查(code inspection)是提高代码质量的最强大的技术之一,代码审查—由同事们寻找代码中的错误—所发现的错误与在测试中所发现的错误不同,因此两者的关系是互补的,而非竞争的。

  如果审查者能够有意识地寻找特定的错误,而不是靠漫无目的的浏览代码来发现错误,那么代码审查的效果会事半功倍。在这篇文章中,我列出了11个Java编程中常见的错误。你可以把这些错误添加到你的代码审查的检查列表(checklist)中,这样在经过代码审查后,你可以确信你的代码中不再存在这类错误了。

  一、常见错误1# :多次拷贝字符串

  测试所不能发现的一个错误是生成不可变(immutable)对象的多份拷贝。不可变对象是不可改变的,因此不需要拷贝它。最常用的不可变对象是String。

  如果你必须改变一个String对象的内容,你应该使用StringBuffer。下面的代码会正常工作:

String s = new String ("Text here");

  但是,这段代码性能差,而且没有必要这么复杂。你还可以用以下的方式来重写上面的代码:

String temp = "Text here";
String s = new String (temp);

  但是这段代码包含额外的String,并非完全必要。更好的代码为:

String s = "Text here";

  二、常见错误2#: 没有克隆(clone)返回的对象

  封装(encapsulation)是面向对象编程的重要概念。不幸的是,Java为不小心打破封装提供了方便——Java允许返回私有数据的引用(reference)。下面的代码揭示了这一点:

import java.awt.Dimension;
/***Example class.The x and y values should never*be negative.*/
public class Example{
  private Dimension d = new Dimension (0, 0);
  public Example (){ }

  /*** Set height and width. Both height and width must be nonnegative * or an exception is thrown.*/
  public synchronized void setValues (int height,int width) throws IllegalArgumentException{
   if (height < 0 || width < 0)
    throw new IllegalArgumentException();
    d.height = height;
      d.width = width;
  }

  public synchronized Dimension getValues(){
   // Ooops! Breaks encapsulation
   return d;
  }
}

  Example类保证了它所存储的height和width值永远非负数,试图使用setValues()方法来设置负值会触发异常。不幸的是,由于getValues()返回d的引用,而不是d的拷贝,你可以编写如下的破坏性代码:

Example ex = new Example();
Dimension d = ex.getValues();
d.height = -5;
d.width = -10;

  现在,Example对象拥有负值了!如果getValues() 的调用者永远也不设置返回的Dimension对象的width 和height值,那么仅凭测试是不可能检测到这类的错误。

  不幸的是,随着时间的推移,客户代码可能会改变返回的Dimension对象的值,这个时候,追寻错误的根源是件枯燥且费时的事情,尤其是在多线程环境中。

  更好的方式是让getValues()返回拷贝:

public synchronized Dimension getValues(){
return new Dimension (d.x, d.y);
}

  现在,Example对象的内部状态就安全了。调用者可以根据需要改变它所得到的拷贝的状态,但是要修改Example对象的内部状态,必须通过setValues()才可以。

  三、常见错误3#:不必要的克隆

  我们现在知道了get方法应该返回内部数据对象的拷贝,而不是引用。但是,事情没有绝对:

/*** Example class.The value should never * be negative.*/
public class Example{
  private Integer i = new Integer (0);
  public Example (){ }

  /*** Set x. x must be nonnegative* or an exception will be thrown*/
  public synchronized void setValues (int x) throws IllegalArgumentException{
   if (x < 0)
    throw new IllegalArgumentException();
    i = new Integer (x);
  }

  public synchronized Integer getValue(){
   // We can’t clone Integers so we makea copy this way.
   return new Integer (i.intValue());
  }
}

  这段代码是安全的,但是就象在错误1#那样,又作了多余的工作。Integer对象,就象String对象那样,一旦被创建就是不可变的。因此,返回内部Integer对象,而不是它的拷贝,也是安全的。

  方法getValue()应该被写为:

public synchronized Integer getValue(){
// ’i’ is immutable, so it is safe to return it instead of a copy.
return i;
}

  Java程序比C++程序包含更多的不可变对象。JDK 所提供的若干不可变类包括:

  ·Boolean
   ·Byte
   ·Character
   ·Class
   ·Double
   ·Float
   ·Integer
   ·Long
   ·Short
   ·String
   ·大部分的Exception的子类

    四、常见错误4# :自编代码来拷贝数组

  Java允许你克隆数组,但是开发者通常会错误地编写如下的代码,问题在于如下的循环用三行做的事情,如果采用Object的clone方法用一行就可以完成:

public class Example{
  private int[] copy;
  /*** Save a copy of ’data’. ’data’ cannot be null.*/
  public void saveCopy (int[] data){
   copy = new int[data.length];
   for (int i = 0; i < copy.length; ++i)
    copy[i] = data[i];
  }
}

  这段代码是正确的,但却不必要地复杂。saveCopy()的一个更好的实现是:

void saveCopy (int[] data){
  try{
   copy = (int[])data.clone();
  }catch (CloneNotSupportedException e){
   // Can’t get here.
  }
}

  如果你经常克隆数组,编写如下的一个工具方法会是个好主意:

static int[] cloneArray (int[] data){
  try{
   return(int[])data.clone();
  }catch(CloneNotSupportedException e){
   // Can’t get here.
  }
}

  这样的话,我们的saveCopy看起来就更简洁了:

void saveCopy (int[] data){
  copy = cloneArray ( data);
}

  五、常见错误5#:拷贝错误的数据

  有时候程序员知道必须返回一个拷贝,但是却不小心拷贝了错误的数据。由于仅仅做了部分的数据拷贝工作,下面的代码与程序员的意图有偏差:

import java.awt.Dimension;
/*** Example class. The height and width values should never * be
negative. */
public class Example{
  static final public int TOTAL_VALUES = 10;
  private Dimension[] d = new Dimension[TOTAL_VALUES];
  public Example (){ }

  /*** Set height and width. Both height and width must be nonnegative * or an exception will be thrown. */
  public synchronized void setValues (int index, int height, int width) throws IllegalArgumentException{
   if (height < 0 || width < 0)
    throw new IllegalArgumentException();
    if (d[index] == null)
     d[index] = new Dimension();
     d[index].height = height;
     d[index].width = width;
  }
  public synchronized Dimension[] getValues()
   throws CloneNotSupportedException{
    return (Dimension[])d.clone();
  }
}

  这儿的问题在于getValues()方法仅仅克隆了数组,而没有克隆数组中包含的Dimension对象,因此,虽然调用者无法改变内部的数组使其元素指向不同的Dimension对象,但是调用者却可以改变内部的数组元素(也就是Dimension对象)的内容。方法getValues()的更好版本为:

public synchronized Dimension[] getValues() throws CloneNotSupportedException{
  Dimension[] copy = (Dimension[])d.clone();
  for (int i = 0; i < copy.length; ++i){
   // NOTE: Dimension isn’t cloneable.
   if (d != null)
    copy[i] = new Dimension (d[i].height, d[i].width);
  }
  return copy;
}

  在克隆原子类型数据的多维数组的时候,也会犯类似的错误。原子类型包括int,float等。简单的克隆int型的一维数组是正确的,如下所示:

public void store (int[] data) throws CloneNotSupportedException{
  this.data = (int[])data.clone();
  // OK
}

  拷贝int型的二维数组更复杂些。Java没有int型的二维数组,因此一个int型的二维数组实际上是一个这样的一维数组:它的类型为int[]。简单的克隆int[][]型的数组会犯与上面例子中getValues()方法第一版本同样的错误,因此应该避免这么做。下面的例子演示了在克隆int型二维数组时错误的和正确的做法:

public void wrongStore (int[][] data) throws CloneNotSupportedException{
  this.data = (int[][])data.clone(); // Not OK!
}
public void rightStore (int[][] data){
  // OK!
  this.data = (int[][])data.clone();
  for (int i = 0; i < data.length; ++i){
   if (data != null)
    this.data[i] = (int[])data[i].clone();
  }
}

   六、常见错误6#:检查new 操作的结果是否为null

  Java编程新手有时候会检查new操作的结果是否为null。可能的检查代码为:

Integer i = new Integer (400);
if (i == null)
throw new NullPointerException();

  检查当然没什么错误,但却不必要,if和throw这两行代码完全是浪费,他们的唯一功用是让整个程序更臃肿,运行更慢。

  C/C++程序员在开始写java程序的时候常常会这么做,这是由于检查C中malloc()的返回结果是必要的,不这样做就可能产生错误。检查C++中new操作的结果可能是一个好的编程行为,这依赖于异常是否被使能(许多编译器允许异常被禁止,在这种情况下new操作失败就会返回null)。在java 中,new 操作不允许返回null,如果真的返回null,很可能是虚拟机崩溃了,这时候即便检查返回结果也无济于事。

    七、常见错误7#:用== 替代.equals

  在Java中,有两种方式检查两个数据是否相等:通过使用==操作符,或者使用所有对象都实现的.equals方法。原子类型(int, flosat, char 等)不是对象,因此他们只能使用==操作符,如下所示:

int x = 4;
int y = 5;
if (x == y)
   System.out.println ("Hi");
// This ’if’ test won’t compile.
if (x.equals (y))
   System.out.println ("Hi");

  对象更复杂些,==操作符检查两个引用是否指向同一个对象,而equals方法则实现更专门的相等性检查。

  更显得混乱的是由java.lang.Object 所提供的缺省的equals方法的实现使用==来简单的判断被比较的两个对象是否为同一个。

  许多类覆盖了缺省的equals方法以便更有用些,比如String类,它的equals方法检查两个String对象是否包含同样的字符串,而Integer的equals方法检查所包含的int值是否相等。

  大部分时候,在检查两个对象是否相等的时候你应该使用equals方法,而对于原子类型的数据,你用该使用==操作符。

  八、常见错误8#: 混淆原子操作和非原子操作

  Java保证读和写32位数或者更小的值是原子操作,也就是说可以在一步完成,因而不可能被打断,因此这样的读和写不需要同步。以下的代码是线程安全(thread safe)的:

public class Example{
  private int value; // More code here…
  public void set (int x){
   // NOTE: No synchronized keyword
   this.value = x;
  }
}

  不过,这个保证仅限于读和写,下面的代码不是线程安全的:

public void increment (){
  // This is effectively two or three instructions:
  // 1) Read current setting of ’value’.
  // 2) Increment that setting.
  // 3) Write the new setting back.
  ++this.value;
}

  在测试的时候,你可能不会捕获到这个错误。首先,测试与线程有关的错误是很难的,而且很耗时间。其次,在有些机器上,这些代码可能会被翻译成一条指令,因此工作正常,只有当在其它的虚拟机上测试的时候这个错误才可能显现。因此最好在开始的时候就正确地同步代码:

public synchronized void increment (){
  ++this.value;
}

  九、常见错误9#:在catch 块中作清除工作

  一段在catch块中作清除工作的代码如下所示:

OutputStream os = null;
try{
  os = new OutputStream ();
  // Do something with os here.
  os.close();
}catch (Exception e){
  if (os != null)
  os.close();
}

  尽管这段代码在几个方面都是有问题的,但是在测试中很容易漏掉这个错误。下面列出了这段代码所存在的三个问题:

  1.语句os.close()在两处出现,多此一举,而且会带来维护方面的麻烦。

  2.上面的代码仅仅处理了Exception,而没有涉及到Error。但是当try块运行出现了Error,流也应该被关闭。

  3.close()可能会抛出异常。

  上面代码的一个更优版本为:

OutputStream os = null;
try{
  os = new OutputStream ();
  // Do something with os here.
}finally{
  if (os != null)
   os.close();
}

  这个版本消除了上面所提到的两个问题:代码不再重复,Error也可以被正确处理了。但是没有好的方法来处理第三个问题,也许最好的方法是把close()语句单独放在一个try/catch块中。

  十、常见错误10#: 增加不必要的catch 块

  一些开发者听到try/catch块这个名字后,就会想当然的以为所有的try块必须要有与之匹配的catch块。

  C++程序员尤其是会这样想,因为在C++中不存在finally块的概念,而且try块存在的唯一理由只不过是为了与catch块相配对。

  增加不必要的catch块的代码就象下面的样子,捕获到的异常又立即被抛出:

try{
  // Nifty code here
}catch(Exception e){
  throw e;
}finally{
  // Cleanup code here
}

  不必要的catch块被删除后,上面的代码就缩短为:

try{
  // Nifty code here
}finally{
  // Cleanup code here
}

  常见错误11#;没有正确实现equals,hashCode,或者clone 等方法

  方法equals,hashCode,和clone 由java.lang.Object提供的缺省实现是正确的。不幸地是,这些缺省实现在大部分时候毫无用处,因此许多类覆盖其中的若干个方法以提供更有用的功能。但是,问题又来了,当继承一个覆盖了若干个这些方法的父类的时候,子类通常也需要覆盖这些方法。在进行代码审查时,应该确保如果父类实现了equals,hashCode,或者clone等方法,那么子类也必须正确。正确的实现equals,hashCode,和clone需要一些技巧。

  小结

  我在代码审查的时候至少遇到过一次这些错误,我自己也犯过其中的几个错误。好消息是只要你知道你在找什么错误,那么代码审查就很容易管理,错误也很容易被发现和修改。即便你找不到时间来进行正规的代码审查,以自审的方式把这些错误从你的代码中根除会大大节省你的调试时间。花时间在代码审查上是值得的。

问题一:如保加载JDBC驱动程序

正常我们加载驱动程序有三个途径:

1)Class.forName(String)这想当于classLoader一个String指定的类,在装载时把该驱动程序的静态内容都初始化,其实这时驱动程序类调用了DriverManager.registerDriver(driver);方法
2)使用系统属性:System.getProperty().load(new FileInputStream("属性文件"));
在属性文件中指定jdbc.driver=drivername 这样的好处是可以同时加载多个JDBC,换数据库时不用访问JAVA源代码,只是修改属性文件
3)直接registerDriver(driver)这种方法最可靠,可以在任何环境下使用。

1)方法简单,但MS的JVM不能正确初始化。比如使用IE时在APPLET中就不能使用,应该用3)的方法。但3)方法在灵活性方面不如2),可以根据环境综合考虑。

问题二:大对象存储

一般来说,大对象存储是把文件存到数据库中,当然也可以内存中的超大字符串。对于象图片这样的文件当然是用二进制存储,这里有很多误区,网络上的教程99%都是行不通的,连SUN自己的文档都一直错误,虽然错误很小。按说二进制文件应该存为BLOB类型,但JBDC2并不能直接对BLOB存入二进制文件,如果你这样做,会得到一个IO而不是SQL异常,为此花了我近两个小时才弄清楚。

如果要把一个二制文件存入ORACLE,用标准的JDBC你就要用LONG ROW类型:
create table tb_file(name varchar(20),detail long row);
然后
File file = new File("aaa.gif");
int fileLength =(int) file.length();
InputStream fin = new FileInputStream(file);
PreparedStatement pstmt = con.prepareStatement("insert into tb_file values(´aaa.gif´,?)");
pstmt.setBinaryStream (1, fin, fileLength);
pstmt.executeUpdate();

如果你一定要用BLOB存储,你就必须用ORACLE自己的方法:
create table tb_file(name varchar(20),detail BLOB);
con.setAutoCommit(false);
stmt.executeUpdate("insert into tb_file values(´aaa.gif´,empty_blob())");
下面必须SELECT得到BLOB的对象再向里写:
rs = stmt.executeQuery("select detail from tb_file where name=´aaa.gif´ for upfdate" );
if(rs.next())
{
Blob blob = rs.getBlob(1);
BinaryOutputStream out = ((oracle.sql.BLOB)blob).getBinaryOutputStream();
byte[] b = new byte[((oracle.sql.BLOB)blob).getBufferSize];
InputStream fin = new FileInputStream(file);
int len = 0;
while( (len = fin.read(b)) != -1)
out.write(b,0,len);
fin.close();
out.close();
con.commit();
}

同样读取数据你并不能象LONG ROW那样
InputStream in = rs.getBinaryInputStream("detail");
而要
Blob blob = rs.getBlob("detail");
in = blob.getBinaryStream();

问题三:可滚动结果集

ORACLE 明确说明不支持结果集滚动,那么我们用JDBC2得到一个可滚动的结果集就是同JDBC自己支持的,就是说结果集要在内在中高度缓存,很多很多的开发者都错误地认为是数据库支持的。只是他们没有真正查询大量行,如果真的查询大量行的话肯定是死定了!对于超大量行的数据,情愿返回到它的笨方法也不要使用可滚动结果集.

SYS-CON 媒体(www.sys-con.com)在近日揭晓了一年一度的" 读者选择奖" .这个奖项有“软件工业奥斯卡”之称.今年是第10届,有超过1.7万的SYS-CON读者参与了投票,分别评选出了在SOA、Web Services, Java 和 XML技术等领域的最佳产品、工具以及最佳提名。

1) 最佳 Java 应用服务器

获胜者:BEA WebLogic Server (BEA Systems)

提名:
1) JBoss Application Server (JBoss)
2) Sun Java Application Server (Sun Microsystems)
3) IBM WebSphere Application Server (IBM)

2) 最佳 Java 应用程序

获胜者: Eclipse (Eclipse Foundation)

提名:
1) IntelliJ IDEA (JetBrains)
2) BEA WebLogic Server (BEA Systems)
3) IBM WebSphere Application Server (IBM)

3) 最佳企业级数据库

获胜者: Oracle Database 10g (Oracle)

提名:
1) IBM DB2 Universal Database (IBM)
2) HSQLDB (HSQLDB Development Team)
3) Sybase Adaptive Server Enterprise (Sybase)

4) 最佳 Java IDE

获胜者: IntelliJ IDEA (JetBrains)

提名:
1) Eclipse (Eclipse Foundation)
2) NetBeans Java Studio Enterprise (Sun Microsystems)
3) IBM Rational Application Developer (IBM)

5) 最佳 Java 安装

获胜者:InstallAnywhere (Macrovision)

提名:
1) InstallShield (Macrovision)
2) Install4j (ej-technologies)
3) Advanced Installer for Java (Caphyon)

6) 最佳Java 应用程序监控工具

获胜者: IBM Tivoli (IBM)

提名:
1) APM Suite for Java 2 Platform (Quest)
2) Borland Deployment Op-Center (Borland)
3) Veritas Java 2 Adaptive Instrumentation (Veritas/Symantec)

7) 最佳 Java? BI 工具

获胜者:SAP Business Intelligence Development Kit (SAP)

提名:
1) Crystal Enterprise (Business Objects)
2) Oracle Business Intelligence Beans (Oracle)
3) JReport (Jinfonet Software)

8) 最佳移动数据库

获胜者: IBM DB2 Everyplace (IBM)

提名:
1) PointBase Micro (DataMirror)
2) SQL Anywhere Studio (Sybase)
3) Berkeley DB Java Edition (Sleepycat Software)

9) 最佳Java 报表工具

获胜者: Style Report (InetSoft)

提名:
1) Crystal Reports (Business Objects)
2) JReport (Jinfonet Software)
3) JasperAssistant (JasperAssistant Software)

10)最佳Rich Client平台

获胜者: Eclipse Rich Client Platform (Eclipse Foundation)

提名:
1) Ultra Light Client for Java 2 (Canoo)
2) Nexaweb Platform (Nexaweb Technologies)
3) Laszlo Presentation Server (Laszlo Systems)

11) 最佳Java 组件

获胜者: Oracle ADF Business Components (Oracle)

提名:
1) IMS DL/I Model Utility (IBM)
2) Chart FX for Java (Software FX)
3) JClass (Quest Software)

12) 最佳Java 数据库访问工具

获胜者: IBM Rational Application Developer (IBM)

提名:
1) Oracle Application Server TopLink 10g (Oracle)
2) DBVisualizer (Minq Sofware)
3) Kodo JDO (SolarMetric/BEA Systems)

13) 最佳 Java 消息发送工具

获胜者: IBM WebSphere MQ (IBM)

提名:
1) Sun Java System Message Queue (Sun Microsystems)
2) TIBCO Enterprise Message Service (TIBCO)
3) JORAM (ScalAgent Distributed Technologies)

14) 最佳Java Profiling / Testing工具

获胜者:JProfiler (ej-technologies)

提名:
1) IBM Rational Application Developer for WebSphere (IBM)
2) Optimizeit Enterprise Suit (Borland)
3) Oracle JDeveloper (Oracle)

15) 最佳Java 培训

获胜者: Java BluePrints (Sun Microsystems)

提名:
1) developerWorks Java Tutorials (IBM)
2) dev2dev (BEA Systems)
3) Essential Java (DevelopMentor)

16)最佳Java 虚拟机

获胜者: Java SE (Sun Microsystems)

提名:
1) BEA WebLogic JRockit (BEA Systems)
2) IBM Java SDK (IBM)
3) Kaffe (Kaffe.org)

17)最佳Java 团队开发工具

获胜者: Eclipse IDE (Eclipse Foundation)

提名:
1) IBM WebSphere Studio with Rational ClearCase (IBM)
2) Oracle JDeveloper (Oracle)
3) BEA WebLogic Workshop (BEA Systems)

18) 最佳Java 创新工具

获胜者: Eclipse IDE (Eclipse Foundation)

提名:
1) Java Studio Creator (Sun Microsystems)
2) IntelliJ IDEA (JetBrains)
3) BEA WebLogic Workshop (BEA Systems)

19) 最佳 Java SOA Kit

获胜者:BEA WebLogic Workshop (BEA Systems)

提名:
1) Java Web Services Developer Pack (Sun Microsystems)
2) IBM Emerging Technologies Toolkit (IBM)
3) Oracle JDeveloper (Oracle)

20) 最佳 Java 无线应用

获胜者: IBM WebSphere MQ Everyplace (IBM)

提名:
1) RIM Blackberry Wireless Handheld (Research in Motion)
2) Ericsson Wireless Office (Ericsson)
3) ValueFirst Velocity Plus (ValueFirst)

21) 最佳 Java 类库

获胜者: SWT (Eclipse Foundation)

提名:
1) Asynchronous IO Java Package (IBM)
2) Quest JClass (Quest)
3) Unified IO for Java (Andrey Kuznetsov)

22) 最佳 数据库工具或者驱动程序

获胜者:MagicDraw UML (No Magic)

提名:
1) Oracle JDeveloper (Oracle)
2) IBM Rational Rose Data Modeler (IBM)
3) AllFusion ERwin Data Modeler (CA)

23) 最佳 Java 持久层架构

获胜者:Oracle Application Server TopLink 10g (Oracle)

提名:
1) WebObjects (Apple)
2) iBatis SQL Map (Open Source: iBatis)
3) Kodo JDO (SolarMetric)

24) 最佳 Java 调试工具

获胜者: Eclipse (Eclipse Foundation)

提名:
1) NetBeans (Sun Microsystems)
2) Enerjy Code Analyzer (Enerjy Software)
3) IBM Rational (IBM)

25) 最佳 Java 应用开发框架

获胜者: Sun Java Studio Enterprise (Sun Microsystems)

提名:
1) IBM Rational (IBM)
2) IntelliJ IDEA (JetBrains)
3) BEA WebLogic Workshop (BEA Systems)

26) 最佳 Java 书

获胜者:Hibernate in Action (Manning Publications)

提名:
1) J2EE BluePrints (Sun Microsystems)
2) Core Java 2 (Sun Microsystems)
3) Java Developer’s Guide to Eclipse (IBM)

  本文介绍的Java规则的说明分为5个级别,级别1是最基本也是最重要的级别,在今后将陆续写出其他的规则。遵守了这些规则可以提高程序的效率、使代码有更好的可读性等。

  (1) 避免使用NEW关键字来创建String对象

  把一个String常量copy到String 对象中通常是多余、浪费时间的。

Public class test{
 Public void method(){
  System.out.print (str);
 }
 private String str = new String ("1"); //这里新建对象是完全没有必要的
 private String str2=”2” //正确的应该如此
}

  (2) 避免使用不必要的嵌套

  过多的嵌套会使你的代码复杂化,减弱可读性。

Public class test {
 String add (){
  Int c=(a=a+b)+b; //过于复杂
  Return c
 }
}

  (3) 避免在同一行声明不同类型的多个变量

  这样可以使程序更加清晰,避免混乱

private int index, index1[];

  正确的应该如此:

private int index;
private int index1[];

  (4) 在每一行里写一条语句

  这条规则不包括for语句:比如:‘for (int i = 0; i < 10; i++) x–;’可以增加代码的可读性。

public class OSPL {
 int method (int a, int b) {
  int i = a + b; return i; // 可读性不强
 }

  正确的:

public class OSPLFixed {
 int method (int a, int b) {
  int i = a + b;
  return i;
 }
}

  (5)经常从finalize ()中调用super.finalize ()

  这里的finalize ()是java在进行垃圾收集的时候调用的,和finally不一样。如果你的父类没有定义finally()的话,你也应该调用。这里有两个原因:(1)在不改变代码的情况下能够将父类的finally方法加到你的类中。 (2)以后你会养成习惯调用父类的finally方法,即使父类没有定义finally方法的时候。

  正确的方法应该如此:

public class parentFinalize {
 protected void finalize () throws Throwable {
  super.finalize(); // FIXED
 }

  (6) 不要在finalize ()中注销listeners

  不要再finalize ()方法中中注销listeners,finalize ()只有再没有对象引用的时候调用,如果listeners从finalize()方法中去除了,被finalize的对象将不会在垃圾收集中去除。

public void finalize () throws Throwable {
 bButton.removeActionListener (act);
}

  (7) 不要显式的调用finalize ()方法

  虽然显式的调用这个方法可以使你确保你的调用,但是当这个方法收集了以后垃圾收集会再收集一次。

public class T7 {
 public void finalize() throws Throwable {
  close_resources ();
  super.finalize ();
 }
 public void close_resources() {}
}
class Test {
 void cleanup () throws Throwable {
  t71.finalize(); // 调用
  t71 = null;
 }
 private t71 = new T7 ();
}

  对于这样的调用我们应该自己创建一个释放的方法,做最初finalize ()所作的事情,当你每次想显式的调用finalize ()的时候实际上调用了释放方法。然后再使用一个判断字段来确保这个方法只执行一次,以后再调用就没关系了。

public class T7 {
 public synchronized void release () throws Throwable{
  if (!_released) {
   close_resources (); // do what the old ‘finalize ()‘
   did _released = true;
  }
 }
 public void finalize () throws Throwable {
  release ();
  super.finalize ();
 }
 public void close_resources() {}
 private boolean _released = false;
}
class TestFixed {
 void closeTest () throws Throwable {
  t71 .release (); // FIXED
  t71 = null;
 }
 private T7 t71 = new T7 ();
}

  (8)不要使用不推荐的API

  尽量使用JDK1.3推荐的API。在类和方法或者java组件里有很多方法是陈旧的或者是可以选择的。有一些方法SUN用了"deprecated“标记。最好不要使用例如:

private List t_list = new List ();
t_list.addItem (str);

  如果查一下javadoc的话,会发现建议用add()来代替addItem()。

  (9)为所有序列化的类创建一个‘serialVersionUID‘

  可以避免从你各种不同的类破坏序列的兼容性。如果你不特别制订一个UID的话,那么系统为自动产生一个UID(根据类的内容)。如果UID在你新版本的类中改变了,即使那个被序列化的类没改变,你也不能反序列化老的版本了。

public class DUID implements java.io.Serializable { public void method () {}}

  在里面加一个UID,当这个类的序列化形式改变的时候,你也改变这个UID就可以了。

public class DUIDFixed implements java.io.Serializable {
 public void method () {}
 private static final long serialVersionUID = 1;
}

  (10)对于private常量的定义

  比较好的做法是对于这样的常量,加上final标记,这样的常量从初始化到最后结束值都不会改变。

private int size = 5;

  改变后的做法是:

private final int size = 5;

  (11)避免把方法本地变量和参数定义成和类变量相同的名字

  这样容易引起混扰,建议把任何的变量字都定义成唯一的。这样看来,SCJP里的那些题目在现实中就用不到了:)

public void method (int j) { final int i = 5; // VIOLATION } private int j = 2;

  建议:

public void method (int j1) { final int i = 5; // VIOLATION } private int j = 2;

前言

  java是跨平台语言,一般来说对网络的操作都在IP层以上,也就是只能对tcp/udp进行操作,当然也可以设置部分tcp/udp的option,如果想再往IP层或者数据link层操作就无能为力了,必须依靠jni使用本地OS的socket部分接口。很幸运,我在知道有winpcap的时候同时也知道有人在开发jpcap,此包可以方便的操作网络底层应用协议,以下详细描述。

  实施步骤

  下载需要的包:http://netresearch.ics.uci.edu/kfujii/jpcap/doc/index.html上可以下到最新的jpcap,你只需要把lib中的dll文件拷贝到jre的bin目录,同时lib中的jar文件拷贝到jre中的lib/ext目录下就安装完整,当然你可以使用exe安装包进行安装,这样会更加的简单。

  编码

  你可以使用任何你喜欢的ide工具,但是必须把jpcap.jar加到classpath中,否则无法编译通过,以下为代码详细。

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;

import jpcap.*;
import jpcap.packet.*;

public class ARP {
 public static byte[] arp(InetAddress ip) throws java.io.IOException{
  //发现本机器的网络接口
  int i;
  NetworkInterface[] devices=JpcapCaptor.getDeviceList();
  NetworkInterface device=null;
  for (i = 0; i < devices.length; i++)
  {
   System.out.println(devices[i].description);
  }
  device = devices[2];//我的机器是第三个进行网络通信

  if(device==null)
   throw new IllegalArgumentException(ip+" is not a local address");

  //开启网络接口
  JpcapCaptor captor=JpcapCaptor.openDevice(device,2000,false,3000);
  captor.setFilter("arp",true);
  JpcapSender sender=captor.getJpcapSenderInstance();

  InetAddress srcip=null;
  //获得接口地址,这里考虑到一网络接口可能有多地址
  for(i = 0; i < device.addresses.length ; i++)
   if(device.addresses[i].address instanceof Inet4Address){
    srcip=device.addresses[i].address;
    break;
  }

  //填写全1广播mac目标地址
  byte[] broadcast=new byte[]{(byte)255,(byte)255,(byte)255,(byte)255,(byte)255,(byte)255};
  ARPPacket arp=new ARPPacket();
  arp.hardtype=ARPPacket.HARDTYPE_ETHER;
  arp.prototype=ARPPacket.PROTOTYPE_IP;
  arp.operation=ARPPacket.ARP_REQUEST;
  arp.hlen=6;
  arp.plen=4;
  arp.sender_hardaddr=device.mac_address;
  arp.sender_protoaddr=srcip.getAddress();
  arp.target_hardaddr=broadcast;
  arp.target_protoaddr=ip.getAddress();

  EthernetPacket ether=new EthernetPacket();
  ether.frametype=EthernetPacket.ETHERTYPE_ARP;
  ether.src_mac=device.mac_address;
  ether.dst_mac=broadcast;
  arp.datalink=ether;
  //发送
  sender.sendPacket(arp);
  //处理回复
  while(true){
   ARPPacket p=(ARPPacket)captor.getPacket();
   if(p==null){
    throw new IllegalArgumentException(ip+" is not a local address");
   }
   if(Arrays.equals(p.target_protoaddr,srcip.getAddress())){
    return p.sender_hardaddr;
   }
  }
 }

 public static void main(String[] args) throws Exception{
  int i;
  if(args.length<1){
   System.out.println("Usage: java ARP <ip address>");
  }else{
   byte[] mac=ARP.arp(InetAddress.getByName(args[0]));
   for (i = 0;i < mac.length; i++)
    System.out.print(Integer.toHexString(mac[i]&0xff) + ":");
   System.out.println();
   System.exit(0);
  }
 }
}

  运行:java ARP <ipaddr>

  本程序经过测试,在linux和win都可以正常运行。

Java 平台一直都以其平台无关性自豪。虽然这种无关性有许多好处,但是它也使得编写与硬件交互的 Java 应用程序的过程变得相当复杂。在本文中,研究科学家蒋清野讨论了两个项目,它们通过提供使Java 应用程序可以使用 USB 设备的 API 而使这个过程变得更容易。虽然这两个项目仍然处于萌芽状态,但是它们都显示了良好的前景,并已经成为一些实用应用程序的基础。

通用串行总线(Universal Serial Bus USB)规范的第一个版本发表于 1996 1月。因为它的低成本、高数据传输率、使用容易和灵活性,USB 在计算机行业里获得了广泛接受。今天,许多周边设备和装置都是通过 USB 接口连接到计算机上的。目前,大多数一般用途的操作系统都提供了对 USB 设备的支持,并且用 C 或者 C++ 可以相对容易地开发访问这些外设的应用程序。不过,Java 编程语言在设计上对硬件访问提供的支持很少,所以编写与 USB 设备交互的应用程序是相当困难的。

IBM Dan Streetman 最早开始了在 Java 语言中提供对 USB 设备的访问的努力。2001年,他的项目通过 Java 规范请求(Java Specification RequestJSR)过程被接受为 Java 语言的候选扩展标准。这个项目现在称为 JSR-80 并且指定了官方包 javax.usb。同时,在 2000 6月,Mojo Jojo David Brownell SourceForge 开始了 jUSB 项目。这两个项目都开发出了 Linux 开发人员可以使用的包,尽管它们都还很不完善。这两个项目也都开始试图向其他操作系统上的 Java 应用程序提供对 USB 设备的访问,尽管它们都还没有开发出可以使用的包(参阅 参考资料 中有关本文中讨论的这两个项目及其他项目的资料)

在本文中,将对 jUSB JSR-80 项目作一个简要介绍,不过,我们首先要看一下 USB 协议的具体细节,这样您就可以理解这两个项目是如何与 USB 设备交互的。我们还将提供代码片段以展示如何用这两个项目的 API 访问 USB 设备。

USB 介绍

1994年,一个由四个行业伙伴(CompaqIntelMicrosoft NEC)组成的联盟开始制定 USB 协议。该协议最初的目的是将 PC 与电话相连并提供容易扩展和重新配置的 I/O 接口。1996 1月,发表了 USB 规范的第一个版本,1998 9月发表了后续版本(版本 1.1)。这个规范允许 127台设备同时连接到一起,总的通信带宽限制为 12 Mbps。后来,又有三个成员(Hewlett-PackardLucent Philips)加入了这个联盟。2000 4月,发表了 USB 规范的 2.0版本,它支持高达 480 Mbps 的传输率。今天,USB 在高速(视频、图像、储存)和全速(音频、宽带、麦克风)数据传输应用中起了关键作用。它还使各种低速设备(键盘、鼠标、游戏外设、虚拟现实外设)连接到 PC 上。

USB 协议有严格的层次结构。在所有 USB 系统中,只有一个主设备,到主计算机的的 USB 接口称为主控器(host controller)。主控器有两个标准??开放主控器接口(Compaq Open Host Controller InterfaceOHCI)和通用主控器接口(Intel Universal Host Controller InterfaceUHCI)。这两个标准提供了同样的能力,并可用于所有的 USB 设备,UHCI 的硬件实现更简单一些,但是需要更复杂的设备驱动程序(因而 CPU 的负荷更大一些)

USB 物理互连是分层的星形拓朴,最多有七层。一个 hub 是每个星形的中心,USB 主机被认为是 root hub。每一段连线都是 hub USB 设备的点对点连接,后者可以是为系统提供更多附加点的另一个 hub,也可以是一个提供功能的某种设备。主机使用主/从协议与 USB 设备通信。这种方式解决了包冲突的问题,但是同时也阻止了附加的设备彼此建立直接通信。

所有传输的数据都是由主控器发起的。数据从主机流向设备称为下行(downstream)或者输出(out)传输,数据从设备流向主机称为上 (upstream)或者输入(in)传输。数据传输发生在主机和 USB 设备上特定的端点(endpoint) 之间,主机与端点之间的数据链接称为管道(pipe) 一个给定的 USB 设备可以有许多个端点,主机与设备之间数据管道的数量与该设备上端点的数量相同。一个管道可以是单向或者是双向的,一个管道中的数据流与所有其他管道中的数据流无关。

USB 网络中的通信可以使用下面四种数据传输类型中的任意一种:

控制传输 这些是一些短的数据包,用于设备控制和配置,特别是在设备附加到主机上时。

批量传输 这些是数量相对大的数据包。像扫描仪或者 SCSI 适配器这样的设备使用这种传输类型。

中断传输 这些是定期轮询的数据包。主控器会以特定的间隔自动发出一个中断。

等时传输 这些是实时的数据流,它们对带宽的要求高于可靠性要求。音频和视频设备一般使用这种传输类型。

像串行端口一样,计算机上每一个 USB 端口都由 USB 控制器指定了一个惟一的标识数字(端口 ID)。当 USB 设备附加到 USB 端口上时,就将这个 惟一端口 ID 分配给这台设备,并且 USB 控制器会读取设备描述符。设备描述符包括适用于该设备的全局信息、以及设备的配置信息。配置定义了一台 USB 设备的功能和 I/O 行为。一台 USB 设备可以有一个或者多个配置,这由它们相应的配置描述符所描述。每一个配置都有一个或者多个接口,它可以视为一个物理通信渠道 ;每一个接口有零个或者多个端点,它可以是数据提供者或者数据消费者,或者同时具有这两种身份。接口由接口描述符描述,端点由端点描述符描述。并且一台 USB 设备可能还有字符串描述符以提供像厂商名、设备名或者序列号这样的附加信息。

正如您所看到的,像 USB 这样的协议为使用 Java 这种强调平台和硬件无关性的语言的开发人员提出了挑战。现在让我们看两个试图解决这个问题的项目。

jUSB API

jUSB 项目是由 Mojo Jojo David Brownell 2000 6月创立的。其目标是提供一组免费的、在 Linux 平台上访问 USB 设备的 Java API。这个 API 是按照 Lesser GPL (LGPL)条款发表的,这意味着您可以在专有和免费软件项目中使用它。这个 API 提供了对多个物理 USB 设备的多线程访问,并支持本机和远程设备。具有多个接口的设备可以同时被多个应用程序(或者设备驱动程序)所访问,其中每一个应用程序(或者设备驱动程序)都占据一个不同的接口。该 API 支持控制传输、批量传输和中断传输,不支持等时传输,因为等时传输用于媒体数据(如音频和视频)JMF API 已经在其他标准设备驱动程序上对此提供了很好的支持(参阅 参考资料)。当前,该 API 可以在具有 Linux 2.4 核心或者以前的 2.2.18 核心的 GNU/Linux 版本上工作。因此可支持大多数最新的版本,例如,该 API 可以在没有任何补丁或者升级的 Red Hat 7.2 9.0 上工作。

jUSB API 包括以下包:

1.usb.core: 这个包是 jUSB API 的核心部分。它使得 Java 应用程序可以从 USB 主机访问 USB 设备。

2.usb.linux: 这个包包含 usb.core.Host 对象的 Linux 实现、bootstrapping 支持和其他可以提升 Linux USB 支持的类。这个实现通过虚拟 USB 文件系统(usbdevfs)访问 USB 设备。

3.usb.windows: 这个包包含 usb.core.Host 对象的 Windows 实现、bootstrapping 支持和其他可以提升 Windows USB 支持的类。这个实现仍然处于非常初级的阶段。

4.usb.remote: 这个包是 usb.core API 的远程版本。它包括一个 RMI proxy 和一个 daemon 应用程序,它让 Java 应用程序可以访问远程计算机上的 USB 设备。

5.usb.util: 这个包提供了一些有用的实用程序,可以将 firmware下载到 USB 设备上、将 USB 系统的内容转储到 XML 中、以及将只有 bulk I/O USB 设备工具转换成一个套接字(socket)

6.usb.devices: 这个可选包收集了用 jUSB API 访问不同 USB 设备的 Java 代码,包括柯达数码相机和 Rio 500 MP3 播放器。这些 API 经过特别编写以简化访问特定 USB 设备的过程,并且不能用于访问其他设备。这些 API 是在 usb.core API 之上构建的,它们可以工作在所有支持 jUSB 的操作系统上。

7.usb.view: 这个可选包提供了基于 Swing USB 树简单浏览器。它是一个展示 jUSB API 应用的很好的示例程序。

尽管 usb.core.Host 对象的实现对于不同的操作系统是不同的,但是 Java 程序员只需要理解 usb.core 包就可以用 jUSB API 开始应用程序的开发。表 1 列出了 usb.core 的接口和类,Java 程序员应该熟悉它们:

1. jUSB 中的接口和类

接口/

说明

Bus

将一组 USB 设备连接到 Host

Host

表示具有一个或者多个 Bus USB 控制器

Configuration

提供对设备所支持的 USB 配置的访问,以及对与该配置关联的接口的访问

Descriptor

具有 USB 类型的描述符的实体的基类

Device

提供对 USB 设备的访问

DeviceDescriptor

提供对 USB 设备描述符的访问

EndPoint

提供对 USB 端点描述符的访问、在给定设备配置中构造设备数据输入或者输出

HostFactory

包含 bootstrapping 方法

Hub

提供对 USB hub 描述符以及一些 hub 操作的访问

Interface

描述一组端点,并与一个特定设备配置相关联

PortIdentifier

USB 设备提供稳定的字符串标识符,以便在操作和故障诊断时使

jUSB API 访问一台 USB 设备的正常过程如下:

1.通过从 HostFactory 得到 USB Host 进行 Bootstrap

2. Host 访问 USB Bus,然后从这个 Bus 访问 USB root hub( USB Device)

3.得到 hub 上可用的 USB 端口数量,遍历所有端口以找到正确的 Device

4.访问附加到特定端口上的 USB Device。可以用一台 Device PortIdentifier 直接从 Host 访问它,也可以通过从 root hub 开始遍历 USB Bus 找到它。

5. ControlMessage 与该 Device 直接交互,或者从该 Device 的当前 Configuration 中要求一个 Interface,并与该 Interface 上可用的 Endpoint 进行 I/O

清单 1 展示了如何用 jUSB API 获得 USB 系统中的内容。这个程序编写为只是查看 root hub 上可用的 USB 设备,但是很容易将它改为遍历整个 USB 树。这里的逻辑对应于上述步骤 1 到步骤 4

清单 1. jUSB API 获得 USB 系统的内容

import usb.core.*;

public class ListUSB

{

 public static void main(String[] args)

 {

try

{

 // Bootstrap by getting the USB Host from the HostFactory.

 Host host = HostFactory.getHost();

 // Obtain a list of the USB buses available on the Host.

 Bus[] bus = host.getBusses();

 int total_bus = bus.length;

 // Traverse through all the USB buses.

 for (int i=0; i

 {

// Access the root hub on the USB bus and obtain the

// number of USB ports available on the root hub.

Device root = bus[i].getRootHub();

int total_port = root.getNumPorts();

// Traverse through all the USB ports available on the

// root hub. It should be mentioned that the numbering

// starts from 1, not 0.

for (int j=1; j<=total_port; j++)

{

 // Obtain the Device connected to the port.

 Device device = root.getChild(j);

 if (device != null)

 {

// USB device available, do something here.

 }

}

 }

} catch (Exception e)

{

 System.out.println(e.getMessage());

}

 }

清单 2 展示了在应用程序成功地找到了 Device 的条件下,如何与 Interface EndPoint 进行批量 I/O 这个代码段也可以修改为执行控制或者中断 I/O。它对应于上述步骤 5

清单 2. jUSB API 执行批量 I/O

if (device != null)

{

 // Obtain the current Configuration of the device and the number of

 // Interfaces available under the current Configuration.

 Configuration config = device.getConfiguration();

 int total_interface = config.getNumInterfaces();

 // Traverse through the Interfaces

 for (int k=0; k

 {

// Access the currently Interface and obtain the number of

// endpoints available on the Interface.

Interface itf = config.getInterface(k, 0);

int total_ep = itf.getNumEndpoints();

// Traverse through all the endpoints.

for (int l=0; l

{

 // Access the endpoint, and obtain its I/O type.

 Endpoint ep = itf.getEndpoint(l);

 String io_type = ep.getType();

 boolean input = ep.isInput();

 // If the endpoint is an input endpoint, obtain its

 // InputStream and read in data.

 if (input)

 {

InputStream in;

in = ep.getInputStream();

// Read in data here

in.close();

 }

 // If the Endpoint is and output Endpoint, obtain its

 // OutputStream and write out data.

 else

 {

OutputStream out;

out = ep.getOutputStream();

// Write out data here.

out.close();

 }

}

 }

}


jUSB
项目在 2000 6月到 2001 2月期间非常活跃。该 API 的最新的版本 0.4.4发表于 2001 2 14日。从那以后只提出了很少的改进,原因可能是 IBM 小组成功地成为了 Java 语言的候选扩展标准。不过,基于 jUSB 已经开发出一些第三方应用程序,包括 JPhoto 项目(这是一个用 jUSB 连接到数码照相机的应用程序) jSyncManager 项目(这是一个用 jUSB 与使用 Palm 操作系统的 PDA 同步的应用程序)

JSR-80 API (javax.usb)

正如前面提到的,JSR-80 项目是由 IBM Dan Streetman 1999年创立的。2001年,这个项目通过 Java 规范请求(JSR)过程被接受为 Java 语言的候选扩展标准。这个项目现在称为 JSR-80 并且被正式分派了 Java javax.usb。这个项目使用 Common Public License 的许可证形式,并通过 Java Community Process 进行开发。这个项目的目标是为 Java 平台开发一个 USB 接口,可以从任何 Java 应用程序中完全访问 USB 系统。JSR-80 API 支持 USB 规范所定义的全部四种传输类型。目前,该 API Linux 实现可以在支持 2.4 核心的大多数最新 GNU/Linux 版本上工作,如 Red Hat 7.2 9.0

JSR-80 项目包括三个包:javax-usb (javax.usb API)javax-usb-ri (操作系统无关的基准实现的公共部分)以及 javax-usb-ri-linux (Linux 平台的基准实现,它将公共基准实现链接到 Linux USB 堆栈)。所有这三个部分都是构成 Linux 平台上 java.usb API 完整功能所必需的。在该项目的电子邮件列表中可以看到有人正在致力于将这个 API 移植到其他操作系统上(主要是 Microsoft Windows),但是还没有可以工作的版本发表。

尽管 JSR-80 API 的操作系统无关的实现在不同的操作系统上是不同的,但是 Java 程序员只需要理解 javax.usb 包就可以开始开发应用程序了。表 2 列出了 javax.usb 中的接口和类, Java 程序员应该熟悉它们:

2. JSR-80 API 中的接口和类

接口/

说明

UsbConfiguration

表示 USB 设备的配置

UsbConfigurationDescriptor

USB 配置描述符的接口

UsbDevice USB

设备的接口

UsbDeviceDescriptor USB

设备描述符的接口

UsbEndpoint USB

端点的接口

UsbEndpointDescriptor USB

端点描述符的接口

UsbHub

USB hub 的接口

UsbInterface

USB 接口的接口

UsbInterfaceDescriptor

USB 接口描述符的接口

UsbPipe USB

管道的接口

UsbPort USB

端口的接口

UsbServices

javax.usb实现的接口

UsbHostManager

javax.usb 的入口点

JSR-80 API 访问 USB 设备的正常过程如下:

1.通过从 UsbHostManager 得到相应的 UsbServices 进行 Bootstrap

2.通过 UsbServices 访问 root hub。在应用程序中 root hub 就是一个 UsbHub

3.获得连接到 root hub UsbDevices 清单。遍历所有低级 hub 以找到正确的 UsbDevice

4.用控制消息(UsbControlIrp) UsbDevice 直接交互,或者从 UsbDevice 的相应 UsbConfiguration 中要求一个 UsbInterface 并与该 UsbInterface 上可用的 UsbEndpoint 进行 I/O

5.如果一个 UsbEndpoint 用于进行 I/O,那么打开与它关联的 UsbPipe。通过这个 UsbPipe 可以同步或者异步提交上行数

( USB 设备到主计算机)和下行数据(从主计算机到 USB 设备)

6.当应用程序不再需要访问该 UsbDevice 时,关闭这个 UsbPipe 并释放相应的 UsbInterface

在清单 3 中,我们用 JSR-80 API 获得 USB 系统的内容。这个程序递归地遍历 USB 系统上的所有 USB hub 并找出连接到主机计算机上的所有 USB 设备。这段代码对应于上述步骤 1 到步骤 3

清单 3. JSR-80 API 获得 USB 系统的内容

import javax.usb.*;

import java.util.List;

public class TraverseUSB

{

 public static void main(String argv[])

 {

try

{

 // Access the system USB services, and access to the root

 // hub. Then traverse through the root hub.

 UsbServices services = UsbHostManager.getUsbServices();

 UsbHub rootHub = services.getRootUsbHub();

 traverse(rootHub);

} catch (Exception e) {}

 }

 public static void traverse(UsbDevice device)

 {

if (device.isUsbHub())

{

 // This is a USB Hub, traverse through the hub.

 List attachedDevices = ((UsbHub) device).getAttachedUsbDevices();

 for (int i=0; i

 {

traverse((UsbDevice) attachedDevices.get(i));

 }

}

else

{

 // This is a USB function, not a hub.

 // Do something.

}

 }

}

清单 4 展示了在应用程序成功地找到 Device 后,如何与 Interface EndPoint 进行 I/O。这段代码还可以修改为进行所有四种数据传输类型的 I/O。它对应于上述步骤 4 到步骤 6

清单 4. JSR-80 API 进行 I/O

public static void testIO(UsbDevice device)

{

 try

 {

// Access to the active configuration of the USB device, obtain

// all the interfaces available in that configuration.

UsbConfiguration config = device.getActiveUsbConfiguration();

List totalInterfaces = config.getUsbInterfaces();

// Traverse through all the interfaces, and access the endpoints

// available to that interface for I/O.

for (int i=0; i

{

 UsbInterface interf = (UsbInterface) totalInterfaces.get(i);

 interf.claim();

 List totalEndpoints = interf.getUsbEndpoints();

 for (int j=0; j

 {

// Access the particular endpoint, determine the direction

// of its data flow, and type of data transfer, and open the

// data pipe for I/O.

UsbEndpoint ep = (UsbEndpoint) totalEndpoints.get(i);

int direction = ep.getDirection();

int type = ep.getType();

UsbPipe pipe = ep.getUsbPipe();

pipe.open();

// Perform I/O through the USB pipe here.

pipe.close();

 }

 interf.release();

}

 } catch (Exception e) {}

}

JSR-80 项目从一开始就非常活跃。2003 2月发表了 javax.usb APIRI RI 0.10.0 版本。看起来这一版本会提交给 JSR-80 委员会做最终批准。预计正式成为 Java 语言的扩展标准后,其他操作系统上的实现会很快出现。Linux 开发者团体看来对 JSR-80 项目的兴趣比 jUSB 项目更大,使用 Linux 平台的 javax.usb API 的项目数量在不断地增加。

结束语

 

jUSB API JSR-80 API 都为应用程序提供了从运行 Linux 操作系统的计算机中访问 USB 设备的能力。JSR-80 API 提供了比 jUSB API 更多的功能,很有可能成为 Java 语言的扩展标准。目前,只有 Linux 开发人员可以利用 jUSB JSR-80 API 的功能。不过,有人正在积极地将这两种 API 移植到其他操作系统上。Java 开发人员应该在不久就可以在其他操作系统上访问 USB 设备。从现在起就熟悉这些 API,当这些项目可以在多个平台上发挥作用时,您就可以在自己的应用程序中加入 USB 功能了.

  在程序中,发送短信息的方式一般有三种:

  1 使用程序在网络上发送短信息,例如各大网站的短信业务。这种方式是通过程序将信息发送给运营商的网关服务器,然后通过运营商的网络发送给手机。

  2 在计算机中,通过数据线连接到手机,然后通过手机来发送短信息。这种方式是通过使用AT指令来实现。爱立信手机的AT指令你可以在以下地址找到:http://mobilityworld.ericsson.com.cn/development/download_hit.asp

  3 通过在手机中运行的程序来发送短信息。这个正是本文实现的方式。

  在J2ME中,如果想发送短信息,需要使用WMA包,MIDP2.0中已经包含,MIDP1.0中可以通过厂商提供的扩展API实现,和WMA的类库基本一样。

  下面是使用WMA向指定手机号码发送短信息的一个方法,很简单。当然WMA也提供了其他的方式来发送更多的内容。

// SMSUtil.java
package my.util;
import javax.wireless.messaging.*;
import javax.microedition.io.*;
/**
*
发送文本短信息的方法
*/
public class SMSUtil
{
 /**
 * 给指定号码发送短信息
 * @param content 短信息内容
 * @param phoneNumber 手机号码
 * @return 发送成功返回true,否则返回false
 */
 public static boolean send(String content,String phoneNumber)
 {
  //返回值
  boolean result = true;
  try
  {
   //地址
   String address = "sms://+" + phoneNumber;
   //建立连接
   MessageConnection conn = (MessageConnection)Connector.open(address);
   //设置短信息类型为文本,短信息有文本和二进制两种类型
   TextMessage msg = (TextMessage)conn.newMessage(MessageConnection.TEXT_MESSAGE);
   //设置信息内容
   msg.setPayloadText(content);
   //发送
   conn.send(msg);
  }
  catch(Exception e)
  {
   result = false;
   //未处理
  }
  return result;
 }
}