2005年12月30日星期五

Class.forName和ClassLoader.loadClass的区别

Class的装载分了三个阶段,loading,linking和initializing,分别定义在The Java Language Specification的12.2,12.3和12.4。
Class.forName(className)实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须被初始化。
ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false),第二个参数指出Class是否被link。区别就出来了。Class.forName(className)装载的class已经被初始化,而ClassLoader.loadClass(className)装载的class还没有被link。
一般情况下,这两个方法效果一样,都能装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。
例如,在JDBC编程中,常看到这样的用法,Class.forName("com.mysql.jdbc.Driver"),如果换成了getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver"),就不行。
为什么呢?打开com.mysql.jdbc.Driver的源代码看看,
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
原来,Driver在static块中会注册自己到java.sql.DriverManager。而static块就是在Class的初始化中被执行。所以这个地方就只能用Class.forName(className)。

eclipse常用快捷键

Atl + / 代码提示
Ctrl + / 注释/取消注释
Ctrl + D 删除光标所在行
Ctrl + Shift + F 代码格式化
Ctrl + Shift + O 优化import
Ctrl + Shift + M 导入import
Ctrl + 鼠标单击 跟踪方法和类的源代码
Alt + <- -> 跳转
双击括号 选择括号内的所有内容
Ctrl + 1 快速重构

2005年12月28日星期三

整型常量前加0,该常量被视为八进制

整型常量前加0,该常量被视为八进制。《C陷阱与缺陷》中看到的,Java里也是这样。原来真的不知道,比较弱。 int octal = 0100;
int num = 100;
System.out.println(octal);
System.out.println(num); 运行结果:
64
100

2005年12月27日星期二

vim使用小技巧

