blogjava-凯发k8网页登录

blogjava-凯发k8网页登录http://www.blogjava.net/dreamstone/category/15445.html开发出高质量的系统zh-cnsun, 16 sep 2007 14:22:04 gmtsun, 16 sep 2007 14:22:04 gmt60java实现zip与unziphttp://www.blogjava.net/dreamstone/archive/2007/08/09/134986.htmldreamstonedreamstonethu, 09 aug 2007 01:33:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/08/09/134986.htmlhttp://www.blogjava.net/dreamstone/comments/134986.htmlhttp://www.blogjava.net/dreamstone/archive/2007/08/09/134986.html#feedback2http://www.blogjava.net/dreamstone/comments/commentrss/134986.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/134986.html1,压缩的
import java.io.bufferedinputstream;
import java.io.bufferedoutputstream;
import java.io.file;
import java.io.fileinputstream;
import java.io.fileoutputstream;
import java.util.zip.zipentry;
import java.util.zip.zipoutputstream;

public class zip {
    
static final int buffer = 2048;

    
public static void main(string argv[]) {
        
try {
            bufferedinputstream origin 
= null;
            fileoutputstream dest 
= new fileoutputstream("e:\\test\\myfiles.zip");
            zipoutputstream out 
= new zipoutputstream(new bufferedoutputstream(
                    dest));
            
byte data[] = new byte[buffer];
            file f 
= new file("e:\\test\\a\\");
            file files[] 
= f.listfiles();

            
for (int i = 0; i < files.length; i{
                fileinputstream fi 
= new fileinputstream(files[i]);
                origin 
= new bufferedinputstream(fi, buffer);
                zipentry entry 
= new zipentry(files[i].getname());
                out.putnextentry(entry);
                
int count;
                
while ((count = origin.read(data, 0, buffer)) != -1{
                    out.write(data, 
0, count);
                }

                origin.close();
            }

            out.close();
        }
 catch (exception e) {
            e.printstacktrace();
        }

    }

}


2,解压缩的。
import java.io.bufferedinputstream;
import java.io.bufferedoutputstream;
import java.io.file;
import java.io.fileoutputstream;
import java.util.enumeration;
import java.util.zip.zipentry;
import java.util.zip.zipfile;

public class unzip {
    
static final int buffer = 2048;

    
public static void main(string argv[]) {
        
try {
            string filename 
= "e:\\test\\myfiles.zip";
            string filepath 
= "e:\\test\\";
            zipfile zipfile 
= new zipfile(filename);
            enumeration emu 
= zipfile.entries();
            
int i=0;
            
while(emu.hasmoreelements()){
                zipentry entry 
= (zipentry)emu.nextelement();
                
//会把目录作为一个file读出一次,所以只建立目录就可以,之下的文件还会被迭代到。
                if (entry.isdirectory())
                
{
                    
new file(filepath  entry.getname()).mkdirs();
                    
continue;
                }

                bufferedinputstream bis 
= new bufferedinputstream(zipfile.getinputstream(entry));
                file file 
= new file(filepath  entry.getname());
                
//加入这个的原因是zipfile读取文件是随机读取的,这就造成可能先读取一个文件
                
//而这个文件所在的目录还没有出现过,所以要建出目录来。
                file parent = file.getparentfile();
                
if(parent != null && (!parent.exists())){
                    parent.mkdirs();
                }

                fileoutputstream fos 
= new fileoutputstream(file);
                bufferedoutputstream bos 
= new bufferedoutputstream(fos,buffer);           
                
                
int count;
                
byte data[] = new byte[buffer];
                
while ((count = bis.read(data, 0, buffer)) != -1)
                
{
                    bos.write(data, 
0, count);
                }

                bos.flush();
                bos.close();
                bis.close();
            }

            zipfile.close();
        }
 catch (exception e) {
            e.printstacktrace();
        }

    }

}



dreamstone 2007-08-09 09:33 发表评论
]]>
file的getpath getabsolutepath和getcanonicalpath的不同http://www.blogjava.net/dreamstone/archive/2007/08/08/134968.htmldreamstonedreamstonewed, 08 aug 2007 01:12:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/08/08/134968.htmlhttp://www.blogjava.net/dreamstone/comments/134968.htmlhttp://www.blogjava.net/dreamstone/archive/2007/08/08/134968.html#feedback2http://www.blogjava.net/dreamstone/comments/commentrss/134968.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/134968.html
概念上的区别:(内容来自jdk,个人感觉这个描述信息,只能让明白的人明白,不明白的人看起来还是有点难度(特别试中文版,英文版稍好些)所以在概念之后我会举例说明。如果感觉看概念很累就跳过直接看例子吧。看完例子回来看概念会好些。
getpath
public string getpath()将此抽象路径名转换为一个路径名字符串。所得到的字符串使用默认名称分隔符来分隔名称序列中的名称。 

返回:
此抽象路径名的字符串形式

getabsolutepath
public string getabsolutepath()返回抽象路径名的绝对路径名字符串。 
如果此抽象路径名已经是绝对路径名,则返回该路径名字符串,这与 getpath() 方法一样。如果此抽象路径名是空的抽象路径名,则返回当前用户目录的路径名字符串,该目录由系统属性 user.dir 指定。否则,使用与系统有关的方式分析此路径名。在 unix 系统上,通过根据当前用户目录分析某一相对路径名,可使该路径名成为绝对路径名。在 microsoft windows 系统上,通过由路径名指定的当前驱动器目录(如果有)来分析某一相对路径名,可使该路径名成为绝对路径名;否则,可以根据当前用户目录来分析它。 


返回:
绝对路径名字符串,它与此抽象路径名表示相同的文件或目录的 
抛出: 
securityexception 
- 如果无法访问所需的系统属性值。
另请参见:
isabsolute()

getcanonicalpath
public string getcanonicalpath()
                        
throws ioexception返回抽象路径名的规范路径名字符串。 
规范路径名是绝对路径名,并且是惟一的。规范路径名的准确定义与系统有关。如有必要,此方法首先将路径名转换成绝对路径名,这与调用 getabsolutepath() 方法的效果一样,然后用与系统相关的方式将它映射到其惟一路径名。这通常涉及到从路径名中移除多余的名称(比如 
"." 和 "..")、分析符号连接(对于 unix 平台),以及将驱动器名转换成标准大小写形式(对于 microsoft windows 平台)。 

表示现有文件或目录的每个路径名都有一个惟一的规范形式。表示非存在文件或目录的每个路径名也有一个惟一的规范形式。非存在文件或目录路径名的规范形式可能不同于创建文件或目录之后同一路径名的规范形式。同样,现有文件或目录路径名的规范形式可能不同于删除文件或目录之后同一路径名的规范形式。 


返回:
表示与此抽象路径名相同的文件或目录的规范路径名字符串 
抛出: 
ioexception 
- 如果发生 i/o 错误(可能是因为构造规范路径名需要进行文件系统查询) 
securityexception 
- 如果无法访问所需的系统属性值,或者存在安全管理器,且其 securitymanager.checkread(java.io.filedescriptor) 方法拒绝对该文件进行读取访问
从以下版本开始: 
jdk1.
1 

二、例子:
1,getpath()与getabsolutepath()的区别
public static void test1(){
        file file1 
= new file(".\\test1.txt");
        file file2 
= new file("d:\\workspace\\test\\test1.txt");
        system.out.println(
"-----默认相对路径:取得路径不同------");
        system.out.println(file1.getpath());
        system.out.println(file1.getabsolutepath());
        system.out.println(
"-----默认绝对路径:取得路径相同------");
        system.out.println(file2.getpath());
        system.out.println(file2.getabsolutepath());
        
    }

得到的结果:
-----默认相对路径:取得路径不同------
.\test1.txt
d:\workspace\test\.\test1.txt
-----默认绝对路径:取得路径相同------
d:\workspace\test\test1.txt
d:\workspace\test\test1.txt
因为getpath()得到的是构造file的时候的路径。
getabsolutepath()得到的是全路径
如果构造的时候就是全路径那直接返回全路径
如果构造的时候试相对路径,返回当前目录的路径 构造file时候的路径

2,getabsolutepath()和getcanonicalpath()的不同
public static void test2() throws exception{
        file file 
= new file("..\\src\\test1.txt");
        system.out.println(file.getabsolutepath());
        system.out.println(file.getcanonicalpath());
    }
得到的结果
d:\workspace\test\..\src\test1.txt
d:\workspace\src\test1.txt
可以看到canonicalpath不但是全路径,而且把..或者.这样的符号解析出来。
3,getcanonicalpath()和自己的不同。
就是解释这段话:
表示现有文件或目录的每个路径名都有一个惟一的规范形式。表示非存在文件或目录的每个路径名也有一个惟一的规范形式。非存在文件或目录路径名的规范形式可能不同于创建文件或目录之后同一路径名的规范形式。同样,现有文件或目录路径名的规范形式可能不同于删除文件或目录之后同一路径名的规范形式。 
单下边这段代码是看不到结果的,要配合一定的操作来看。下边操作步骤,同时讲解

public static void test3() throws exception{
        file file 
= new file("d:\\text.txt");
        system.out.println(file.getcanonicalpath());
    }
步骤:
确定你的系统是windows系统。
(1),确定d盘下没有text.txt这个文件,直接执行这段代码,得到的结果是:
d:\text.txt
注意这里试大写的text.txt
(2)在d盘下建立一个文件,名叫text.txt,再次执行代码,得到结果
d:\text.txt
同样的代码得到不同的结果。
同时可以对比getabsolutepath()看看,这个得到的结果是一样的。

原因:
window是大小写不敏感的,也就是说在windows上test.txt和test.txt是一个文件,所以在windows上当文件不存在时,得到的路径就是按照输入的路径。但当文件存在时,就会按照实际的情况来显示。这也就是建立文件后和删除文件后会有不同的原因。文件夹和文件类似。

三、最后:
1,尝试在linux下执行上边的步骤,两次打印的结果是相同的,因为linux是大小写敏感的系统。
2,手动删掉test.txt,然后尝试执行下边代码
public static void test4() throws exception{
        file file 
= new file("d:\\text.txt");
        system.out.println(file.getcanonicalpath());
        file file1 
= new file("d:\\text.txt");
        file1.createnewfile();
        file 
= new file("d:\\text.txt");
        system.out.println(file.getcanonicalpath());
    }

public static void test3() throws exception{
        file file1 
= new file("d:\\text.txt");
        file1.createnewfile();
        file file 
= new file("d:\\text.txt");
        system.out.println(file.getcanonicalpath());
    }
执行上边两个函数,看看结果,然后思考一下为什么?
1,的结果是两个大写,
2,的结果试两个小写
连续两个大写的,是否跟上边的矛盾 ?
这是因为虚拟机的缓存机制造成的。第一次file file = new file("d:\\text.txt");决定了结果.



dreamstone 2007-08-08 09:12 发表评论
]]>
jdk相关知识合集http://www.blogjava.net/dreamstone/archive/2007/07/31/133661.htmldreamstonedreamstonetue, 31 jul 2007 14:14:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/07/31/133661.htmlhttp://www.blogjava.net/dreamstone/comments/133661.htmlhttp://www.blogjava.net/dreamstone/archive/2007/07/31/133661.html#feedback1http://www.blogjava.net/dreamstone/comments/commentrss/133661.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/133661.htmlhttp://www.blogjava.net/dreamstone/archive/2007/07/28/133003.html
jdk1.5中的新特性 --泛型 (详细版)
http://www.blogjava.net/dreamstone/archive/2006/11/08/79791.html
jdk1.4 和jdk1.5中的stringbuffer的不同
http://www.blogjava.net/dreamstone/archive/2006/10/17/75608.html
jdk1.5中泛型的实现原理
http://www.blogjava.net/dreamstone/archive/2006/11/09/80280.html
jdk1.5 annotation

