2005年12月30日星期五
Class.forName和ClassLoader.loadClass的区别
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常用快捷键
Ctrl + / 注释/取消注释
Ctrl + D 删除光标所在行
Ctrl + Shift + F 代码格式化
Ctrl + Shift + O 优化import
Ctrl + Shift + M 导入import
Ctrl + 鼠标单击 跟踪方法和类的源代码
Alt + <- -> 跳转
双击括号 选择括号内的所有内容
Ctrl + 1 快速重构
2005年12月28日星期三
整型常量前加0,该常量被视为八进制
int num = 100;
System.out.println(octal);
System.out.println(num); 运行结果:
64
100
2005年12月27日星期二
2005年12月17日星期六
使用JNI时,装载本地库的小技巧
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日星期四
2005年12月7日星期三
使用xerces作为jaxp的缺省实现,如何设置factory
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:
- The value of a system property like
javax.xml.parsers.DocumentBuilderFactory
if it exists and is accessible. - The contents of the file
$JAVA_HOME/jre/lib/jaxp.properties
if it exists. - 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. - 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应用服务器
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的使用
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" );