1. 使用/pattern搜索,可以再使用*继续查找整个word
2. % 匹配 {} 和 ()
3. [{ 跳到 { start of the current code block
4. gd 跳到变量的定义
5. * find整个word
6. n next find next
7. . (dot) 重复上一次的change
8. ctrl _ N 自动完成
9 缩进 << >>

首先设置
:set shiftwidth=4
:set cindent

2005年12月17日星期六

使用JNI时,装载本地库的小技巧

刚到新公司,就接到一个棘手的任务。想了很多办法,最后想使用Java COM桥来解决。JACOB是一个较成熟的开源项目,可以很方便的调用COM组件。搞过JNI的都知道,本地库要放到系统path中,这样,Java进程在运行中才能找到本地库并动态加载。我们可以通过环境变量System.getProperty("java.library.path")来查看当前JVM搜索本地库的路径。 这时,就会遇到一个问题,部署应用的时候要记住将本地库拷贝到环境变量path指定的路径中。一般在windows平台上直接copy到C:\WINDOWS\System32目录下了事。但要换一台机器部署怎么办?除了要把Java程序拿过去,还要记的把本地库也copy到正确的目录,真麻烦。于是想看看有什么好办法来解决这个问题。 首先,最容易想到的是,把本地库和class文件放在一起,利用Class.getResource(str)找到路径,然后加到环境java.library.path中:
java代码: URL url = Foo.class.getResource("Foo.class");
String path = (new File(url.getPath())).getParent();
System.setProperty("java.library.path", path);
看上去很好,但却不能工作。查了一下ClassLoader的源代码,原来它把搜索路径定义为静态变量并只初始化一次,后面再设置java.library.path就没有用了。ClassLoader代码片断:
java代码: // The paths searched for libraries
static private String usr_paths[];
static private String sys_paths[];
...
if (sys_paths == null) {
usr_paths = initializePath("java.library.path");
sys_paths = initializePath("sun.boot.library.path");
}
正在一筹莫展是,翻看JACOB的源代码,忽然有了惊喜的发现。
java代码: try
{
//Finds a stream to the dll. Change path/class if necessary
InputStream inputStream = getClass().getResource("/jacob.dll").openStream();
//Change name if necessary
File temporaryDll = File.createTempFile("jacob", ".dll");
FileOutputStream outputStream = new FileOutputStream(temporaryDll);
byte[] array = new byte[8192];
for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) {
outputStream.write(array, 0, i);
}
outputStream.close();
temporaryDll.deleteOnExit();
System.load(temporaryDll.getPath());
return true;
}
catch(Throwable e)
{
e.printStackTrace();
return false;
}
高,真是好办法。和我一样,把dll放在classpath中,用Class.getResource(str).openStream()读取这个dll,然后写到temp目录中,最后用System.load(path)来动态加载。多说一句,为什么得到了jacob.dll的URL不直接去加载呢?想想看,如果把dll和class一起打成Jar包,ClassLoader还是不能加载本地库,因为System.load(path)需要的是dll的完整路径,但并不支持jar协议。还不明白,看看下面的代码:
java代码: URL url = Foo.class.getResource("/java/lang/String.class");
System.out.println(url.toExternalForm());
System.out.println(url.getFile());
在我的机器上的运行结果是:
java代码: jar:file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class
file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class
ClassLoader中用new File(name),当然会找不到文件。同时,看看我的第一种方法,就算能设置成功环境java.library.path,如果dll是在jar包中,还是加载不了。 到现在,你是不是觉得问题已经解决了?还没呢!jacob的很多源文件中已经写了下面的代码:
java代码: static {
System.loadLibrary("jacob");
}
除非我去掉这一句重新编译jacob的源代码,否则系统还是会报错。不过,既然有了上面的想法,稍微变通一下,就可以巧妙的解决。首先找到环境java.library.path,然后把dll拷贝到其中一个路径中就行了。
java代码: static {
try {
String libpath = System.getProperty("java.library.path");
if ( libpath==null || libpath.length() == 0 ) {
throw new RuntimeException("java.library.path is null");
}

String path = null;
StringTokenizer st = new StringTokenizer(libpath, System.getProperty("path.separator"));
if ( st.hasMoreElements() ) {
path = st.nextToken();
} else {
throw new RuntimeException("can not split library path:" + libpath);
}

InputStream inputStream = Foo.class.getResource("jacob.dll").openStream();
final File dllFile = new File(new File(path), "jacob.dll");
if (!dllFile.exists()) {
FileOutputStream outputStream = new FileOutputStream(dllFile);
byte[] array = new byte[8192];
for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) {
outputStream.write(array, 0, i);
}
outputStream.close();
}
//dllFile.deleteOnExit();
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run() {
if ( dllFile.exists() ) {
boolean delete = dllFile.delete();
System.out.println("delete : " + delete);
}
}
});
} catch (Throwable e) {
throw new RuntimeException("load jacob.dll error!", e);
}
}
唯一的美中不足,在系统关闭的时候删除dll总是不能成功,试了两种办法都不行。想想也对,dll正被程序使用,当然不能删除。翻了一下API,Java好像没用提供unload本地库的功能,只好做罢。
解决了这么个小问题,罗罗嗦嗦一大篇,罪过罪过。后来这个项目又没有使用jacob,真对不起各位观众。

2005年12月10日星期六

今天去购书中心的收获

呵呵,一本书都没买,一直站着看书,腿都酸了:)
1.《Java多线程设计模式》,日本人写的,内容浅显实用,总结的不错。Future模式,搞明白了我前一阵的疑问。准备用Asynchronous IO写一个简单的HttpServer,对比一下几个IO库的性能。
IBM aio4j
apache MINA
Coconut AIO

2. 《Java测试与设计——从单元测试到自动Web测试》发现了一个很好的自动测试工具TestMaker,而且是开源的,有时间一定要研究一下。

3. 《More Java Pitfalls》学习了其中一章关于logging API使用的陷阱。看下面的这张图,有种豁然开朗的感觉。一般,只在root logger上注册Handler,并把Handler的level设为ALL,这样设置root logger的level就可以控制最终输出的级别。而且,继承自root的logger可以进一步设置level控制日志的输出。系统应该按照功能来设计logger的继承结构。
From blog

4. 准备去新的公司了,要学的东西太多了。
计划看的书:《精通Hibernate》《精通Struts》还要熟悉Websphere和Eclipse。