jdk泛型中的问号(?)的用途 (泛型集合类的作为参数的时候的继承问题)

jdk6的新特性之十二:脚本语言支持(scripting) (转载)

使用simpledateformat必须注意的问题

jdk1.5 泛型之外的其它新特性



dreamstone 2007-07-31 22:14
]]>
jdk1.5 中提供的新集合类queuehttp://www.blogjava.net/dreamstone/archive/2007/07/28/133003.htmldreamstonedreamstonesat, 28 jul 2007 06:31:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/07/28/133003.htmlhttp://www.blogjava.net/dreamstone/comments/133003.htmlhttp://www.blogjava.net/dreamstone/archive/2007/07/28/133003.html#feedback2http://www.blogjava.net/dreamstone/comments/commentrss/133003.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/133003.html偶然发现,jdk1.5后加入了新的集合类queue,在这之前如果想使用一个队列的数据结构,大多数是用list来模拟出来的,加入的时候用add()
弹出的时候自己手动remove前边的对象。但queue本身就是个队列,不需要再多这些步骤。下边是个简单的用法,对比stack的演示代码:
import java.util.linkedlist;
import java.util.queue;
import java.util.stack;


public class testqueue {
    
public static void main(string[] args) {
        
//队列是先进先出
        queue<string> q = new linkedlist<string>();
        
//这里offer和add类似
        q.offer("a");
        q.offer(
"b");
        q.offer(
"c");
        system.out.println(q);
        
//队列头部弹出
        q.poll();
        system.out.println(q);
        
//栈你是后进先出
        stack<string> s = new stack<string>();
        s.add(
"a");
        s.add(
"b");
        
//这里push等同add
        s.push("c");
        system.out.println(s);
        
//栈顶弹出
        s.pop();
        system.out.println(s);
        
//只是看一下栈顶的值,并不移除
        s.peek();
        system.out.println(s);
    }

}



dreamstone 2007-07-28 14:31
]]>
一个有趣的现象http://www.blogjava.net/dreamstone/archive/2007/07/05/128219.htmldreamstonedreamstonewed, 04 jul 2007 17:00:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/07/05/128219.htmlhttp://www.blogjava.net/dreamstone/comments/128219.htmlhttp://www.blogjava.net/dreamstone/archive/2007/07/05/128219.html#feedback14http://www.blogjava.net/dreamstone/comments/commentrss/128219.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/128219.html1,编写一个接口
package iface;
public interface myint {

}

把这个接口做成一个jar包iface.jar
2,编写一个类
package mylib;

import iface.myint;

public class myjar {
    
public static boolean ismyinterface(){
        
return null==getmyint();
    }

    
public static myint getmyint(){
        
return null;
    }

}

同样把这个类做成一个jar文件mylib.jar
3,新建一个工程,加入两个jar包。然后写一个测试类
import mylib.myjar;

public class test {
    
public static void main(string[] args) {
        system.out.println(myjar.ismyinterface());
        system.out.println(myjar.getmyint());
    }

}

4,运行得到结果:
true
null
到这里一切正常,下边开始不同的地方。
5,把iface.jar从工程中移走,这时测试类出现编译错误,这是因为myjar.getmyint()要返回一个myint类型的对象,而myint接口不存在。
删掉测试类的第二个println 变成这样
import mylib.myjar;

public class test {
    
public static void main(string[] args) {
        system.out.println(myjar.ismyinterface());
    }

}

重新编译,通过。运行得到结果 :
true
8,分析:移走接口后导致测试类第二行println()中的代码myjar.getmyint()编译不过去,不能之行,是因为找不到myint接口
但第一行能编译,且能执行,而第一行的代码在内部其实是调用第二行的,但依然能执行成功。
这就说同样的函数:myjar.getmyint()外部调用不能执行,通过一个函数转一下就能执行,呵呵,挺有趣。
感觉应该是虚拟机的实现机制方面的原因。详细情形还没有确认。

dreamstone 2007-07-05 01:00
]]>
走近ejb jboss 下 hello worldhttp://www.blogjava.net/dreamstone/archive/2007/03/05/101979.htmldreamstonedreamstonemon, 05 mar 2007 12:02:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/03/05/101979.htmlhttp://www.blogjava.net/dreamstone/comments/101979.htmlhttp://www.blogjava.net/dreamstone/archive/2007/03/05/101979.html#feedback3http://www.blogjava.net/dreamstone/comments/commentrss/101979.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/101979.html
首先是hello world的代码,很简单就几个接口
1,远程接口
package eg;

import java.rmi.remoteexception;
import javax.ejb.ejbobject;

public interface hello extends ejbobject {
    
public string hello() throws remoteexception;
}

2,企业bean
package eg;

import java.rmi.remoteexception;

import javax.ejb.ejbexception;
import javax.ejb.sessionbean;
import javax.ejb.sessioncontext;

public class hellobean implements sessionbean {
    
private sessioncontext ctx;

    
public void ejbactivate() throws ejbexception, remoteexception {
        
// todo auto-generated method stub

    }


    
public void ejbpassivate() throws ejbexception, remoteexception {
        
// todo auto-generated method stub

    }


    
public void ejbcreate() throws javax.ejb.createexception {
        
    }


    
public void ejbremove() throws ejbexception, remoteexception {
        
// todo auto-generated method stub

    }


    
public void setsessioncontext(sessioncontext ctx) throws ejbexception,
            remoteexception 
{
        
this.ctx = ctx;
    }


    
public string hello() {
        system.out.println(
"hello");
        
return "hello world";
    }


}

3,home接口
package eg;

import java.rmi.remoteexception;

import javax.ejb.createexception;
import javax.ejb.ejbhome;

public interface hellohome extends ejbhome {
    hello create() 
throws remoteexception,createexception;
}

4,用来调用的client类
package eg;

import java.util.hashtable;

import javax.naming.context;
import javax.naming.initialcontext;
import javax.rmi.portableremoteobject;