2005年12月8日星期四

jdk环境设置

set ant_home=D:\javaapp\apache-ant-1.6.2
set java_home=D:\jdk1.5.0
rem set java_home=D:\jdk1.4.2
set path=%java_home%\bin;%ant_home%\bin
这样,可以指定JDK的版本,并使用ant进行编译,满足大部分的要求。

2005年12月7日星期三

使用xerces作为jaxp的缺省实现,如何设置factory

javax.xml.datatype.DatatypeFactory --- org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl
javax.xml.validation.SchemaFactory --- org.apache.xerces.jaxp.validation.XMLSchemaFactory
javax.xml.parsers.SAXParserFactory --- org.apache.xerces.jaxp.SAXParserFactoryImpl
javax.xml.parsers.DocumentBuilderFactory --- org.apache.xerces.jaxp.DocumentBuilderFactoryImpl

2005年12月6日星期二

Why are there Apache classes in the J2SE 1.4 RI?

The J2SE 1.4 RI is the first version of the JDK that bundles in an implementation of JAXP 1.1. This allows developers to write applications without having to provide a parser and XSLT processor with their application. However, in some cases, it may create additional problems.

The Sun J2SE 1.4 RI uses Apache software for its implemenation of JAXP 1.1 with package names unchanged from Apache software distributions. This can cause problems, for example, if your application wants to use a newer version of Apache software. Under the Java 2 class loader delegation model, the java launcher's ClassLoader will load the bundled version of a class before any other version. Thus, if you place a newer version of xalan.jar on your CLASSPATH, then that version will be ignored since the runtime will use the older bundled version instead. As a workaround, see the question on overriding the implementation in J2SE SDK 1.4.

The future plan is to rename the org.apache.** packages to be something like com.sun.org.apache.** to fix this problem. In addition, other package-dependent parts of the software may also need to be modified. However, because of lack of resources, this may not be done until after J2SE SDK 1.4.1.

use a different JAXP implementation

http://java.sun.com/webservices/jaxp/faq.html

The JAXP 1.1 API allows applications to plug in different JAXP compatible implementations of parsers or XSLT processors. For example, when an application wants to create a new JAXP DocumentBuilderFactory instance, it calls the staic method DocumentBuilderFactory.newInstance(). This causes a search for the name of a concrete subclass of DocumentBuilderFactory using the following order:

  1. The value of a system property like javax.xml.parsers.DocumentBuilderFactory if it exists and is accessible.
  2. The contents of the file $JAVA_HOME/jre/lib/jaxp.properties if it exists.
  3. The Jar Service Provider discovery mechanism specified in the Jar File Specification. A jar file can have a resource (i.e. an embedded file) such as META-INF/services/javax.xml.parsers.DocumentBuilderFactory containing the name of the concrete class to instantiate.
  4. The fallback platform default implementation.

Of the above ways to specify an implementation, perhaps the most useful is the jar service provider mechanism. To use this mechanism, place the implementation jar file on your classpath. For example, to use Xerces 1.4.4 instead of the version of Crimson which is bundled with JDK 1.4 (Java Development Kit version 1.4), place xerces.jar in your classpath. This mechanism also works with older versions of the JDK which do not bundle JAXP. If you are using JDK 1.4 and above, see this question for potential problems.

2005年12月3日星期六

如何调试J2EE应用服务器

应用服务器作为独立运行的java程序,如果获得了源代码,可以在IDE中attach到应用服务器进程进行调试。
1. 在IDE中创建一个项目,引入服务器源代码
2. 以调试方式启动应用服务器。如果在本机,可以用共享内存的方式,在java命令行加入以下参数:
-agentlib:jdwp=transport=dt_shmem,server=y,address=apusic_debug,suspend=n
远程调试可以使用socket方式。具体的配置可以参考http://java.sun.com/j2se/1.5.0/docs/guide/jpda/conninv.html
3. 在IDE中attach到服务器进程。如上面的例子,attach的时候,name为apusic_debug。attach成功后,就可以在源代码中设置断点,进行调试。

2005年11月30日星期三