public class helloclient {

    
public static void main(string[] args) throws exception {
        hashtable env 
= new hashtable();
        env.put(context.initial_context_factory,
"org.jnp.interfaces.namingcontextfactory");
        env.put(context.provider_url, 
"localhost:1099");
        env.put(
"java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
         
        context ctx 
= new initialcontext(env);
        object obj 
= ctx.lookup("hello");
        hellohome home 
= (hellohome) portableremoteobject.narrow(obj,
                hellohome.
class);
        hello hello 
= home.create();
        system.out.println(hello.hello());
        hello.remove();

    }


}

ejb-jar.xml文件
xml version="1.0" encoding="utf-8"?>
doctype ejb-jar public "-//sun microsystems, inc.//dtd enterprise javabeans 2.0//en" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<description>this is ejb helloworlddescription>
<display-name>hellobeandisplay-name>
<enterprise-beans>
    
<session>
        
<display-name>hellodisplay-name>
        
<ejb-name>helloejb-name>
        
<home>eg.hellohomehome>
        
<remote>eg.helloremote>
          
        
<ejb-class>eg.hellobeanejb-class>
        
<session-type>statelesssession-type>
        
<transaction-type>containertransaction-type>
    
session>
enterprise-beans>
ejb-jar>
上边代码下载链接
疑问一:细心的人会发现我的ejb-jar.xml中注释掉了两行,这两行对应两个本地接口
package eg;

import javax.ejb.ejblocalobject;

public interface hellolocal extends ejblocalobject {
    
public string hello();
}

package eg;

import javax.ejb.createexception;
import javax.ejb.ejblocalhome;

public interface hellolocalhome extends ejblocalhome {
    hellolocal create() 
throws createexception;
}

这两个接口是用来本地调用的,这个解释明白它的意思,但还是不知道如何才能实现这个本地调用,了解的人给个提示,如何才能在代码中模拟出使用到这两个接口的情况。或者说模拟出使用本地接口和远程接口的区别?
疑问二:为什么企业bean类不实现组建接口(既不实现远程接口也不实现本地接口),毕竟组建接口定义了企业bean所有的业务方法,如果实现了,可以保证编译期check
答案:这个答案是<<精通ejb>>一书中给出的解释:

理由一:组件接口继承于sun定义的标准接口,比如javax.ejb.ejbobject和javax.ejb.ejblocalobject。这些超级接口为客户定义了其它方法。因此如果企业bean类实现组件接口,则企业bean类将需要为这些方法提供空的实现,显然,在企业bean类中不需要这些方法

理由二:
假定企业bean (a)需要调用其它企业bean(b)的方法,并且,如果需要传递当前企业bean(a)的引用给b的方法(类似于java中的this参数)。在ejb中,如何传递和处理这种情形?
请记住,所有的ejb客户都是调用ejb对象而不是企业bean类实例本身,因此如果a调用b,开发者必须将a的ejb对象引用(而不是对a的引用)传递给b,b将操作a的ejb对象,而不是a中企业bean实例本身,对于a而言,b是它的客户,同所有的ejb客户一样,必须通过ejb对象调用企业bean类实例。
而且如果企业bean类实现了ejb组件的远程接口将存在潜在的危险,开发者可能不小心将企业bean类的实例本身的引用传递,而不是ejb对象的引用,犹豫企业bean类同ejb对象实现了同一接口,似的开发者能够使用this参数,而实现对企业bean本身的引用。这太不可思议了。
看完这段解释,朦胧的懂了为什么不这么做,但是对这么作带来的后果还是不是很清楚,有经验的给说说.谢谢.

另外部署不说了,看这里吧

如果调用client的时候抛出这个异常:
exception in thread "main" javax.naming.noinitialcontextexception: cannot instantiate class: org.jnp.interfaces.namingcontextfactory [root exception is java.lang.classnotfoundexception: org.jnp.interfaces.namingcontextfactory]
 at javax.naming.spi.namingmanager.getinitialcontext(namingmanager.java:657)
 at javax.naming.initialcontext.getdefaultinitctx(initialcontext.java:247)
 at javax.naming.initialcontext.init(initialcontext.java:223)
 at javax.naming.initialcontext.(initialcontext.java:197)
 at eg.helloclient.main(helloclient.java:17)
caused by: java.lang.classnotfoundexception: org.jnp.interfaces.namingcontextfactory
 at java.net.urlclassloader$1.run(urlclassloader.java:200)
 at java.security.accesscontroller.doprivileged(native method)
 at java.net.urlclassloader.findclass(urlclassloader.java:188)
 at java.lang.classloader.loadclass(classloader.java:306)
 at sun.misc.launcher$appclassloader.loadclass(launcher.java:268)
 at java.lang.classloader.loadclass(classloader.java:251)
 at java.lang.classloader.loadclassinternal(classloader.java:319)
 at java.lang.class.forname0(native method)
 at java.lang.class.forname(class.java:242)
 at com.sun.naming.internal.versionhelper12.loadclass(versionhelper12.java:42)
 at javax.naming.spi.namingmanager.getinitialcontext(namingmanager.java:654)
 ... 4 more

把jboss client目录下的一些类库加进来就好了

最后提供一个简单的做法:
eclipse myeclipse
新建ejb工程-->新建sessionbean,名字叫hellobean ,更改里边的业务方法
然后选择工程的属性-->myeclipse xdoclet -->add standard -->standard ejb -->确定
右键工程-->myeclipse-->run xdoclet,看看是不是和上边的差不多,什么都有了,不过他的结构更好一点,借助工具能更快捷的完成任务。不过建议初学者还是自己手动来比较好。
借助eclipse myeclipse 部署调试也会变的方便.





dreamstone 2007-03-05 20:02
]]>
对象与实例的区别http://www.blogjava.net/dreamstone/archive/2007/03/04/101733.htmldreamstonedreamstonesun, 04 mar 2007 05:47:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/03/04/101733.htmlhttp://www.blogjava.net/dreamstone/comments/101733.htmlhttp://www.blogjava.net/dreamstone/archive/2007/03/04/101733.html#feedback7http://www.blogjava.net/dreamstone/comments/commentrss/101733.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/101733.html>一书,书中对这两个的概念明显是有些区别的,于是开始搜索一下。找了很多地方都没有答案,同时发现这个问题也不只是我一个人的问题,很多地方都在讨论。例如这些地方



而且很多地方有对象实例  instance of object 的写法。真是让人迷惑啊。
查了一些资料,经过分析,现在觉得这个结论还是比较容易让人理解:
类-->对象-->实例
人类是类
某个人是对象
你是实例
实例本身也是对象。

表现出来是这样的
string 类
string str   str是对象
string str = "abc";  "abc"是实例,也是对象.
这样也能解释instance of object这种说法  str的实例是"abc"

暂时先这么理解,希望有人能提出更好的理解方法和见解。。。



dreamstone 2007-03-04 13:47 发表评论
]]>
推荐给大家一个很有趣的话题: javaeye上的"奇技淫巧"http://www.blogjava.net/dreamstone/archive/2007/02/27/100950.htmldreamstonedreamstonetue, 27 feb 2007 08:34:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/27/100950.htmlhttp://www.blogjava.net/dreamstone/comments/100950.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/27/100950.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/100950.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/100950.html>,链接在这里

文中就一个问题提出了各种各样的思路,很多人给出了自己的观点,是个对人有启发的话题。
下边是我想出的一个思路,就是利用jdk1.6的script支持,也算一个办法吧。建议看完了上边的讨论再看
我下边的内容,要不可能不知道是什么意思。
    static double getoverall(details[] arr ,string method){
        
double sum = 0;
        
try{
            scriptenginemanager factory 
= new scriptenginemanager();
            scriptengine engine 
= factory.getenginebyname("javascript");
            
for(int i=0;i<arr.length;i){
                engine.put(
"obj", arr[i]);
                object obj 
= engine.eval("obj."method);
                
if (obj instanceof double){
                    sum 
= ((double)obj).doublevalue();
                }

            }

        }
catch(exception e){
            e.printstacktrace();
        }

        
return sum;
    }

    
    
static double getoverallbalance(details[] arr) {
        
return getoverall(arr,"getbalance()");
    }


    
static double getoverallfixed(details[] arr) {
        
return getoverall(arr,"getfixed()");
    }

    ..    
最终的实现就是上边的样子,利用script我们能实现通过函数名来调用函数,其实内部和动态代理,反射
 的效果是一样的。不过就是写起来简单,学习成本低。因为用java的人大部分是了解javascript的。

dreamstone 2007-02-27 16:34
]]>
clone和new哪个更快http://www.blogjava.net/dreamstone/archive/2007/02/26/100761.htmldreamstonedreamstonemon, 26 feb 2007 08:11:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/26/100761.htmlhttp://www.blogjava.net/dreamstone/comments/100761.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/26/100761.html#feedback6http://www.blogjava.net/dreamstone/comments/commentrss/100761.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/100761.html阅读全文

dreamstone 2007-02-26 16:11
]]>
使用simpledateformat必须注意的问题http://www.blogjava.net/dreamstone/archive/2007/02/26/100756.htmldreamstonedreamstonemon, 26 feb 2007 08:03:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/26/100756.htmlhttp://www.blogjava.net/dreamstone/comments/100756.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/26/100756.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/100756.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/100756.html
public class testdateformat{
     
private simpledateformat sdf = new simpledateformat("yyyy-mm-dd");
     
public void method1(){
         sdf.format(
new date());
     }

     
public void method2(){
         sdf.format(
new date());
     }

 )
为了渐少new 的次数而把simpledateformat做成成员或者静态成员,但这样的做法是隐含着错误的,是不
 安全的。如下给出证明:
 import java.text.simpledateformat;
import java.util.date;
import java.util.hashmap;
import java.util.map;


public class testdateformat{
    
private simpledateformat sdf ;
    
public static void main(string[] args) {
        simpledateformat sdf
= new simpledateformat("yyyy-mm-dd");
        date date1 
= new date();
        date date2 
= new date(date1.gettime()1000*60*60*24);
        system.out.println(date1);
        system.out.println(date2);
        thread thread1 
= new thread(new thread1(sdf,date1));
        thread1.start();
        thread thread2 
= new thread(new thread2(sdf,date2));
        thread2.start();
    }

    
}

class thread1 implements runnable{
    
private simpledateformat sdf;
    
private date date;
    
public thread1(simpledateformat sdf,date date){
        
this.sdf = sdf;
        
this.date = date;
    }

    
public void run() {
        
for(;;){
            string strdate 
= sdf.format(date);
            
if("2007-02-27".equals(strdate)){
                system.err.println(
"error: date1="strdate);
                system.exit(
0);
            }

        }

    }

}

class thread2 implements runnable{
    
private simpledateformat sdf;
    
private date date;
    
public thread2(simpledateformat sdf,date date){
        
this.sdf = sdf;
        
this.date = date;
    }

    
public void run() {
        
for(;;){
            string strdate 
= sdf.format(date);
            
if("2007-02-26".equals(strdate)){
                system.err.println(
"error: date1="strdate);
                system.exit(
0);
            }

        }

    }

}