Jakarta Commons BeanUtils的使用

MyBean bean = new MyBean();
String propertyName = "name";
String value = "mazhen";

Method setter = getWriteMethod(bean, propertyName);
if ( setter != null ) {
try {
setter.invoke(bean, new Object[]{value});
} catch (Exception ex) {
ex.printStaceTrace();
}
}
private Method getWriteMethod(Object bean, String name) {
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(bean.getClass());
} catch (IntrospectionException ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
PropertyDescriptor pds[] = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds ) {
if ( pd.getName().equals(name) ) {
return pd.getWriteMethod();
}
}
return null;
}
难倒不难,就是比较麻烦。现在,我们有了Commons BeanUtils,一切都变得非常简单,而且功能更加强大。

1. 访问简单属性
String name = (String) PropertyUtils.getSimpleProperty( person, "name" );

2. 访问嵌套属性
Book book = new Book( );
book.setName( "Emerson's Essays" );

Person author = new Person( );
author.setName( "Ralph Waldo Emerson" );

book.setAuthor( author );

String authorName = (String) PropertyUtils.getNestedProperty(book, "author.name");

3. 访问索引属性
Book book = new Book( );
Chapter chapter1 = new Chapter( );
Chapter chapter2 = new Chapter( );

List chapters = new ArrayList( );
chapters.add( chapter1 );
chapters.add( chapter2 );

book.setChapters( chapters );

// You can retrieve the first chapters like this...

Chapter chapterOne = (Chapter) PropertyUtils.getIndexedProperty( book, "chapters[0]" );

// Or... you can retrieve the first chapter like this...

chapterOne = (Chapter) PropertyUtils.getIndexedProperty( book, "chapters", 0 );

4. 访问maped 属性
Room dining = new Room( );
dining.setArea( 20 );
dining.setCarpeted( true );
dining.setFurnished( true );

Map rooms = new HashMap( );
rooms.put( "Dining Room", dining );

Apartment apartment = new Apartment( );
apartment.setRooms( rooms );

// Retrieve the livingRoom key
Room room = (Room) PropertyUtils.getMappedProperty( apartment, "rooms(Dining Room)" );

// Or.. retrieve the livingRoom key with 3 parameters -

// equivalent to previous

room = (Room) PropertyUtils.getMappedProperty( apartment, "rooms", "Dining Room" );

5.访问简单、嵌套、索引和mapped属性

// Create a series of nested beans
City richmond = new City( );
richmond.setName( "Richmond" );
richmond.setPopulation( new Long(500000) );

Map cities = new HashMap( );
cities.put( "richmond", richmond );

Region midAtlantic = new Region( );
midAtlantic.setName( "Mid-Atlantic" );
midAtlantic.setCities( cities );

List regions = new ArrayList( );
regions.add( midAtlantic );

Country country = new Country( );
country.setName( "United States" );
country.setRegions( regions );

// Retrieve the population of Richmond
Long population = (Long) PropertyUtils.getProperty( country, "regions[0].cities(richmond).population" );

6. 决定属性的类型
Class type = PropertyUtils.getPropertyType( book, "author" );

7. 按照属性排序
Country country1 = new Country( );
country1.setName( "India" );

Country country2 = new Country( );
country2.setName( "Pakistan" );

Country country3 = new Country( );
country3.setName( "Afghanistan" );

// Create a List of Country objects
Country[] countryArray = new Country[] { country1, country2, country3 };

List countryList = Arrays.asList( countryArray );

// Sort countries by name
Comparator nameCompare = new BeanComparator( "name" );
Collections.sort( countryList, nameCompare );

8. copy属性
注意没有clone
Author author = new Author( );
author.setName( "Zinsser" );

Book book = new Book( );
book.setName( "On Writing Well" );

book.setAuthor( author );

Book destinationBook = new Book( );

PropertyUtils.copyProperties( destinationBook, book );

9. clone 一个Bean
Book book1 = new Book( );
book1.setName( "Count of Monte Cristo" );

Book book2 = (Book) BeanUtils.cloneBean( book1 );