很快,基本几十次就会出现错误。错误原因:
 因为simpledateformat处理复杂,jdk的实现中使用了成员变量来传递参数,这个就造成在多线程的时候
 会出现错误。上边的程序证明了这个问题。
 
 再看看simpledateformat的jdk 的source,有这么一段注释,说明它不是线程安全的。
 date formats are not synchronized.
 * it is recommended to create separate format instances for each thread.
 * if multiple threads access a format concurrently, it must be synchronized
 
 继续看看sun自己的网站上。在sun的bug database中
 bug id:  4228335  讲的就是这个问题。
 simpledateformat is not threadsafe (one more try)
 其中有这么几段话值得关注:
 1,
 aside from the obvious, there are two reasons why this fix should be made:
- the documentation for dateformat states that a dateformat object should be used
multiple times, implying that construction is expensive.  furthermore,
 no mention is made of simpledateformat's lack of thread-safety. 
 since for most applications the date formats remain constant,
 it is very easy to conclude that dateformats should be application globals.
 but simpledateformat produces incorrect results when used in this way.
- bug 4101500, a similar problem with numberformat, was fixed.

建议修改这个问题,而且numberformat已经修改,解决了这个问题。简单测试了一下numberformat确是不出错

2,
use of a cloned calendar object in format(), as suggested in jdc comments,
slows down the execution 30-50%. and it potentially affects footprint because
of cloned objects. another approach would be to have a calendar instance per
thread. however, this approach will cause problems at the api semantic level due
to the get/setcalendar methods.
这一段解释了为什么没修改这个问题,一个是保持api不变,另一个是因为clone比new慢,会损失效率
关于clone与new的快慢,看这里。

其它的还有好多,感兴趣的自己看看吧。

结论:每次使用它的时候现new,或者用一个同步函数把new好的包装起来使用吧。



dreamstone 2007-02-26 16:03
]]>
jdk6的新特性之十二:脚本语言支持(scripting) (转载)http://www.blogjava.net/dreamstone/archive/2007/02/25/100576.htmldreamstonedreamstonesun, 25 feb 2007 06:59:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/25/100576.htmlhttp://www.blogjava.net/dreamstone/comments/100576.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/25/100576.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/100576.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/100576.htmlhttp://blog.csdn.net/chinajash/archive/2007/01/23/1491570.aspx
jdk6增加了对脚本语言的支持(),原理上是将脚本语言编译成bytecode,这样脚本语言也能享用java平台的诸多优势,包括可移植性,安全等,另外,由于现在是编译成bytecode后再执行,所以比原来边解释边执行效率要高很多。加入对脚本语言的支持后,对java语言也提供了以下好处。
1、许多脚本语言都有动态特性,比如,你不需要用一个变量之前先声明它,你可以用一个变量存放完全不同类型的对象,你不需要做强制类型转换,因为转换都是自动的。现在java语言也可以通过对脚本语言的支持间接获得这种灵活性。
2、 可以用脚本语言快速开发产品原型,因为现在可以edit-run,而无需edit-compile-run,当然,因为java有非常好的ide支持,我 们完全可以在ide里面编辑源文件,然后点击运行(隐含编译),以此达到快速开发原型的目的,所以这点好处基本上可以忽略。
3、通过引入脚本语言可以轻松实现java应用程序的扩展和自定义,我们可以把原来分布在在java应用程序中的配置逻辑,数学表达式和业务规则提取出来,转用javascript来处理。

sun的jdk6实现包含了一个基于的 脚本语言引擎,支持javascript,这并不是说明jdk6只支持javascript,任何第三方都可以自己实现一个jsr-223兼容的脚本引擎 使得jdk6支持别的脚本语言,比如,你想让jdk6支持ruby,那你可以自己按照jsr 223的规范实现一个ruby的脚本引擎类,具体一点,你需要实现javax.script.scriptengine(简单起见,可以继承javax.script.abstractscriptengine)和javax.script.scriptenginefactory两个接口。当然,在你实现自己的脚本语言引擎之前,先到 这里看看是不是有人已经帮你做了工作,这样你就可以直接拿来用就行。

scripting api

scripting api是用于在java里面编写脚本语言程序的api, 在javax.script中可以找到scripting api,我们就是用这个api来编写javascript程序,这个包里面有一个scriptenginemanager类,它是使用scripting api的入口,scriptenginemanager可以通过jar服务发现(service discovery)机制寻找合适的脚本引擎类(scriptengine),使用scripting api的最简单方式只需下面三步
1、创建一个
scriptenginemanager对象
2、通过scriptenginemanager获得scriptengine对象
3、用scriptengineeval方法执行脚本

下面是一个hello world程序
/**
* @author chinajash
*/
public class helloscript {
public static void main(string[] args) throws exception {
scriptenginemanager factory = new scriptenginemanager();//step 1
scriptengine engine = factory.getenginebyname("javascript");//step 2
engine.eval("print('hello, scripting')");//step 3
}
}
运行上面程序,控制台会输出
hello, scripting
上面这个简单的scripting程序演示了如何在java里面运行脚本语言,除此之外,我们还可以利用scripting api实现以下功能
1、暴露java对象为脚本语言的全局变量
2、在java中调用脚本语言的方法
3、脚本语言可以实现java的接口
4、脚本语言可以像java一样使用jdk平台下的类
下面的类演示了以上4种功能


package scripting;


import java.io.file;
import javax.script.invocable;
import javax.script.scriptengine;
import javax.script.scriptenginemanager;
import javax.script.scriptexception;


/**
* @author chinajash
*/
public class scriptingapitester {
public static void main(string[] args) throws exception {
scriptenginemanager manager = new scriptenginemanager();
scriptengine engine = manager.getenginebyname("javascript");
testscriptvariables(engine);//演示如何暴露java对象为脚本语言的全局变量
testinvokescriptmethod(engine);//演示如何在java中调用脚本语言的方法
testscriptinterface(engine);//演示脚本语言如何实现java的接口
testusingjdkclasses(engine);//演示脚本语言如何使用jdk平台下的类
}

public static void testscriptvariables(scriptengine engine) throws scriptexception{
file file = new file("test.txt");
engine.put("f", file);
engine.eval("println('total space:' f.gettotalspace())");
}

public static void testinvokescriptmethod(scriptengine engine) throws exception{
string script = "function hello(name) { return 'hello,' name;}";
engine.eval(script);
invocable inv = (invocable) engine;
string res = (string)inv.invokefunction("hello", "scripting" );
system.out.println("res:" res);
}

public static void testscriptinterface(scriptengine engine) throws scriptexception{
string script = "var obj = new object(); obj.run = function() { println('run method called'); }";
engine.eval(script);
object obj = engine.get("obj");
invocable inv = (invocable) engine;
runnable r = inv.getinterface(obj,runnable.class);
thread th = new thread(r);
th.start();
}

public static void testusingjdkclasses(scriptengine engine) throws exception{
//packages是脚本语言里的一个全局变量,专用于访问jdk的package
string js = "function doswing(t){var f=new packages.javax.swing.jframe(t);f.setsize(400,300);f.setvisible(true);}";
engine.eval(js);
invocable inv = (invocable) engine;
inv.invokefunction("doswing", "scripting swing" );
}
}

scripting tool


sun提供的jdk6中有一个命令行工具——jrunscript,你可以在/bin下面找到这个工具,jrunscript是一个脚本语言的解释程序,它独立于脚本语言,但默认是用javascript,我们可以用jrunscript来测试自己写的脚本语言是否正确,下面是一个在命令行运行jrunscript的简单例子
jrunscript
js>println("hello,jrunscript");
hello,jrunscript
js>9*8
72.0
js>

dreamstone 2007-02-25 14:59
]]>
jdk1.5 annotationhttp://www.blogjava.net/dreamstone/archive/2007/02/25/100541.htmldreamstonedreamstonesun, 25 feb 2007 02:50:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/25/100541.htmlhttp://www.blogjava.net/dreamstone/comments/100541.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/25/100541.html#feedback3http://www.blogjava.net/dreamstone/comments/commentrss/100541.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/100541.html阅读全文

dreamstone 2007-02-25 10:50
]]>
effective java 46-57http://www.blogjava.net/dreamstone/archive/2007/02/12/99552.htmldreamstonedreamstonemon, 12 feb 2007 14:51:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/12/99552.htmlhttp://www.blogjava.net/dreamstone/comments/99552.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/12/99552.html#feedback1http://www.blogjava.net/dreamstone/comments/commentrss/99552.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99552.html因为有些被使用比较少,只简单列举了一下
四十六、努力使失败保持原子性
1,一般而言,一个失败的方法调用应该使对象保持"它在被调用之前的状态",具有这种属性的方法被称为具有
失败原子性。
四十七、不要忽略异常
例如这样的代码是错误的
try{
 //.....
}catch(someexception e){
}
四十八、对共享可变数据要同步访问
注:java的双重检查模式并不保证一定正确
四十九、避免过多的同步
1,为了避免死锁的危险,在一个被同步的方法或者代码中,永远不要放弃对客户的控制。
2,通常同步区域内应该做尽可能少的工作
五十、永远不要在循环外部调用wait
五十一、不要依赖于线程调度器,因为这可能造成不可移植性
1,不要使用thread.yield来修正程序,因为在不同的jdk中,不能移植
2,线程优先级是java平台上最不可移植的特征了。
3,对大多数程序员来说,thread.yield的唯一用途是在测试期间人为的增加一个程序的并发行。
五十二、线程安全性的文档
一个类为了被多个线程安全的使用,必须在文档中姓储的说明它所支持的线程安全级别。
1,非可变。
2,线程安全的。
3,有条件的线程安全
4,线程兼容的
5,线程对立的。
五十三、避免使用线程组
五十四、谨慎的实现serializable
五十五、考虑使用自定义的序列化形式
五十六、保护的编写readobject方法
五十七、必要时提供一个readresolve方法


dreamstone 2007-02-12 22:51
]]>
在一个js文件中引入另一个js文件http://www.blogjava.net/dreamstone/archive/2007/02/12/99480.htmldreamstonedreamstonemon, 12 feb 2007 09:00:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/12/99480.htmlhttp://www.blogjava.net/dreamstone/comments/99480.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/12/99480.html#feedback4http://www.blogjava.net/dreamstone/comments/commentrss/99480.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99480.html
function alertinone(){
    alert(
'in one');
}

第二个js文件,文件名two.js,内容如下
//注意这个地方
document.write("");
function alertintwo()
{
    alert(
"in two");
}

调用的html文件,只需要引入two.js,就能引入one.js了
<script language='javascript' src="two.js">script>
<script language="javascript">
    alertinone();
    alertintwo();
script>

应用场合:
在平时开发的时候,我们都会有一些js文件的积累,大部份场合是不需要重新写的。
但我们会把js文件的功能尽可能的细分,以便引入的时候不引入无用的代码,但这样造成的问题是在jsp文件
中要写大量的这样的句子

如果你有100个jsp文件,都用到10个js文件,这个时候每个jsp文件写10句引入,这个是个麻烦的事情
现在,通过js文件的引入可以解决这个问题。定义一个js文件用来引入一些组合,jsp文件则只需要引入1个
这样的js文件就可以了。js代码少了100*99行

另外引入的js文件时候最好加上charset,特别是你的js文件有非英文,数字的时候。
事例的代码下载:



dreamstone 2007-02-12 17:00 发表评论
]]>
开启一个新的问题(关于声明变量的性能问题) (问题结束)http://www.blogjava.net/dreamstone/archive/2007/02/11/99207.htmldreamstonedreamstonesat, 10 feb 2007 19:26:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/11/99207.htmlhttp://www.blogjava.net/dreamstone/comments/99207.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/11/99207.html#feedback27http://www.blogjava.net/dreamstone/comments/commentrss/99207.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99207.html
for (int i=0;i<n;i){
    string str 
= //
}

string str 
= null;
for(int i=0;i<n;i){
    str 
= //
}



在印象中一直认为方法二的性能好于方法一,但是差距应该很小。但因为一位别人的回文说方法一极大的影响了性能,所以想写个例子证明一下相差很小。例子如下:
public class testot {
    
public static void main(string[] args) {
        
long n=1000000000;
        
long start = system.currenttimemillis();
        test2(n);
        
long end = system.currenttimemillis();
        system.out.println(end
-start);
    }

    
public static void test1(long n){
        
for (int i=0;i<n;i){
            string str 
= "";
        }

    }

    
public static void test2(long n){
        string str 
= null;
        
for (int i=0;i<n;i){
            str 
= "";
        }

    }

}
测试的结果是当n=10亿次的时候差距是1秒,所以说差距应该是很小的,符合我原始的记忆,但是另一个问题来了,测试的结果是
方法一:3300毫秒左右
方法二:4300毫秒左右
结果刚好相反,于是更改方法

public class testot {
    
public static void main(string[] args) {
        
long n=1000000000;
        
long start = system.currenttimemillis();
        test1(n);
        
long end = system.currenttimemillis();
        system.out.println(end
-start);
    }

    
public static void test1(long n){
        
for (int i=0;i<n;i){
            string str 
= null;
        }

    }

    
public static void test2(long n){
        string str 
= null;
        
for (int i=0;i<n;i){
        }

    }

}

结果依旧。

没办法,取得字节码,对比
public class testot extends java.lang.object{
public testot();
  code:
   
0:   aload_0
   
1:   invokespecial   #8//method java/lang/object."":()v
   4:   return

public static void test1(int);
  code:
   
0:   iconst_0
   
1:   istore_1
   
2:   goto    10   
   
5:   aconst_null
   
6:   astore_2
   
7:   iinc    11
   
10:  iload_1   
   
11:  iload_0
   
12:  if_icmplt       5
   
15:  return

public static void test2(int);
  code:
   
0:   aconst_null
   
1:   astore_1
   
2:   iconst_0
   
3:   istore_2
   
4:   goto    10   
   
7:   iinc    21
   
10:  iload_2   
   
11:  iload_0  
   
12:  if_icmplt       7
   
15:  return
}

结果是感觉还是应该是方法二快,那为什么反而方法一快了1秒左右呢?
不得而知,现在我个人猜测的想法是可能有两种情况:
1,jls的底层定义决定的,有什么特殊的优化?
2,因为方法二比方法一虽然少了在循环中的部分,但是引用的声明周期反而是更长了,是否因为引用存在造成了方法二的栈操作消耗了大部分时间?
猜想一有待于jls文档的查阅,我会在有空的时候查询,猜想二正在想办法证明。
看文章的朋友,如果谁了解麻烦指点一下,是我的测试方法写的有问题,还是别的原因,谢谢。

最后:问题已经基本了解了原因,见回复中的讨论,谢谢--daydream 的帮忙。

dreamstone 2007-02-11 03:26
]]>
effective java 31-45http://www.blogjava.net/dreamstone/archive/2007/02/11/99201.htmldreamstonedreamstonesat, 10 feb 2007 17:32:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/11/99201.htmlhttp://www.blogjava.net/dreamstone/comments/99201.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/11/99201.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/99201.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99201.html三十一、如果要求精确的答案,尽量避免使用float 和double,货币尤其不合适。可以使用bigdecimal代替
三十二、如果其它类型更适合,尽量避免使用字符串
1,字符串不能替代其它的值类型
2,字符串不适合代替枚举类型
3,字符串不适合代替聚集类型
4,字符串也不是和代替能力表
因为有些时候,使用字符串会大大降低性能
三十三、了解字符串连接的性能
说明:使用stringbuffer代替 来连接字符串
三十四、通过接口来引用对象,这能让你的程序更加灵活
三十五、接口优先于反射。
使用反射会带来很多问题,例如:
1,不能编译期发现错误
2,代码混乱
3,调试困难
4,性能损失。
除非必须,否则不使用反射
三十六、谨慎的使用本地方法jni
三十七、谨慎的进行优化,有三条优化格言:
1,很多计算上的过失都被归咎于效率原因(没有获得必要的效率),而不是其它的原因--甚至包括盲目的作傻事.   ---william a.wulf [wulf72]
2,不要去计较一些小的效率上的得失,在97%的情况下,不成熟的优化是一切问题的根源。
            ------donald e.knuth[knuth74]
3,在优化方面要遵守两个原则:
规则一:不要做优化
规则二:还是不要做优化--也就是说,在你还没有绝对清晰的未优化方案前,请不要优化。
            -----m.a.jackson[jackson75]
每次试图做优化之前和之后请对性能进行测试
三十八:遵守普遍接受的命名规则
三十九:值针对不正常的条件才使用异常,也就是说不要在正常的情况下使用异常来控制流程,活着解决某些已知的问题。因为会大量的损失性能
四十、对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常
详细:java提供了三种可抛出结构,checked exception,  run-time exception , error
什么时候使用什么很容易让人混淆,下边是一个简单的区分原则
1,如果期望调用者能够恢复,那么对于这样的条件应该使用被检查异常
2,你所实现的所有未检查的抛出结构都是run time exception ,而不是error
四十一:避免不必要的使用被检查异常
四十二:尽可能的使用标准异常,例如illegalargumentexception ,nullpointerexception ,indexoutofboundsexception等等
四十三:抛出异常要适合于相应的抽象。
高层实现应该捕获异常,同时抛出一个可以按照高层抽象解释的异常(业务逻辑上符合高层逻辑),这种做法叫做异常转译
四十四:每个方法抛出的异常都应改有文档
四十五:在细节消息中包含失败-捕获信息
详细:在异常字符串中应包含有用的信息,例如indexoutofboundsexception异常的细节消息应该包括下界、上界以及没有落在其中的实际下标

dreamstone 2007-02-11 01:32
]]>
effective java 27-30http://www.blogjava.net/dreamstone/archive/2007/02/11/99200.htmldreamstonedreamstonesat, 10 feb 2007 17:03:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/11/99200.htmlhttp://www.blogjava.net/dreamstone/comments/99200.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/11/99200.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/99200.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99200.html原因:返回null会造成使用者每次使用的时候都要作一次判断,但有人会说返回一个零长度数组会产生new的开销,不如null性能好。这个不是一定的,因为我们可以这样来作
private final static cheese[]  null_chese_array = new cheese[0];
每次需要的时候返回这个数组就好了。

二十八、为所有的导出api元素编写文档注释
二十九、使一个局部变量的作用域最小化,最好的办法使在第一次使用的时候声明
1,几乎每一个局部变量的声明都应该包含一个初始化表达式,如果你还没有足够的信息来初始化那就推迟声明。
2,for循环优先于while循环,见下边的例子
//        for循环
        for(iterator ie = list.iterator();ie.hasnext()){
            dosomething(ie.next());
        }

//        while循环
        
        iterator ie1 
= list1.iterator();
        
while(ie1.hasnext()){
            dosomething(ie1.next());
        }

        iterator ie2 
= list2.iterator();
        
while(ie1.hasnext()){     //bug
            dosomething(ie2.next());
        }
这个问题源于复制粘贴,在编码的过程中复制粘贴几乎是不可避免的,使用for循环当你出错的时候可以在编译器发生错误,而使用while则不会发现。尽早发现错误总是好的。
三十、了解和使用库(产生随机数)
详细:如果你希望产生一个位于0-某个上界的随机数,大多数的人的写法如下
static random rnd = new random();
    
static int random(int n){
        
return math.abs(rnd.nextint())%n;
    }