10. 设置Bean的属性
Book book1 = new Book( );
book1.getChapters( ).add( new Chapter( ) );
book1.getChapters( ).add( new Chapter( ) );

PropertyUtils.setProperty( book1, "name", "Apache: The Definitive Guide" );
PropertyUtils.setProperty( book1, "author", new Person( ) );
PropertyUtils.setProperty( book1, "author.name", "Laurie" );
PropertyUtils.setProperty( book1, "chapters[0].name", "Introduction" );

Apartment apartment = new Apartment( );
apartment.getRooms( ).put( "livingRoom", new Room( ) );

PropertyUtils.setProperty( apartment, "rooms(livingRoom).length", new Integer(12) );

11. 检查属性的访问
boolean nameReadable = PropertyUtils.isReadable( book, "name" );
boolean nameWritable = PropertyUtils.isWritable( book, "name" );

12. Validating Beans with Predicates
// A Predicate that returns true if the "name" property is not null
Predicate teamNotNull = new BeanPredicate( "name", new NotNullPredicate( ) );

// A Predicate that returns true if the "coach.firstName" property is "Tom"
Predicate coachFirstName = new BeanPredicate( "coach.firstName", new EqualsPredicate("Tom") );

// Tie two Predicates together into an AndPredicate
Predicate validateTeam = new AllPredicate( predicateArray );

// Create Team Objects
Team fish = new Team( "Swordfish", new Coach( "Tom", "O'Connell") );
Team hens = new Team( "Hens", new Coach( "Bob", "McGarry") );

boolean fishValid = validateTeam.evaluate( fish );
boolean henValid = validateTeam.evaluate( hens );

13.Creating a Map of Bean Properties
// Create a Person and a Book bean instance
Person person = new Person( );
person.setName( "Some Dude" );

Book book = new Book( );
book.setName( "Some Silly Computer Book" );
book.setAuthor( person );

// Describe both beans with a Map
Map bookMap = PropertyUtils.describe( book );
Map authorMap = PropertyUtils.describe( bookMap.get("book") );

System.out.println( "Book Name: " + bookMap.get( "name" ) );
System.out.println( "Author Name: " + authorMap.get( "name" ) );

14.Wrapping a Bean with a Map
Person person = new Person( );
person.setName( "Jim" );
person.setAge( new Integer( 28 ) );
person.setOccupation( "Developer" );

Map beanMap = new BeanMap( person );

Set keys = beanMap.keySet( );
Iterator keyIterator = keys.iterator( );
while( keyIterator.hasNext( ) ) {
String propertyName = (String) keyIterator.next( );
System.out.println( "Property: " + propertyName +
", Value: " + beanMap.get( propertyName ) +
", Type: " + beanMap.getType( propertyName ).
toString( ) );
}

15.Creating a Dynamic Bean
DynaProperty[] beanProperties = new DynaProperty[]{
new DynaProperty("name", String.class),
new DynaProperty("party", Party.class),
new DynaProperty("votes", Long.class)
};

BasicDynaClass politician new BasicDynaClass("politician", BasicDynaBean.class, props);

DynaBean politician = politicianClass.newInstance( );

// Set the properties via DynaBean
politician.set( "name", "Tony Blair" );
politician.set( "party", Party.LABOUR );
politician.set( "votes", new Long( 50000000 ) );

// Set the properties with PropertyUtils
PropertyUtils.setProperty( politician, "name", "John Major" );
PropertyUtils.setProperty( politician, "party", Party.TORY );
PropertyUtils.setProperty( politician, "votes", new Long( 50000000 ) );

16. 使用String设置或得到属性
Person person = new Person( );
person.setAge( new Integer( 45 ) );
person.setName( "Donald" );
person.setOccupation( "Salesman" );

// Get the Age as a String
String ageString = BeanUtils.getProperty( person, "age" );

// Set the Age from a String
BeanUtils.setProperty( person, "age", "50" );

2005年11月24日星期四

try to connect from TCP ports greater than 5000

http://support.microsoft.com/default.aspx?scid=kb;EN-US;196271