这个方法存在三个缺点:
缺点一:
如果n是一个比较小的2的乘方 那么经过一段相当短的周期后它产生的随即数序列将会重复
缺点二:
如果n不是2的乘方,那么平均起来某些数比另外一些数出现的更为频繁,如果n比较大则这个问题更加显著如果产生100万范围内的随机数,你会发现数字几乎全部在0-666 666 ,前2/3的数字
缺点三:
在有些情况下会灾难性失败,返回一个落在范围之外的数字。原因是使用了math.abs来得到一个非负数。
如果nextint()返回 integer.min_value,那么abs后也会返回integer.min_value ,假设n不是2的乘方,那么取模操作符%将返回一个负数,这几乎肯定造成你的程序失败,而且这个失败很难重现。

为了编写一个避免上边三个缺点的random,你必须了解线性同于伪随机发生器、数论、和2的求补运算知识。不过jdk已经实现了一个现成的可以使用,那就是random.nextint(int)


dreamstone 2007-02-11 01:03
]]>
effective java 23-26http://www.blogjava.net/dreamstone/archive/2007/02/11/99199.htmldreamstonedreamstonesat, 10 feb 2007 16:29:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/11/99199.htmlhttp://www.blogjava.net/dreamstone/comments/99199.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/11/99199.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/99199.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99199.html如果函数对参数有要求,例如不接受null ,不接受负数等等,应该尽可能在函数的最开始给出校验,如果发现错误抛出异常
二十四、在需要的时候使用保护性拷贝
1,假设类的客户会尽一切手段来破坏这个类的约束条件,在这样的前提下,你必须保护性的设计程序。
2,实例
import java.util.date;

public final class period {
    
private final date start;
    
private final date end;
    
public period(date start,date end){
        
if (start.compareto(end)>0){
            
throw new illegalargumentexception(start"after"end);
        }

        
this.start = start;
        
this.end = end;
    }

    
//.other code
}

//这个函数看似没有问题,实际上存在着漏洞,如下使用方法
date start = new date();
date end 
= new date();
period p 
= new period(start,end);
//如果加上这句,检验就失效了。
end.setyear(78);

//为了对应这个问题,更改构造函数:

public period(date start,date end){
    
this.start = new date(start.gettime());
    
this.end = new date(end.gettime());
    
if (start.compareto(end)>0){
        
throw new illegalargumentexception(start"after"end);
    }

}
注意,拷贝要在检验之前进行
3,参数类型可以被不可信任方子类化的情形,清不要使用clone方法进行参数的保护化拷贝
二十五、谨慎的设计方法的原型
1,谨慎的选择方法的名字,一个好的方法名字可以让人很快记住
2,不要过于追求提供便利的方法,如果方法太多会增加使用者的学习负担,只有当一个操作被频繁使用的时候再添加一个对应的方法。
3,避免太长的参数列表,尽量让你的参数不大于三个
4,对于参数类型,优先使用接口,而不是类。
原因:如果使用接口,你可以随意的替换实现,或者同时存在多个实现。
使用类没有这个优势。
5,谨慎的使用函数对象(一个类中一堆静态函数)
二十六、谨慎的使用重载
1,实例
import java.util.arraylist;
import java.util.collection;
import java.util.hashmap;
import java.util.hashset;
import java.util.list;
import java.util.set;


public class collectionclassifier {
    
public static string classify(set s){
        
return "set";
    }

    
public static string classify(list s){
        
return "list";
    }

    
public static string classify(collection s){
        
return "unknow collection";
    }

    
    
public static void main(string[] args) {
        collection[] tests 
= new collection[]{
            
new hashset(),
            
new arraylist(),
            
new hashmap().values()
        }
;
        
for(int i=0;i<tests.length;i){
            system.out.println(classify(tests[i]));
        }

    }

}
结果是打印出三个unknown
这个程序的行为是违反直觉的,对弈重载方法的选择是静态的,而对于被改写的方法的选择是动态的

2,尽量不要使用两个参数数目相同的重载方法
如以下两个重载函数:
test1(string name,string value)
test1(string name,string[] value)
当你调用test1("name",null)的时候就出错了。

dreamstone 2007-02-11 00:29
]]>
jdk泛型中的问号(?)的用途 (泛型集合类的作为参数的时候的继承问题)http://www.blogjava.net/dreamstone/archive/2007/02/10/99195.htmldreamstonedreamstonesat, 10 feb 2007 15:46:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/10/99195.htmlhttp://www.blogjava.net/dreamstone/comments/99195.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/10/99195.html#feedback1http://www.blogjava.net/dreamstone/comments/commentrss/99195.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99195.html阅读全文

dreamstone 2007-02-10 23:46
]]>
利用反射取得泛型信息http://www.blogjava.net/dreamstone/archive/2007/02/10/99194.htmldreamstonedreamstonesat, 10 feb 2007 15:24:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/10/99194.htmlhttp://www.blogjava.net/dreamstone/comments/99194.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/10/99194.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/99194.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/99194.html阅读全文

dreamstone 2007-02-10 23:24
]]>
effective java 14-22http://www.blogjava.net/dreamstone/archive/2007/02/01/97372.htmldreamstonedreamstonethu, 01 feb 2007 14:22:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/02/01/97372.htmlhttp://www.blogjava.net/dreamstone/comments/97372.htmlhttp://www.blogjava.net/dreamstone/archive/2007/02/01/97372.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/97372.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/97372.html十四:复合优于继承(think in java中有不少说明)
十五:要们专门为继承而设计,并给出文档说明,要么禁止继承
十六:接口优于抽象类(参考一下gof的设计模式)
十七:接口只是被定义类型,不要试图使用常量接口
十八:优先考虑静态成员类
说明:嵌套类有四种
静态成员类 , 非静态成员类,   匿名类,  局部类  除了第一种之外,其它三种都被称为内部类
1,静态成员类是一种最简单的嵌套类,最好把它看成一个普通类,只是碰巧被声明在另一个类内部而已,
它可以访问外围类的所有成员,包括那些生民为私有的成员。静态成员类是外围类的一个静态成员,也遵守同样的可访问性规则,如果它被声明为私有的,那么它只能在外围类内部可以访问。静态成员类的一个用法是公有的辅助类。例如hashmap的  static class entry

非静态成员类和静态成员类的区别主要是非静态成员类需要一个外围类实例的引用,如果你不需要访问外围类的实例的话,记得使用静态成员类。

匿名类被使用的相对多一些,但是大量的使用匿名类会让你的代码比较乱,作过gui开发的人多会有所感触。并且记住,尽可能的让你的匿名类短小。

局部类,局部类的使用是最少的,很少会使用到这个,如果用到记得使局部类尽可能的短小

对于c语言用户的部分
十九:用类代替结构
二十:用类层次代替联合
二十一:用类来代替enum,但是在jdk1.5的时候提供了enum的支持,有些东西不一样了
二十二:用类和接口代替函数指针

dreamstone 2007-02-01 22:22
]]>
effective java 8-13http://www.blogjava.net/dreamstone/archive/2007/01/31/97107.htmldreamstonedreamstonewed, 31 jan 2007 15:31:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/01/31/97107.htmlhttp://www.blogjava.net/dreamstone/comments/97107.htmlhttp://www.blogjava.net/dreamstone/archive/2007/01/31/97107.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/97107.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/97107.html原因:来自java.lang.object关于hashcode的规范
1,在一个应用执行期间,如果一个对象的equals方法比较所用到的信息没有修改的话,那么对该对象调用hashcode多次,比如如一的返回同一个数
2,如果两个对象的equals方法返回true,那么分别调用hashcode方法返回的值应该相等
3,在两个兑现的equals方法返回false时,尽可能的让hashcode方法返回的值不相等,提高散列表的性能
分析:如果改写了equals没有改写hashcode在使用map等集合类的时候会出现问题。
九:尽可能的改写tostring方法,并在显示内容中尽可能的包括令人感兴趣的信息。并且在注释中表示出你的意图。
十:谨慎的改写clone方法,改写前考虑浅拷贝和全拷贝
十一:考虑实现comparable接口,如果你的对象要排序,那么记得实现这个方法
十二:使类和成员的可访问能力最小化,
十三:支持非可变性
非可变性遵循以下的原则:
1,不提供任何改变对象的方法
2,保证没有可被子类改写的方法
3,保证所有的域都使final
4,使所有的域都成为私有的
5,保证任何可变组件互斥访问
6,非可变对象本质是线程安全的,不需要同步

dreamstone 2007-01-31 23:31
]]>
effective java 6-7http://www.blogjava.net/dreamstone/archive/2007/01/30/96797.htmldreamstonedreamstonetue, 30 jan 2007 14:09:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/01/30/96797.htmlhttp://www.blogjava.net/dreamstone/comments/96797.htmlhttp://www.blogjava.net/dreamstone/archive/2007/01/30/96797.html#feedback1http://www.blogjava.net/dreamstone/comments/commentrss/96797.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/96797.html原因:终结函数通常是不可预测的,一般情况下不要使用,使用的结果会带来很多问题,不稳定,性能差,移植问题等等。
分析原因:
1,从一个对象不可到达到它的终结函数被执行,这段时间是任意的,不确定的,所以时间关键的系统不应该使用终结函数。
2,及时的执行终结函数是垃圾回收算法决定的,这种算法在不同的jvm实现中会大相径庭,使用终结函数的结果就是导致移植性问题
3,如果执行终结函数的线程一直低于当前的线程的优先级,很可能造成占用大量内存,极端情况是出现outofmemoryerror
4,jsl不保证终结函数一定被执行,所以不要依赖终结函数来更新关键性的永久状态,例如数据库的永久锁
5,不要相信system.gc()    system.runfinalization这两个函数,它们只能提高终结函数的执行机会,并不保证一定执行。唯一保证一定执行的是system.runfinalizersonexit喝runtime.runfinalizersonexit()但这两个方法已经被声明不建议使用.
6,一个在终结函数中的一场不会打出任何信息
七:在改写equals方法的时候遵守通用约定
分析:
1,有些情况下不要随意改写equals
(1),一个类的每个实例本质上是唯一的,例如thread
(2),不管新一个类是否提供了“逻辑相等”的测试功能,例如java.util.random
(3),超类已经改写了equals,从超类继承过来的行为对于子类也是适合的 例如set从abstractset继承了equals
(4),一个类是私有的,或者是包级私有的,并且确定它的equals方法永远不会被调用
2, 通用的约定
自反性:  对于任意的引用值x ,x.equals(x)一定为true
对称性:  对于任意的引用值x,y  x.equals(y)返回true是 y.equals(x)也一定返回true
传递性:对于任意的引用值x,y,z  如果x.equals(y)返回true 并且y.equals(z)返回true 那么 x.equals(z)也一定是true
一致性:对于任意的x,y如果x,y没有被更改,调用任意多次x.equals(y)返回的结果应该一样。
非空性:对任意的引用x ,x.equals(null)一定返回false
3不要将equals声明中的object对象换成别的类型
4,不要让equals方法依赖不可靠资源




dreamstone 2007-01-30 22:09
]]>
effective java 1-5http://www.blogjava.net/dreamstone/archive/2007/01/28/96418.htmldreamstonedreamstonesun, 28 jan 2007 15:58:00 gmthttp://www.blogjava.net/dreamstone/archive/2007/01/28/96418.htmlhttp://www.blogjava.net/dreamstone/comments/96418.htmlhttp://www.blogjava.net/dreamstone/archive/2007/01/28/96418.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/96418.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/96418.html内容:静态工厂替代构造函数
例子:string.valueof()   getinstance()等
静态工厂方法优点:
1,可以随意起名字,更好的描述返回对象
2,每次调用的时候不一定要创建一个新对象
3,可以返回一个原返回类型的子类型对象
静态工厂方法的缺点:
1,如果类没有公有或者受保护的构造函数就不能被子类化
2,不符合规范,破坏规范。在api文档中不会被那么明确的标识出来。

第二条:使用私有构造函数强化单态
单态的模式大家都知道了,但是使用单态的时候记住要使用私有的构造函数。
原因很简单,如果不如此很难保证单态。只要new一下另一个对象就生成了

第三条:有些类是不能实例化的,如果你要做到这点,记得使用私有的构造函数。
例如:java.util.collections        java.lang.math 等

第四条:避免创造重复的对象
特别是这样的代码不要写: string str = new string("a string");
因为这样每次执行的时候都创建了一个"a string"对象。
可以写成这样:string str = "a string ";
另外顺便说一句,这个时候你再次定义string  str2 = "a string";会复用上边的字符串.

第五条:在有些情况下手动消除对象的引用
public class stack{
    
private object[] elements;
    
private int size = 0;
    
public stack(int initialcapacity){
        
this.elements = new object[initialcapacity];
    }

    
public void push(object e){
        ensurecapacity();
        elements[size
= e;
    }

    
public object pop(){
        
if (size == 0)
            
throw new emptystackexception();
        
return elements[--size];
    }

    
private void ensurecapacity(){
        
if (elements.length == size){
            object[] oldelements 
= elements;
            elements 
= new object[2*element.length1];
        system.arraycopy(oldelements,
0,elements,0,size);
        }

    }

}
如果这个stack先大量增长,然后收缩,然后在比较小的范围内使用,必定造成大量的不可回收的对象,造成内存泄漏.。
解决办法:改造一下pop()方法
public object pop(){
       
if(size = = 0)
           
throw new emptystackexception();
        object result 
= elements[--size];
                 //加上这一句
        elements.[size]
=null;
        
return result;
}


dreamstone 2007-01-28 23:58
]]>
静态分派,动态分派,多分派,单分派 -------------- visitor模式准备http://www.blogjava.net/dreamstone/archive/2006/12/20/88947.htmldreamstonedreamstonetue, 19 dec 2006 17:08:00 gmthttp://www.blogjava.net/dreamstone/archive/2006/12/20/88947.htmlhttp://www.blogjava.net/dreamstone/comments/88947.htmlhttp://www.blogjava.net/dreamstone/archive/2006/12/20/88947.html#feedback4http://www.blogjava.net/dreamstone/comments/commentrss/88947.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/88947.html1,定义:发生在编译时期,分派根据静态类型信息发生,重载就是静态分派
2,什么是静态类型:变量被声明时的类型是静态类型
      什么是动态类型:变量所引用的对象的真实类型
3,有两个类,blackcat ,whitecat都继承自cat
如下调用
class cat{}
class whitecat extends cat{}
class blackcat extends cat{}
public class person {
    
public void feed(cat cat){
        system.out.println(
"feed cat");
    }

    
public void feed(whitecat cat){
        system.out.println(
"feed whitecat");
    }

    
public void feed(blackcat cat){
        system.out.println(
"feed blackcat");
    }

    
public static void main(string[] args) {
        cat wc 
= new whitecat();
        cat bc 
= new blackcat();
        person p 
= new person();
        p.feed(wc);
        p.feed(bc);
    }


}

运行结果是:
feed cat
feed cat
这样的结果是因为重载是静态分派,在编译器执行的,取决于变量的声明类型,因为wc ,bc都是cat所以调用的都是feed(cat cat)的函数.
二,动态分派
1,定义:发生在运行期,动态分派,动态的置换掉某个方法。
还是上边类似的例子:
class cat{
    
public void eat(){
        system.out.println(
"cat eat");
    }

}

public class blackcat extends cat{
    
public void eat(){
        system.out.println(
"black cat eat");
    }

    
public static void main(string[] args){
        cat cat 
= new blackcat();
        cat.eat();
    }

}
这个时候的结果是:
black cat eat
这样的结果是因为在执行期发生了向下转型,就是动态分派了。

三,单分派:
1,定义:根据一个宗量的类型进行方法的选择
四,多分派:
1,定义:根据多于一个宗量的类型对方法的选择
2,说明:多分派其实是一系列的单分派组成的,区别的地方就是这些但分派不能分割。
3,c ,java都是动态单分派,静态多分派语言
多分派的语言有:clos  cecil

最后的部分:

看完本文,如果你对visitor模式有更多的兴趣,想了解更多请看如下几篇文章。
1,2,访问差异类型的集合类 ------------------------   visitor模式入门
3,4,重载overloading和覆写overriding哪个更早执行--   visitor帮助篇
虽然排列顺序是1,2,3,4但是我个人建议的学习方式是2,1,3,4因为这个顺序更方便一般人理解



dreamstone 2006-12-20 01:08
]]>
对(重载overloading和覆写overriding哪个更早执行-- visitor帮助篇)这篇文章的说明http://www.blogjava.net/dreamstone/archive/2006/12/20/88942.htmldreamstonedreamstonetue, 19 dec 2006 16:32:00 gmthttp://www.blogjava.net/dreamstone/archive/2006/12/20/88942.htmlhttp://www.blogjava.net/dreamstone/comments/88942.htmlhttp://www.blogjava.net/dreamstone/archive/2006/12/20/88942.html#feedback3http://www.blogjava.net/dreamstone/comments/commentrss/88942.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/88942.html这篇文章,下边gg_shily朋友和我的一些讨论,在gg_shily的激发下,我决定仔细研究一下这个问题,在我的很多文章中都用过的字节码,我想在这里也能帮助我 。下边是分析的结果,为了方便观看,我把里边的两个类稍微变动一下
 1package ov;
 2
 3public class child extends parent {
 4
 5    public void run(object o) {
 6        system.out.println(" in child  param:object ");
 7    }

 8
 9    public void run(string str) {
10        system.out.println(" in child   param:string ");
11    }

12
13    public static void main(string[] args) {
14        
15        string str = new string();
16        parent p = new child();
17        test(p,str);
18    }

19    public static void test(parent p,string str){
20        p.run(str);
21    }

22}
然后编译,反编译得到字节码如下:()
public class ov.child extends ov.parent{
public ov.child();
  code:
   
0:   aload_0
   
1:   invokespecial   #8//method ov/parent."":()v
   4:   return

public void run(java.lang.object);
  code:
   
0:   getstatic       #16//field java/lang/system.out:ljava/io/printstream;
   3:   ldc     #22//string  in child  param:object
   5:   invokevirtual   #24//method java/io/printstream.println:(ljava/lang/st
ring;)v
   
8:   return

public void run(java.lang.string);
  code:
   
0:   getstatic       #16//field java/lang/system.out:ljava/io/printstream;
   3:   ldc     #32//string  in child   param:string
   5:   invokevirtual   #24//method java/io/printstream.println:(ljava/lang/st
ring;)v
   
8:   return

public static void main(java.lang.string[]);
  code:
   
0:   new     #38//class java/lang/string
   3:   dup
   
4:   invokespecial   #40//method java/lang/string."":()v
   7:   astore_1
   
8:   new     #1//class ov/child
   11:  dup
   
12:  invokespecial   #41//method "":()v
   15:  astore_2
   
16:  aload_2
   
17:  aload_1
   
18:  invokestatic    #42//method test:(lov/parent;ljava/lang/string;)v
   21:  return

public static void test(ov.parent, java.lang.string);
  code:
   
0:   aload_0
   
1:   aload_1
   
2:   invokevirtual   #50//method ov/parent.run:(ljava/lang/string;)v
   5:   return

}
可以看到调用的是parent.run(string)这样说明在编译期间,因为重载就决定了parent.run(string)
然后到运行期,会向下转型到child.run(string)

dreamstone 2006-12-20 00:32
]]>
重载overloading和覆写overriding哪个更早起作用-- visitor帮助篇 http://www.blogjava.net/dreamstone/archive/2006/12/18/88625.htmldreamstonedreamstonemon, 18 dec 2006 12:20:00 gmthttp://www.blogjava.net/dreamstone/archive/2006/12/18/88625.htmlhttp://www.blogjava.net/dreamstone/comments/88625.htmlhttp://www.blogjava.net/dreamstone/archive/2006/12/18/88625.html#feedback18http://www.blogjava.net/dreamstone/comments/commentrss/88625.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/88625.html接受建议,改一下标题.例子不太恰当,我删除了。换成了回文中的例子。
重载overloading和覆写overriding哪个更早执行--   visitor帮助篇
一:问题提出
虽然我们经常写程序用到重载和覆写,但是很少会考虑他们的执行顺序。下边的内容就是关于,他们同时出现时
哪个先起作用:
二:问题分析
java是"动态单分派静态多分派语言",这个定义已经多次提起,如果你不了解这些概念,看这里"visitor模式准备"
所以就注定了重载(静态多分派)要早于覆写(动态单分派),因为静态分派是编绎期实现的,动态分派是执行期实现的。
三:验证


最后的部分:

看完本文,如果你对visitor模式有更多的兴趣,想了解更多请看如下几篇文章。
1,2,访问差异类型的集合类 ------------------------   visitor模式入门
3,visitor模式理论及学术概念-------------------   visitor模式进一步
4,重载overloading和覆写overriding哪个更早执行--   visitor帮助篇 (本文)
虽然排列顺序是1,2,3,4但是我个人建议的学习方式是2,1,3,4因为这个顺序更方便一般人理解



dreamstone 2006-12-18 20:20
]]>
java的类装载器(class loader)和命名空间(namespace) http://www.blogjava.net/dreamstone/archive/2006/12/08/86268.htmldreamstonedreamstonefri, 08 dec 2006 03:30:00 gmthttp://www.blogjava.net/dreamstone/archive/2006/12/08/86268.htmlhttp://www.blogjava.net/dreamstone/comments/86268.htmlhttp://www.blogjava.net/dreamstone/archive/2006/12/08/86268.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/86268.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/86268.html 转载自:
java的类装载器(class loader)和命名空间(namespace)

摘要

java的类装载器是java动态性的核心,本文将向大家简要介绍java的类装载器,及相关的parent delegation模型,命名空间,运行时包等概念,同时讨论一些在学习中容易混淆的问题。

类装载器的功能及分类

顾名思义,类装载器是用来把类(class)装载进jvm的。jvm规范定义了两种类型的类装载器:启动内装载器(bootstrap)和用户自定义装载器(user-defined class loader)。

bootstrap是jvm自带的类装载器,用来装载核心类库,如java.lang.*等。由例1可以看出,java.lang.object是由bootstrap装载的。

java提供了抽象类classloader,所有用户自定义类装载器都实例化自classloader的子类。 system class loader是一个特殊的用户自定义类装载器,由jvm的实现者提供,在编程者不特别指定装载器的情况下默认装载用户类。系统类装载器可以通过classloader.getsystemclassloader() 方法得到。

例1,测试你所使用的jvm的classloader

/*loadersample1.java*/
public class loadersample1 {
    public static void main(string[] args) {
        class c;
        classloader cl;
        cl = classloader.getsystemclassloader();
        system.out.println(cl);
        while (cl != null) {
            cl = cl.getparent();
            system.out.println(cl);
        }
        try {
            c = class.forname("java.lang.object");
            cl = c.getclassloader();
            system.out.println("java.lang.object's loader is " cl);
            c = class.forname("loadersample1");
            cl = c.getclassloader();
            system.out.println("loadersample1's loader is " cl);
        } catch (exception e) {
            e.printstacktrace();
        }
    }
}

在我的机器上(sun java 1.4.2)的运行结果

sun.misc.launcher$appclassloader@1a0c10f
sun.misc.launcher$extclassloader@e2eec8
null 
java.lang.object's loader is null
loadersample1's loader is sun.misc.launcher$appclassloader@1a0c10f

第一行表示,系统类装载器实例化自类sun.misc.launcher$appclassloader

第二行表示,系统类装载器的parent实例化自类sun.misc.launcher$extclassloader

第三行表示,系统类装载器parent的parent为bootstrap

第四行表示,核心类java.lang.object是由bootstrap装载的

第五行表示,用户类loadersample1是由系统类装载器装载的

parent delegation模型

从1.2版本开始,java引入了双亲委托模型,从而更好的保证java平台的安全。在此模型下,当一个装载器被请求装载某个类时,它首先委托自己的parent去装载,若parent能装载,则返回这个类所对应的class对象,若parent不能装载,则由parent的请求者去装载。

如图1所示,loader2的parent为loader1,loader1的parent为system class loader。假设loader2被要求装载类myclass,在parent delegation模型下,loader2首先请求loader1代为装载,loader1再请求系统类装载器去装载myclass。若系统装载器能成功装载,则将myclass所对应的class对象的reference返回给loader1,loader1再将reference返回给loader2,从而成功将类myclass装载进虚拟机。若系统类装载器不能装载myclass,loader1会尝试装载myclass,若loader1也不能成功装载,loader2会尝试装载。若所有的parent及loader2本身都不能装载,则装载失败。

若有一个能成功装载,实际装载的类装载器被称为定义类装载器,所有能成功返回class对象的装载器(包括定义类装载器)被称为初始类装载器。如图1所示,假设loader1实际装载了myclass,则loader1为myclass的定义类装载器,loader2和loader1为myclass的初始类装载器。

 

需要指出的是,class loader是对象,它的父子关系和类的父子关系没有任何关系。一对父子loader可能实例化自同一个class,也可能不是,甚至父loader实例化自子类,子loader实例化自父类。假设myclassloader继承自parentclassloader,我们可以有如下父子loader:

classloader loader1 = new myclassloader();
//参数 loader1 为 parent
classloader loader2 = new parentclassloader(loader1); 

那么parent delegation模型为什么更安全了?因为在此模型下用户自定义的类装载器不可能装载应该由父亲装载器装载的可靠类,从而防止不可靠甚至恶意的代码代替由父亲装载器装载的可靠代码。实际上,类装载器的编写者可以自由选择不用把请求委托给parent,但正如上所说,会带来安全的问题。

命名空间及其作用

每个类装载器有自己的命名空间,命名空间由所有以此装载器为创始类装载器的类组成。不同命名空间的两个类是不可见的,但只要得到类所对应的class对象的reference,还是可以访问另一命名空间的类。

例2演示了一个命名空间的类如何使用另一命名空间的类。在例子中,loadersample2由系统类装载器装载,loadersample3由自定义的装载器loader负责装载,两个类不在同一命名空间,但loadersample2得到了loadersample3所对应的class对象的reference,所以它可以访问loadersampl3中公共的成员(如age)。

例2不同命名空间的类的访问

										/*loadersample2.java*/
import java.net.*;
import java.lang.reflect.*;
public class loadersample2 {
    public static void main(string[] args) {
        try {
            string path = system.getproperty("user.dir");
            url[] us = {new };
            classloader loader = new urlclassloader(us);
            class c = loader.loadclass("loadersample3");
            object o = c.newinstance();
            field f = c.getfield("age");
            int age = f.getint(o);
            system.out.println("age is " age);
        } catch (exception e) {
            e.printstacktrace();
        }
    }
										}
								
										/*sub/loadersample3.java*/
public class loadersample3 {
    static {
        system.out.println("loadersample3 loaded");
    }
    public int age = 30;
}

编译:javac loadersample2.java; javac sub/loadersample3.java

运行:java loadersample2

loadersample3 loaded
age is 30

从运行结果中可以看出,在类loadersample2中可以创建处于另一命名空间的类loadersample3中的对象并可以访问其公共成员age。

运行时包(runtime package)

由同一类装载器定义装载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看的定义类装载器是否相同。只有属于同一运行时包的类才能互相访问包可见的类和成员。这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况。假设用户自己定义了一个类java.lang.yes,并用用户自定义的类装载器装载,由于java.lang.yes和核心类库java.lang.*由不同的装载器装载,它们属于不同的运行时包,所以java.lang.yes不能访问核心类库java.lang中类的包可见的成员。

总结

在简单讨论了类装载器,parent delegation模型,命名空间,运行时包后,相信大家已经对它们的作用有了一定的了解。命名空间并没有完全禁止属于不同空间的类的互相访问,双亲委托模型加强了java的安全,运行时包增加了对包可见成员的保护。

参考资料

<> by bill venners

<> by sun microsystems



dreamstone 2006-12-08 11:30
]]>
深入理解abstract class和interfacehttp://www.blogjava.net/dreamstone/archive/2006/12/04/85398.htmldreamstonedreamstonemon, 04 dec 2006 08:49:00 gmthttp://www.blogjava.net/dreamstone/archive/2006/12/04/85398.htmlhttp://www.blogjava.net/dreamstone/comments/85398.htmlhttp://www.blogjava.net/dreamstone/archive/2006/12/04/85398.html#feedback0http://www.blogjava.net/dreamstone/comments/commentrss/85398.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/85398.html原文 :
abstract class和interface是java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析,试图给开发者提供一个在二者之间进行选择的依据。

dreamstone 2006-12-04 16:49
]]>
探索java nio的历程http://www.blogjava.net/dreamstone/archive/2006/11/18/81982.htmldreamstonedreamstonesat, 18 nov 2006 15:56:00 gmthttp://www.blogjava.net/dreamstone/archive/2006/11/18/81982.htmlhttp://www.blogjava.net/dreamstone/comments/81982.htmlhttp://www.blogjava.net/dreamstone/archive/2006/11/18/81982.html#feedback11http://www.blogjava.net/dreamstone/comments/commentrss/81982.htmlhttp://www.blogjava.net/dreamstone/services/trackbacks/81982.html阅读全文

dreamstone 2006-11-18 23:56
]]>
网站地图