在线观看国产v片,国产免费一区二区三区在线能观看,午夜精品网站http://www.aygfsteel.com/xixidabao/category/15384.htmlGROW WITH JAVAzh-cnSun, 27 Jan 2008 03:09:44 GMTSun, 27 Jan 2008 03:09:44 GMT60MessageDigest對(duì)密碼進(jìn)行加密http://www.aygfsteel.com/xixidabao/archive/2008/01/26/177892.htmlJAVA之路JAVA之路Sat, 26 Jan 2008 04:16:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2008/01/26/177892.html 比如,有明文密碼如下:
String originalPwd = "mypassword";

應(yīng)用報(bào)文摘要方法,得到單向的加密字符串

//MD5是16位,SHA是20位(這是兩種報(bào)文摘要的算法)
//MessageDigest md= MessageDigest.getInstance("MD5");
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");
messageDigest.update(originalPwd.getBytes());
//String digestedPwdString = new String(messageDigest.digest());
String digestedPwdString = new String(Base64.encode(messageDigest.digest()));
System.out.println("pwd:" + digestedPwdString);
這樣,就得到密碼的報(bào)文摘要,把此摘要保存到數(shù)據(jù)庫,
以后用戶登陸時(shí),用相同的算法算出摘要,和數(shù)據(jù)庫中的比較,如果一致,則密碼正確。

注意:
byte[] digest = messageDigest.digest();
得到的是個(gè)二進(jìn)制byte數(shù)組,有可能某些byte是不可打印的字符。
所以用Base64.encode把它轉(zhuǎn)化成可打印字符。

也可以把digest的每個(gè)byte轉(zhuǎn)化成hex(16進(jìn)制)保存。
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");
messageDigest.update(originalPwd.getBytes());
byte[] bin = messageDigest.digest();
再調(diào)用下面的方法生產(chǎn)hex(16進(jìn)制)保存。


二行制轉(zhuǎn)hex字符串的方法如下:
private static String byte2hex(byte[] b){
    String hs="";
    String stmp="";
    for (int n=0; n<b.length; n++){
        stmp=(java.lang.Integer.toHexString(b[n] & 0xFF));
        if (stmp.length()==1) hs=hs+"0"+stmp;
            else hs=hs+stmp;
    }
    return hs;
}

或者:
private static String byto2hex2(byte[] bin){
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < bin.length; ++i) {
        int x = bin[i] & 0xFF, h = x >>> 4, l = x & 0x0F;
        buf.append((char) (h + ((h < 10) ? '0' : 'a' - 10)));
        buf.append((char) (l + ((l < 10) ? '0' : 'a' - 10)));
    }
    return buf.toString();
}

或者:
干脆直接用下面的方法生成,用到第三方包:
public static String encryptPwd(String pwd, String algorithm){
    //String a = org.apache.catalina.realm.RealmBase.Digest(pwd,"SHA-1");
    return org.apache.catalina.realm.RealmBase.Digest(pwd, algorithm);
}

 

http://www.ibm.com/developerworks/cn/java/l-security/index.html

JAVA之路 2008-01-26 12:16 發(fā)表評(píng)論
]]>
Java NIO API詳解http://www.aygfsteel.com/xixidabao/archive/2008/01/18/176208.htmlJAVA之路JAVA之路Fri, 18 Jan 2008 06:47:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2008/01/18/176208.html閱讀全文

JAVA之路 2008-01-18 14:47 發(fā)表評(píng)論
]]>
寫得蠻好的linux學(xué)習(xí)筆記http://www.aygfsteel.com/xixidabao/archive/2007/08/14/136537.htmlJAVA之路JAVA之路Mon, 13 Aug 2007 16:21:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2007/08/14/136537.html
linux目錄架構(gòu)
/   根目錄
/bin    常用的命令 binary file 的目錄
/boot   存放系統(tǒng)啟動(dòng)時(shí)必須讀取的檔案,包括核心 (kernel) 在內(nèi)
     /boot/grub/menu.lst   GRUB設(shè)置
     /boot/vmlinuz   內(nèi)核
     /boot/initrd     核心解壓縮所需 RAM Disk
/dev    系統(tǒng)周邊設(shè)備    
/etc    系統(tǒng)相關(guān)設(shè)定文件
     /etc/DIR_COLORS   設(shè)定顏色
     /etc/HOSTNAME   設(shè)定用戶的節(jié)點(diǎn)名
     /etc/NETWORKING   只有YES標(biāo)明網(wǎng)絡(luò)存在
     /etc/host.conf 文件說明用戶的系統(tǒng)如何查詢節(jié)點(diǎn)名
     /etc/hosts 設(shè)定用戶自已的IP與名字的對(duì)應(yīng)表
     /etc/hosts.allow 設(shè)置允許使用inetd的機(jī)器使用
     /etc/hosts.deny 設(shè)置不允許使用inetd的機(jī)器使用
     /etc/hosts.equiv 設(shè)置遠(yuǎn)端機(jī)不用密碼
     /etc/inetd.conf 設(shè)定系統(tǒng)網(wǎng)絡(luò)守護(hù)進(jìn)程inetd的配置
     /etc/gateways 設(shè)定路由器
     /etc/protocols 設(shè)定系統(tǒng)支持的協(xié)議
     /etc/named.boot 設(shè)定本機(jī)為名字服務(wù)器的配置文件
     /etc/sysconfig/network-scripts/ifcfg-eth0   設(shè)置IP
     /etc/resolv.conf    設(shè)置DNS 
     /etc/X11  X Window的配置文件,xorg.conf XF86Config 這兩個(gè) X Server 的設(shè)定檔
     /etc/fstab    記錄開機(jī)要mount的文件系統(tǒng)
     /etc/inittab 設(shè)定系統(tǒng)啟動(dòng)時(shí)init進(jìn)程將把系統(tǒng)設(shè)置成什么樣的runlevel
     /etc/issue 記錄用戶登錄前顯示的信息
     /etc/group 設(shè)定用戶的組名與相關(guān)信息
     /etc/passwd 帳號(hào)信息
     /etc/shadow 密碼信息
     /etc/sudoers 可以sudo命令的配置文件
     /etc/securetty 設(shè)定哪些終端可以讓root登錄
     /etc/login.defs 所有用戶登錄時(shí)的缺省配置
     /etc/exports 設(shè)定NFS系統(tǒng)用的
     /etc/init.d/   所有服務(wù)的預(yù)設(shè)啟動(dòng) script 都是放在這裡的,例如要啟動(dòng)或者關(guān)閉
     /etc/xinetd.d/  這就是所謂的 super daemon 管理的各項(xiàng)服務(wù)的設(shè)定檔目錄
     /etc/modprobe.conf   內(nèi)核模塊額外參數(shù)設(shè)定
     /etc/syslog.conf   日志設(shè)置文件
/home   使用者家目錄
/lib    系統(tǒng)會(huì)使用到的函數(shù)庫
     /lib/modules   kernel 的相關(guān)模塊
     /var/lib/rpm   rpm套件安裝處
/lost+found    系統(tǒng)不正常產(chǎn)生錯(cuò)誤時(shí),會(huì)將一些遺失的片段放置於此目錄下
/mnt     外設(shè)的掛載點(diǎn)
/media   /mnt類似
/opt     主機(jī)額外安裝的軟件
/proc    虛擬目錄,是內(nèi)存的映射
      /proc/version   內(nèi)核版本
       /proc/sys/kernel   系統(tǒng)內(nèi)核功能
/root    系統(tǒng)管理員的家目錄
/sbin    系統(tǒng)管理員才能執(zhí)行的指令
/srv     一些服務(wù)啟動(dòng)之後,這些服務(wù)所需要取用的資料目錄
/tmp     一般使用者或者是正在執(zhí)行的程序暫時(shí)放置檔案的地方
/usr     最大的目錄,存許應(yīng)用程序和文件
    /usr/X11R6   X-Window目錄
    /usr/src    Linux源代碼
    /usr/include:系統(tǒng)頭文件
    /usr/openwin 存放SUNOpenWin
    /usr/man 在線使用手冊(cè)
    /usr/bin           使用者可執(zhí)行的 binary file 的目錄
    /usr/local/bin     使用者可執(zhí)行的 binary file 的目錄
    /usr/lib           系統(tǒng)會(huì)使用到的函數(shù)庫
    /usr/local/lib     系統(tǒng)會(huì)使用到的函數(shù)庫
    /usr/sbin          系統(tǒng)管理員才能執(zhí)行的指令
    /usr/local/sbin    系統(tǒng)管理員才能執(zhí)行的指令
/var   日志文件
    /var/log/secure    記錄登入系統(tǒng)存取資料的檔案,例如 pop3, ssh, telnet, ftp 等都會(huì)記錄在此檔案中
    /var/log/wtmp      記錄登入者的訊息資料, last
    /var/log/messages  幾乎系統(tǒng)發(fā)生的錯(cuò)誤訊息
    /var/log/boot.log  記錄開機(jī)或者是一些服務(wù)啟動(dòng)的時(shí)候,所顯示的啟動(dòng)或關(guān)閉訊息
    /var/log/maillog   紀(jì)錄郵件存取或往來( sendmail pop3 )的使用者記錄
    /var/log/cron      記錄 crontab 這個(gè)例行性服務(wù)的內(nèi)容
    /var/log/httpd, /var/log/news, /var/log/mysqld.log, /var/log/samba, /var/log/procmail.log
    分別是幾個(gè)不同的網(wǎng)路服務(wù)的記錄檔
 
一些常用的基本命令:
uname -a    查看內(nèi)核版本      
ls -al    顯示所有文件的屬性
pwd         顯示當(dāng)前路徑       
cd -    返回上一次目錄     cd ~    返回主目錄
date s      設(shè)置時(shí)間、日期         
cal      顯示日歷     cal 2006
bc          計(jì)算器具              
man  & info     幫助手冊(cè)
locale     顯示當(dāng)前字體     locale -a    所有可用字體     /etc/sysconfig/i18n設(shè)置文件
LANG=en    使用英文字體           
sync       將數(shù)據(jù)同步寫入硬盤       
shutdonw -h now & half & poweroff  關(guān)機(jī)
reboot     重啟                  
startx  &  init 5   進(jìn)入圖形介面
/work  & ?work    向上、下查找文檔內(nèi)容
chgrp      改變檔案群組  chgrp testing install.log   
chown     改變所屬人   chown root:root install.log
chmod      改變屬性     chmod 777 install.log     read=4  write=2  execute=1
cp   復(fù)制   cp filename
rm   刪除文件  rm -rf filename   強(qiáng)制刪除文件
rmdir   刪除文件夾
mv  移動(dòng)    mv 123.txt 222.txt  重命名
mkdir     創(chuàng)建文件夾
touch     創(chuàng)建文件  更新當(dāng)前時(shí)間
cat       由第一行開始顯示     cat |more  分頁
nl        在內(nèi)容前加行號(hào)
more  &  less   一面一面翻動(dòng)
head -n filename   顯示第N行內(nèi)容
tail -n filename  顯示后N行內(nèi)容
od        顯示非純文檔
df -h 顯示分區(qū)空間
du  顯示目錄或文件的大小
fdisk   分區(qū)設(shè)置    fdisk -l /dev/hda  顯示硬盤分區(qū)狀態(tài)
mkfs    建立各種文件系統(tǒng)  mkfs -t ext3  /dev/ram15  
fsck    檢查和修復(fù)LINUX檔案
ln      硬鏈接   ln -s  軟件鏈接
whereis   查找命令
locate    查找
find      查找   find / -name "***.***"
which     查看工具
whoami    顯示當(dāng)前用戶
gcc -v    查看GCC版本
chattr +i filename  禁止刪除   chattr -i filename  取消禁止
lsattr    顯示隱藏檔屬性
updatedb  更新資料庫
mke2fs    格式化   mkfs -t ext3
dd if=/etc/passwd of=/tmp/passwd.bak    備份
mount     列出系統(tǒng)所有的分區(qū)
mount -t iso9660 /dev/cdrom /mnt/cdrom   掛載光盤
mount -t vfat /dev/fd0 /mnt/floppy       掛載軟盤
mount -t vfat -o iocharset=utf8,umask=000 /dev/hda2 /mnt/hda2   掛載fat32分區(qū)
mount -t ntfs -o nls=utf8,umask=000 /dev/hda3 /mnt/hda3         掛載ntfs分區(qū)
Linux-NTFS Project: http://linux-ntfs.sourceforge.net/
umount /mnt/hda3  缷載
ifconfig   顯示或設(shè)置網(wǎng)絡(luò)設(shè)備
service network restart   重啟網(wǎng)卡 
ifdown eth0  關(guān)閉網(wǎng)卡
ifup eth0    開啟網(wǎng)卡
clear    清屏
history    歷史記錄       !55  執(zhí)行第55個(gè)指令
stty   設(shè)置終端    stty -a
fdisk /mbr   刪除GRUB
at     僅進(jìn)行一次的工作排程
crontab   循環(huán)執(zhí)行的例行性命令    [e]編輯,[l]顯示,[r]刪除任務(wù)
&       后臺(tái)運(yùn)行程序    tar -zxvf 123.tar.gz & --------->后臺(tái)運(yùn)行
jobs    觀看后臺(tái)暫停的程序   jobs -l
fg      將后臺(tái)程序調(diào)到前臺(tái)   fg n ------>n是數(shù)字,可以指定進(jìn)行那個(gè)程序
bg      讓工作在后臺(tái)運(yùn)行
kill    結(jié)束進(jìn)程    kill -9 PID     [9]強(qiáng)制結(jié)束,[15]正常結(jié)束,[l]列出可用的kill信號(hào)
ps aux  查看后臺(tái)程序  
top     查看后臺(tái)程序   top -d 2    每?jī)擅敫乱淮?/font>        top -d 2 -p10604   觀看某個(gè)PID
        top -b -n 2 > /tmp/top.txt -----> top 的資訊進(jìn)行 2 次,然後將結(jié)果輸出到 /tmp/top.txt   
pstree   以樹狀圖顯示程序    [A] ASCII 來連接, [u]列出PID, [p]列出帳號(hào)
killall   要?jiǎng)h除某個(gè)服務(wù)    killall -9 httpd
free      顯示內(nèi)存狀態(tài)     free -m  -------->M為單位顯示
uptime    顯示目前系統(tǒng)開機(jī)時(shí)間
netstat   顯示網(wǎng)絡(luò)狀態(tài)    netstat -tulnp------>找出目前系統(tǒng)上已在監(jiān)聽的網(wǎng)路連線及其 PID
dmesg     顯示開機(jī)信息    demsg | more
nice      設(shè)置優(yōu)先權(quán)      nice -n -5 vi & -----> root 給一個(gè) nice 植為 -5 ,用於執(zhí)行 vi
renice    調(diào)整已存在優(yōu)先權(quán)
runlevel  顯示目前的runlevel
depmod    分析可載入模塊的相依性
lsmod     顯示已載入系統(tǒng)的模塊
modinfo   顯示kernel模塊的信息
insmod    載入模塊
modprobe   自動(dòng)處理可載入模塊
rmmod     刪除模塊
chkconfig   檢查,設(shè)置系統(tǒng)的各種服務(wù)     chkconfig --list ----->列出各項(xiàng)服務(wù)狀態(tài)
ntsysv     設(shè)置系統(tǒng)的各種服務(wù)
cpio      備份文件
 

壓縮命令:
 *.Z      compress 程式壓縮的檔案;
 *.bz2    bzip2 程式壓縮的檔案;
 *.gz     gzip 程式壓縮的檔案;
 *.tar    tar 程式打包的資料,並沒有壓縮過;
 *.tar.gz tar 程式打包的檔案,其中並且經(jīng)過 gzip 的壓縮
compress filename  壓縮文件  [-d]解壓  uncompress
gzip filename   壓縮  [-d]解壓  zcat 123.gz 查看壓縮文件內(nèi)容
bzip2 -z filename  壓縮  [-d]解壓   bzcat filename.bz2  查看壓縮文件內(nèi)容
tar -cvf /home/123.tar /etc  打包,不壓縮
tar -xvf 123.tar   解開包
tar -zxvf /home/123.tar.gz  gzip解壓
tar -jxvf /home/123.tar.bz2  bzip2解壓
tar -ztvf /tmp/etc.tar.gz   查看tar內(nèi)容
cpio -covB  > [file|device]   份份
cpio -icduv < [file|device]   還原
 
vi一般用法
一般模式              編輯模式                  指令模式
h                a,i,r,o,A,I,R,O             :w 保存
j                 進(jìn)入編輯模式                :w! 強(qiáng)制保存
k                 dd 刪除光標(biāo)當(dāng)前行           :q! 不保存離開
l                 ndd 刪除n                 :wq! 保存后離開
0 移動(dòng)到行首        yy 復(fù)制當(dāng)前行                :e! 還原原始檔
$ 移動(dòng)到行尾        nyy 復(fù)制n                  :w filename 另存為
H 屏幕最上          p,P 粘貼                     :set nu 設(shè)置行號(hào)
M 屏幕中央          u  撤消                      :set nonu 取消行號(hào)
L 屏幕最下          [Ctrl]+r 重做上一個(gè)動(dòng)作       ZZ 保存離開
G 檔案最后一行      [ctrl]+z 暫停退出            :set nohlsearch   永久地關(guān)閉高亮顯示
/work 向下搜索                                   :sp 同時(shí)打開兩個(gè)文檔
?work 向上搜索                                   [Ctrl]+w 兩個(gè)文檔設(shè)換
gg 移動(dòng)到檔案第一行                              :nohlsearch    暫時(shí)關(guān)閉高亮顯示
 
認(rèn)識(shí)SHELL
alias    顯示當(dāng)前所有的命令別名      alias lm="ls -al"   命令別名    unalias lm 取消命令別名
type      類似which
exprot    設(shè)置或顯示環(huán)境變量
exprot PATH="$PATH":/sbin  添加/sbinPATH路徑
echo $PATH    顯示PATH路徑
bash      進(jìn)入子程序
name=yang     設(shè)定變量
unset name    取消變量
echo $name    顯示變量的內(nèi)容
myname="$name its me"   &   myname='$name its me'     單引號(hào)時(shí)$name失去變量?jī)?nèi)容
ciw=/etc/sysconfig/network-scripts/     設(shè)置路徑
env      列出所有環(huán)境變量
echo $RANDOM    顯示隨意產(chǎn)生的數(shù)
set      設(shè)置SHELL
PS1='[\u@\h \w \A #\#]\$ '     提示字元的設(shè)定
   [root@linux ~]# read [-pt] variable     -----------讀取鍵盤輸入的變量
   參數(shù):
   -p  :後面可以接提示字元!
   -t  :後面可以接等待的『秒數(shù)!』
declare    聲明 shell 變量
ulimit -a   顯示所有限制資料
 ls /tmp/yang && echo "exist" || echo "not exist"
 意思是說,當(dāng) ls /tmp/yang 執(zhí)行後,若正確,就執(zhí)行echo "exist" ,若有問題,就執(zhí)行echo "not exist"
 echo $PATH | cut -d ':' -f 5       :為分隔符,讀取第5段內(nèi)容
 export | cut -c 10-20      讀取第1020個(gè)字節(jié)的內(nèi)容
 last | grep 'root'    搜索有root的一行,[-v]反向搜索
 cat /etc/passwd | sort    排序顯示
 cat /etc/passwd | wc      顯示『行、字?jǐn)?shù)、字節(jié)數(shù)』
正規(guī)表示法
[root@test root]# grep [-acinv] '搜尋字串' filename
       參數(shù)說明:
       -a :將 binary 檔案以 text 檔案的方式搜尋資料
       -c :計(jì)算找到 '搜尋字串' 的次數(shù)
       -i :忽略大小寫的不同,所以大小寫視為相同
       -n :順便輸出行號(hào)
       -v :反向選擇,亦即顯示出沒有 '搜尋字串' 內(nèi)容的那一行!
 grep -n 'the' 123.txt     搜索the字符 -----------搜尋特定字串      
 grep -n 't[ea]st' 123.txt    搜索testtaste兩個(gè)字符---------利用 [] 來搜尋集合字元
 grep -n '[^g]oo' 123.txt     搜索前面不為goo-----------向選擇 [^]
 grep -n '[0-9]' 123.txt  搜索有0-9的數(shù)字
 grep -n '^the' 123.txt 搜索以the為行首-----------行首搜索^
 grep -n '^[^a-zA-Z]' 123.txt  搜索不以英文字母開頭
 grep -n '[a-z]$' 123.txt    搜索以a-z結(jié)尾的行---------- 行尾搜索$
 grep -n 'g..d' 123.txt     搜索開頭g結(jié)尾d字符----------任意一個(gè)字元 .
 grep -n 'ooo*' 123.txt     搜索至少有兩個(gè)oo的字符---------重複字元 *
sed    文本流編輯器    利用腳本命令來處理文本文件
awd    模式掃描和處理語言
 nl 123.txt | sed '2,5d'   刪除第二到第五行的內(nèi)容
diff     比較文件的差異
cmp      比較兩個(gè)文件是否有差異
patch    修補(bǔ)文件
pr       要打印的文件格式化
 

帳號(hào)管理
/etc/passwd    系統(tǒng)帳號(hào)信息
/etc/shadow    帳號(hào)密碼信息    經(jīng)MD5 32位加密
     在密碼欄前面加『 * 』『 ! 』禁止使用某帳號(hào)
/etc/group     系統(tǒng)群組信息
/etc/gshadow
newgrp    改變登陸組
useradd  &  adduser    建立新用戶  ---------> useradd -m test  自動(dòng)建立用戶的登入目錄
          useradd -m -g pgroup test --------->指定所屬級(jí)
/etc/default/useradd   相關(guān)設(shè)定
/etc/login.defs       UID/GID 有關(guān)的設(shè)定
passwd    更改密碼 -----------> passwd test
usermod   修改用戶帳號(hào)
userdel   刪除帳號(hào) ----------->userdel -r test
chsh      更換登陸系統(tǒng)時(shí)使用的SHELL   [-l]顯示可用的SHELL;[-s]修改自己的SHELL
chfn      改變finger指令顯示的信息
finger    查找并顯示用戶信息
id        顯示用戶的ID ----------->  id test
groupadd   添加組
groupmod   usermod類似
groupdel   刪除組
su test    更改用戶   su -    進(jìn)入root,且使用root的環(huán)境變量
sudo       以其他身份來執(zhí)行指令
visudo     編輯/etc/sudoers      加入一行『 test ALL=(ALL) ALL
           %wheel ALL = (ALL) ALL               系統(tǒng)里所有wheel群組的用戶都可用sudo
           %wheel ALL = (ALL) NOPASSWD: ALL     wheel群組所有用戶都不用密碼NOPASSWD
       User_Alias ADMPW = vbird, dmtsai, vbird1, vbird3         加入ADMPW
       ADMPW ALL = NOPASSWD: !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, \
       !/usr/bin/passwd root      可以更改使用者密碼,但不能更改root密碼 (在指令前面加入 ! 代表不可)
PAM (Pluggable Authentication Modules, 嵌入式模組)
who & w     看誰在線                    
last        最近登陸主機(jī)的信息
lastlog     最近登入的時(shí)間    讀取 /var/log/lastlog
talk        與其他用戶交談
write       發(fā)送信息    write test   [ctrl]+d 發(fā)送
mesg        設(shè)置終端機(jī)的寫入權(quán)限    mesg n 禁止接收     mesg y
wall        向所有用戶發(fā)送信息    wall this is q test
mail        mail  
/etc/default/useradd    家目錄默認(rèn)設(shè)置
quota      顯示磁盤已使用的空間與限制     quota -guvs ----->秀出目前 root 自己的 quota 限制值
           quota -vu   查詢
quotacheck   檢查磁盤的使用空間與限制     quotacheck -avug  ----->將所有的在 /etc/mtab 內(nèi),含有 quota 支援的 partition 進(jìn)行掃瞄
             [-m] 強(qiáng)制掃描 
     quota一定要是獨(dú)立的分區(qū),要有quota.userquota.group兩件文件,/etc/fstab添加一句:
     /dev/hda3 /home ext3 defaults,usrquota,grpquota 1 2
     chmod 600 quota*         設(shè)置完成,重啟生效
edquota    編輯用戶或群組的quota  [u]用戶,[g]群組,[p]復(fù)制,[t]設(shè)置寬限期限
           edquota -a yang       edquota -p yang -u young ----->復(fù)制   
quotaon    開啟磁盤空間限制     quotaon -auvg -------->啟動(dòng)所有的具有 quota filesystem
quotaoff   關(guān)閉磁盤空間限制     quotaoff -a  -------->關(guān)閉了 quota 的限制
repquota -av     查閱系統(tǒng)內(nèi)所有的具有 quota filesystem 的限值狀態(tài)
Quota 從開始準(zhǔn)備 filesystem 的支援到整個(gè)設(shè)定結(jié)束的主要的步驟大概是:
1、設(shè)定 partition filesystem 支援 quota 參數(shù):
由於 quota 必須要讓 partition 上面的 filesystem 支援才行,一般來說, 支援度最好的是 ext2/ext3
其他的 filesystem 類型鳥哥我是沒有試過啦! 啟動(dòng) filesystem 支援 quota 最簡(jiǎn)單就是編輯 /etc/fstab
使得準(zhǔn)備要開放的 quota 磁碟可以支援 quota 囉;
2、建立 quota 記錄檔:
剛剛前面講過,整個(gè) quota 進(jìn)行磁碟限制值記錄的檔案是 aquota.user/aquota.group
要建立這兩個(gè)檔案就必須要先利用 quotacheck 掃瞄才行喔!
3、編輯 quota 限制值資料:
再來就是使用 edquota 來編輯每個(gè)使用者或群組的可使用空間囉;
4、重新掃瞄與啟動(dòng) quota
設(shè)定好 quota 之後,建議可以再進(jìn)行一次 quotacheck ,然後再以 quotaon 來啟動(dòng)吧!

開機(jī)流程簡(jiǎn)介
1、載入 BIOS 的硬體資訊,並取得第一個(gè)開機(jī)裝置的代號(hào);
2、讀取第一個(gè)開機(jī)裝置的 MBR boot Loader (亦即是 lilo, grub, spfdisk 等等) 的開機(jī)資訊;
3、載入 Kernel 作業(yè)系統(tǒng)核心資訊, Kernel 開始解壓縮,並且嘗試驅(qū)動(dòng)所有硬體裝置;
4Kernel 執(zhí)行 init 程式並取得 run-level 資訊;
5init 執(zhí)行 /etc/rc.d/rc.sysinit 檔案;
6、啟動(dòng)核心的外掛模組 (/etc/modprobe.conf)
7init 執(zhí)行 run-level 的各個(gè)批次檔( Scripts )
8init 執(zhí)行 /etc/rc.d/rc.local 檔案;
9、執(zhí)行 /bin/login 程式,並等待使用者登入;
10、登入之後開始以 Shell 控管主機(jī)。
/etc/rc.d/rc3.d內(nèi),S開頭的為開機(jī)啟動(dòng),K開頭的為關(guān)閉,接著的數(shù)字代表執(zhí)行順序
GRUB vga設(shè)定
彩度\解析度  640x480  800x600  1024x768  1280x1024   bit
    256        769      771      773       775      8 bit
   32768       784      787      790       793     15 bit
   65536       785      788      791       794     16 bit
   16.8M       786      789      792       795     32 bit

./configure    檢查系統(tǒng)信息       ./configure --help | more  幫助信息
make clean     清除之前留下的文件
make           編譯
make install   安裝
rpm -q  ----->查詢是否安裝             rpm -ql ------>查詢?cè)撎准械哪夸?/font>
rpm -qi ----->查詢套件的說明資料       rpm -qc[d] ----->設(shè)定檔與說明檔
rpm -ivh  ---->安裝                    rpm -V  -------->查看套件有否更動(dòng)過
rpm -e  ------>刪除                    rpm -Uvh ------->升級(jí)安裝 
--nodeps ----->強(qiáng)行安裝                --test ----->測(cè)試安裝


]]>
java面試筆試題大匯總 ~很全面http://www.aygfsteel.com/xixidabao/archive/2007/06/01/121279.htmlJAVA之路JAVA之路Thu, 31 May 2007 18:56:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2007/06/01/121279.html閱讀全文

]]>
網(wǎng)上搜集的java問題http://www.aygfsteel.com/xixidabao/archive/2007/04/14/110597.htmlJAVA之路JAVA之路Sat, 14 Apr 2007 02:13:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2007/04/14/110597.html1,看看API,在Integer類里面有轉(zhuǎn)成 16進(jìn)制,8進(jìn)制和2進(jìn)制的函數(shù) 16進(jìn)制 Integer.toHexString(int i) 8進(jìn)制 Integer.toOctalString(int i) 2進(jìn)制 Integer.toBinaryString(int i)

int i=100; String binStr=Integer.toBinaryString(i); String otcStr=Integer.toOctalString(i); String hexStr=Integer.toHexString(i); System.out.println(binStr); System.out.println(otcStr); System.out.println(hexStr);

2,java 如何實(shí)現(xiàn)程序的自動(dòng)更新,有例子最好了

做一個(gè)線程 過一段時(shí)間 就連接指定的遠(yuǎn)程服務(wù)器 看最新版本號(hào) 與本地當(dāng)前版本號(hào)是不是一致 是的話 就彈出窗口 提示用戶 用戶確認(rèn)就 自動(dòng)下載下來 然后更新原來的class 再啟動(dòng) 過程就是這樣 自己寫一個(gè)小小的代碼測(cè)試一下就可以



]]>
內(nèi)存管理內(nèi)幕http://www.aygfsteel.com/xixidabao/archive/2007/04/03/108120.htmlJAVA之路JAVA之路Tue, 03 Apr 2007 01:33:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2007/04/03/108120.htmlJonathan Bartlett (johnnyb@eskimo.com), 技術(shù)總監(jiān), New Media Worx

2004 年 11 月 29 日

本文將對(duì) Linux™ 程序員可以使用的內(nèi)存管理技術(shù)進(jìn)行概述,雖然關(guān)注的重點(diǎn)是 C 語言,但同樣也適用于其他語言。文中將為您提供如何管理內(nèi)存的細(xì)節(jié),然后將進(jìn)一步展示如何手工管理內(nèi)存,如何使用引用計(jì)數(shù)或者內(nèi)存池來半手工地管理內(nèi)存,以及如何使用垃圾收集自動(dòng)管理內(nèi)存。

為什么必須管理內(nèi)存

內(nèi)存管理是計(jì)算機(jī)編程最為基本的領(lǐng)域之一。在很多腳本語言中,您不必?fù)?dān)心內(nèi)存是如何管理的,這并不能使得內(nèi)存管理的重要性有一點(diǎn)點(diǎn)降低。對(duì)實(shí)際編程來說,理解您的內(nèi)存管理器的能力與局限性至關(guān)重要。在大部分系統(tǒng)語言中,比如 C 和 C++,您必須進(jìn)行內(nèi)存管理。本文將介紹手工的、半手工的以及自動(dòng)的內(nèi)存管理實(shí)踐的基本概念。

追溯到在 Apple II 上進(jìn)行匯編語言編程的時(shí)代,那時(shí)內(nèi)存管理還不是個(gè)大問題。您實(shí)際上在運(yùn)行整個(gè)系統(tǒng)。系統(tǒng)有多少內(nèi)存,您就有多少內(nèi)存。您甚至不必費(fèi)心思去弄明白它有多少內(nèi)存,因?yàn)槊恳慌_(tái)機(jī)器的內(nèi)存數(shù)量都相同。所以,如果內(nèi)存需要非常固定,那么您只需要選擇一個(gè)內(nèi)存范圍并使用它即可。

不過,即使是在這樣一個(gè)簡(jiǎn)單的計(jì)算機(jī)中,您也會(huì)有問題,尤其是當(dāng)您不知道程序的每個(gè)部分將需要多少內(nèi)存時(shí)。如果您的空間有限,而內(nèi)存需求是變化的,那么您需要一些方法來滿足這些需求:

  • 確定您是否有足夠的內(nèi)存來處理數(shù)據(jù)。
  • 從可用的內(nèi)存中獲取一部分內(nèi)存。
  • 向可用內(nèi)存池(pool)中返回部分內(nèi)存,以使其可以由程序的其他部分或者其他程序使用。

 

實(shí)現(xiàn)這些需求的程序庫稱為 分配程序(allocators),因?yàn)樗鼈冐?fù)責(zé)分配和回收內(nèi)存。程序的動(dòng)態(tài)性越強(qiáng),內(nèi)存管理就越重要,您的內(nèi)存分配程序的選擇也就更重要。讓我們來了解可用于內(nèi)存管理的不同方法,它們的好處與不足,以及它們最適用的情形。





回頁首


C 風(fēng)格的內(nèi)存分配程序

C 編程語言提供了兩個(gè)函數(shù)來滿足我們的三個(gè)需求:

  • malloc:該函數(shù)分配給定的字節(jié)數(shù),并返回一個(gè)指向它們的指針。如果沒有足夠的可用內(nèi)存,那么它返回一個(gè)空指針。
  • free:該函數(shù)獲得指向由 malloc 分配的內(nèi)存片段的指針,并將其釋放,以便以后的程序或操作系統(tǒng)使用(實(shí)際上,一些 malloc 實(shí)現(xiàn)只能將內(nèi)存歸還給程序,而無法將內(nèi)存歸還給操作系統(tǒng))。

 

物理內(nèi)存和虛擬內(nèi)存

要理解內(nèi)存在程序中是如何分配的,首先需要理解如何將內(nèi)存從操作系統(tǒng)分配給程序。計(jì)算機(jī)上的每一個(gè)進(jìn)程都認(rèn)為自己可以訪問所有的物理內(nèi)存。顯然,由于同時(shí)在運(yùn)行多個(gè)程序,所以每個(gè)進(jìn)程不可能擁有全部?jī)?nèi)存。實(shí)際上,這些進(jìn)程使用的是 虛擬內(nèi)存

只是作為一個(gè)例子,讓我們假定您的程序正在訪問地址為 629 的內(nèi)存。不過,虛擬內(nèi)存系統(tǒng)不需要將其存儲(chǔ)在位置為 629 的 RAM 中。實(shí)際上,它甚至可以不在 RAM 中 —— 如果物理 RAM 已經(jīng)滿了,它甚至可能已經(jīng)被轉(zhuǎn)移到硬盤上!由于這類地址不必反映內(nèi)存所在的物理位置,所以它們被稱為虛擬內(nèi)存。操作系統(tǒng)維持著一個(gè)虛擬地址到物理地址的轉(zhuǎn)換的表,以便計(jì)算機(jī)硬件可以正確地響應(yīng)地址請(qǐng)求。并且,如果地址在硬盤上而不是在 RAM 中,那么操作系統(tǒng)將暫時(shí)停止您的進(jìn)程,將其他內(nèi)存轉(zhuǎn)存到硬盤中,從硬盤上加載被請(qǐng)求的內(nèi)存,然后再重新啟動(dòng)您的進(jìn)程。這樣,每個(gè)進(jìn)程都獲得了自己可以使用的地址空間,可以訪問比您物理上安裝的內(nèi)存更多的內(nèi)存。

在 32-位 x86 系統(tǒng)上,每一個(gè)進(jìn)程可以訪問 4 GB 內(nèi)存。現(xiàn)在,大部分人的系統(tǒng)上并沒有 4 GB 內(nèi)存,即使您將 swap 也算上, 每個(gè)進(jìn)程所使用的內(nèi)存也肯定少于 4 GB。因此,當(dāng)加載一個(gè)進(jìn)程時(shí),它會(huì)得到一個(gè)取決于某個(gè)稱為 系統(tǒng)中斷點(diǎn)(system break)的特定地址的初始內(nèi)存分配。該地址之后是未被映射的內(nèi)存 —— 用于在 RAM 或者硬盤中沒有分配相應(yīng)物理位置的內(nèi)存。因此,如果一個(gè)進(jìn)程運(yùn)行超出了它初始分配的內(nèi)存,那么它必須請(qǐng)求操作系統(tǒng)“映射進(jìn)來(map in)”更多的內(nèi)存。(映射是一個(gè)表示一一對(duì)應(yīng)關(guān)系的數(shù)學(xué)術(shù)語 —— 當(dāng)內(nèi)存的虛擬地址有一個(gè)對(duì)應(yīng)的物理地址來存儲(chǔ)內(nèi)存內(nèi)容時(shí),該內(nèi)存將被映射。)

基于 UNIX 的系統(tǒng)有兩個(gè)可映射到附加內(nèi)存中的基本系統(tǒng)調(diào)用:

  • brk: brk() 是一個(gè)非常簡(jiǎn)單的系統(tǒng)調(diào)用。還記得系統(tǒng)中斷點(diǎn)嗎?該位置是進(jìn)程映射的內(nèi)存邊界。 brk() 只是簡(jiǎn)單地將這個(gè)位置向前或者向后移動(dòng),就可以向進(jìn)程添加內(nèi)存或者從進(jìn)程取走內(nèi)存。
  • mmap: mmap(),或者說是“內(nèi)存映像”,類似于 brk(),但是更為靈活。首先,它可以映射任何位置的內(nèi)存,而不單單只局限于進(jìn)程。其次,它不僅可以將虛擬地址映射到物理的 RAM 或者 swap,它還可以將它們映射到文件和文件位置,這樣,讀寫內(nèi)存將對(duì)文件中的數(shù)據(jù)進(jìn)行讀寫。不過,在這里,我們只關(guān)心 mmap 向進(jìn)程添加被映射的內(nèi)存的能力。 munmap() 所做的事情與 mmap() 相反。

 

如您所見, brk() 或者 mmap() 都可以用來向我們的進(jìn)程添加額外的虛擬內(nèi)存。在我們的例子中將使用 brk(),因?yàn)樗?jiǎn)單,更通用。

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的分配程序

如果您曾經(jīng)編寫過很多 C 程序,那么您可能曾多次使用過 malloc()free()。不過,您可能沒有用一些時(shí)間去思考它們?cè)谀牟僮飨到y(tǒng)中是如何實(shí)現(xiàn)的。本節(jié)將向您展示 mallocfree 的一個(gè)最簡(jiǎn)化實(shí)現(xiàn)的代碼,來幫助說明管理內(nèi)存時(shí)都涉及到了哪些事情。

要試著運(yùn)行這些示例,需要先 復(fù)制本代碼清單,并將其粘貼到一個(gè)名為 malloc.c 的文件中。接下來,我將一次一個(gè)部分地對(duì)該清單進(jìn)行解釋。

在大部分操作系統(tǒng)中,內(nèi)存分配由以下兩個(gè)簡(jiǎn)單的函數(shù)來處理:

  • void *malloc(long numbytes):該函數(shù)負(fù)責(zé)分配 numbytes 大小的內(nèi)存,并返回指向第一個(gè)字節(jié)的指針。
  • void free(void *firstbyte):如果給定一個(gè)由先前的 malloc 返回的指針,那么該函數(shù)會(huì)將分配的空間歸還給進(jìn)程的“空閑空間”。

malloc_init 將是初始化內(nèi)存分配程序的函數(shù)。它要完成以下三件事:將分配程序標(biāo)識(shí)為已經(jīng)初始化,找到系統(tǒng)中最后一個(gè)有效內(nèi)存地址,然后建立起指向我們管理的內(nèi)存的指針。這三個(gè)變量都是全局變量:



清單 1. 我們的簡(jiǎn)單分配程序的全局變量
            int has_initialized = 0;
            void *managed_memory_start;
            void *last_valid_address;
            

如前所述,被映射的內(nèi)存的邊界(最后一個(gè)有效地址)常被稱為系統(tǒng)中斷點(diǎn)或者 當(dāng)前中斷點(diǎn)。在很多 UNIX® 系統(tǒng)中,為了指出當(dāng)前系統(tǒng)中斷點(diǎn),必須使用 sbrk(0) 函數(shù)。 sbrk 根據(jù)參數(shù)中給出的字節(jié)數(shù)移動(dòng)當(dāng)前系統(tǒng)中斷點(diǎn),然后返回新的系統(tǒng)中斷點(diǎn)。使用參數(shù) 0 只是返回當(dāng)前中斷點(diǎn)。這里是我們的 malloc 初始化代碼,它將找到當(dāng)前中斷點(diǎn)并初始化我們的變量:



清單 2. 分配程序初始化函數(shù)
            /* Include the sbrk function */
            #include <unistd.h>
            void malloc_init()
            {
            /* grab the last valid address from the OS */
            last_valid_address = sbrk(0);
            /* we don't have any memory to manage yet, so
            *just set the beginning to be last_valid_address
            */
            managed_memory_start = last_valid_address;
            /* Okay, we're initialized and ready to go */
            has_initialized = 1;
            }
            

現(xiàn)在,為了完全地管理內(nèi)存,我們需要能夠追蹤要分配和回收哪些內(nèi)存。在對(duì)內(nèi)存塊進(jìn)行了 free 調(diào)用之后,我們需要做的是諸如將它們標(biāo)記為未被使用的等事情,并且,在調(diào)用 malloc 時(shí),我們要能夠定位未被使用的內(nèi)存塊。因此, malloc 返回的每塊內(nèi)存的起始處首先要有這個(gè)結(jié)構(gòu):



清單 3. 內(nèi)存控制塊結(jié)構(gòu)定義
            struct mem_control_block {
            int is_available;
            int size;
            };
            

現(xiàn)在,您可能會(huì)認(rèn)為當(dāng)程序調(diào)用 malloc 時(shí)這會(huì)引發(fā)問題 —— 它們?nèi)绾沃肋@個(gè)結(jié)構(gòu)?答案是它們不必知道;在返回指針之前,我們會(huì)將其移動(dòng)到這個(gè)結(jié)構(gòu)之后,把它隱藏起來。這使得返回的指針指向沒有用于任何其他用途的內(nèi)存。那樣,從調(diào)用程序的角度來看,它們所得到的全部是空閑的、開放的內(nèi)存。然后,當(dāng)通過 free() 將該指針傳遞回來時(shí),我們只需要倒退幾個(gè)內(nèi)存字節(jié)就可以再次找到這個(gè)結(jié)構(gòu)。

在討論分配內(nèi)存之前,我們將先討論釋放,因?yàn)樗?jiǎn)單。為了釋放內(nèi)存,我們必須要做的惟一一件事情就是,獲得我們給出的指針,回退 sizeof(struct mem_control_block) 個(gè)字節(jié),并將其標(biāo)記為可用的。這里是對(duì)應(yīng)的代碼:



清單 4. 解除分配函數(shù)
            void free(void *firstbyte) {
            struct mem_control_block *mcb;
            /* Backup from the given pointer to find the
            * mem_control_block
            */
            mcb = firstbyte - sizeof(struct mem_control_block);
            /* Mark the block as being available */
            mcb->is_available = 1;
            /* That's It!  We're done. */
            return;
            }
            

如您所見,在這個(gè)分配程序中,內(nèi)存的釋放使用了一個(gè)非常簡(jiǎn)單的機(jī)制,在固定時(shí)間內(nèi)完成內(nèi)存釋放。分配內(nèi)存稍微困難一些。以下是該算法的略述:



清單 5. 主分配程序的偽代碼
            1. If our allocator has not been initialized, initialize it.
            2. Add sizeof(struct mem_control_block) to the size requested.
            3. start at managed_memory_start.
            4. Are we at last_valid address?
            5. If we are:
            A. We didn't find any existing space that was large enough
            -- ask the operating system for more and return that.
            6. Otherwise:
            A. Is the current space available (check is_available from
            the mem_control_block)?
            B. If it is:
            i)   Is it large enough (check "size" from the
            mem_control_block)?
            ii)  If so:
            a. Mark it as unavailable
            b. Move past mem_control_block and return the
            pointer
            iii) Otherwise:
            a. Move forward "size" bytes
            b. Go back go step 4
            C. Otherwise:
            i)   Move forward "size" bytes
            ii)  Go back to step 4
            

我們主要使用連接的指針遍歷內(nèi)存來尋找開放的內(nèi)存塊。這里是代碼:



清單 6. 主分配程序
            void *malloc(long numbytes) {
            /* Holds where we are looking in memory */
            void *current_location;
            /* This is the same as current_location, but cast to a
            * memory_control_block
            */
            struct mem_control_block *current_location_mcb;
            /* This is the memory location we will return.  It will
            * be set to 0 until we find something suitable
            */
            void *memory_location;
            /* Initialize if we haven't already done so */
            if(! has_initialized) 	{
            malloc_init();
            }
            /* The memory we search for has to include the memory
            * control block, but the users of malloc don't need
            * to know this, so we'll just add it in for them.
            */
            numbytes = numbytes + sizeof(struct mem_control_block);
            /* Set memory_location to 0 until we find a suitable
            * location
            */
            memory_location = 0;
            /* Begin searching at the start of managed memory */
            current_location = managed_memory_start;
            /* Keep going until we have searched all allocated space */
            while(current_location != last_valid_address)
            {
            /* current_location and current_location_mcb point
            * to the same address.  However, current_location_mcb
            * is of the correct type, so we can use it as a struct.
            * current_location is a void pointer so we can use it
            * to calculate addresses.
            */
            current_location_mcb =
            (struct mem_control_block *)current_location;
            if(current_location_mcb->is_available)
            {
            if(current_location_mcb->size >= numbytes)
            {
            /* Woohoo!  We've found an open,
            * appropriately-size location.
            */
            /* It is no longer available */
            current_location_mcb->is_available = 0;
            /* We own it */
            memory_location = current_location;
            /* Leave the loop */
            break;
            }
            }
            /* If we made it here, it's because the Current memory
            * block not suitable; move to the next one
            */
            current_location = current_location +
            current_location_mcb->size;
            }
            /* If we still don't have a valid location, we'll
            * have to ask the operating system for more memory
            */
            if(! memory_location)
            {
            /* Move the program break numbytes further */
            sbrk(numbytes);
            /* The new memory will be where the last valid
            * address left off
            */
            memory_location = last_valid_address;
            /* We'll move the last valid address forward
            * numbytes
            */
            last_valid_address = last_valid_address + numbytes;
            /* We need to initialize the mem_control_block */
            current_location_mcb = memory_location;
            current_location_mcb->is_available = 0;
            current_location_mcb->size = numbytes;
            }
            /* Now, no matter what (well, except for error conditions),
            * memory_location has the address of the memory, including
            * the mem_control_block
            */
            /* Move the pointer past the mem_control_block */
            memory_location = memory_location + sizeof(struct mem_control_block);
            /* Return the pointer */
            return memory_location;
            }
            

這就是我們的內(nèi)存管理器。現(xiàn)在,我們只需要構(gòu)建它,并在程序中使用它即可。

運(yùn)行下面的命令來構(gòu)建 malloc 兼容的分配程序(實(shí)際上,我們忽略了 realloc() 等一些函數(shù),不過, malloc()free() 才是最主要的函數(shù)):



清單 7. 編譯分配程序
            gcc -shared -fpic malloc.c -o malloc.so
            

該程序?qū)⑸梢粋€(gè)名為 malloc.so 的文件,它是一個(gè)包含有我們的代碼的共享庫。

在 UNIX 系統(tǒng)中,現(xiàn)在您可以用您的分配程序來取代系統(tǒng)的 malloc(),做法如下:



清單 8. 替換您的標(biāo)準(zhǔn)的 malloc
            LD_PRELOAD=/path/to/malloc.so
            export LD_PRELOAD
            

LD_PRELOAD 環(huán)境變量使動(dòng)態(tài)鏈接器在加載任何可執(zhí)行程序之前,先加載給定的共享庫的符號(hào)。它還為特定庫中的符號(hào)賦予優(yōu)先權(quán)。因此,從現(xiàn)在起,該會(huì)話中的任何應(yīng)用程序都將使用我們的 malloc(),而不是只有系統(tǒng)的應(yīng)用程序能夠使用。有一些應(yīng)用程序不使用 malloc(),不過它們是例外。其他使用 realloc() 等其他內(nèi)存管理函數(shù)的應(yīng)用程序,或者錯(cuò)誤地假定 malloc() 內(nèi)部行為的那些應(yīng)用程序,很可能會(huì)崩潰。ash shell 似乎可以使用我們的新 malloc() 很好地工作。

如果您想確保 malloc() 正在被使用,那么您應(yīng)該通過向函數(shù)的入口點(diǎn)添加 write() 調(diào)用來進(jìn)行測(cè)試。

我們的內(nèi)存管理器在很多方面都還存在欠缺,但它可以有效地展示內(nèi)存管理需要做什么事情。它的某些缺點(diǎn)包括:

  • 由于它對(duì)系統(tǒng)中斷點(diǎn)(一個(gè)全局變量)進(jìn)行操作,所以它不能與其他分配程序或者 mmap 一起使用。
  • 當(dāng)分配內(nèi)存時(shí),在最壞的情形下,它將不得不遍歷 全部進(jìn)程內(nèi)存;其中可能包括位于硬盤上的很多內(nèi)存,這意味著操作系統(tǒng)將不得不花時(shí)間去向硬盤移入數(shù)據(jù)和從硬盤中移出數(shù)據(jù)。
  • 沒有很好的內(nèi)存不足處理方案( malloc 只假定內(nèi)存分配是成功的)。
  • 它沒有實(shí)現(xiàn)很多其他的內(nèi)存函數(shù),比如 realloc()
  • 由于 sbrk() 可能會(huì)交回比我們請(qǐng)求的更多的內(nèi)存,所以在堆(heap)的末端會(huì)遺漏一些內(nèi)存。
  • 雖然 is_available 標(biāo)記只包含一位信息,但它要使用完整的 4-字節(jié) 的字。
  • 分配程序不是線程安全的。
  • 分配程序不能將空閑空間拼合為更大的內(nèi)存塊。
  • 分配程序的過于簡(jiǎn)單的匹配算法會(huì)導(dǎo)致產(chǎn)生很多潛在的內(nèi)存碎片。
  • 我確信還有很多其他問題。這就是為什么它只是一個(gè)例子!

 

其他 malloc 實(shí)現(xiàn)

malloc() 的實(shí)現(xiàn)有很多,這些實(shí)現(xiàn)各有優(yōu)點(diǎn)與缺點(diǎn)。在設(shè)計(jì)一個(gè)分配程序時(shí),要面臨許多需要折衷的選擇,其中包括:

  • 分配的速度。
  • 回收的速度。
  • 有線程的環(huán)境的行為。
  • 內(nèi)存將要被用光時(shí)的行為。
  • 局部緩存。
  • 簿記(Bookkeeping)內(nèi)存開銷。
  • 虛擬內(nèi)存環(huán)境中的行為。
  • 小的或者大的對(duì)象。
  • 實(shí)時(shí)保證。

 

每一個(gè)實(shí)現(xiàn)都有其自身的優(yōu)缺點(diǎn)集合。在我們的簡(jiǎn)單的分配程序中,分配非常慢,而回收非常快。另外,由于它在使用虛擬內(nèi)存系統(tǒng)方面較差,所以它最適于處理大的對(duì)象。

還有其他許多分配程序可以使用。其中包括:

  • Doug Lea Malloc:Doug Lea Malloc 實(shí)際上是完整的一組分配程序,其中包括 Doug Lea 的原始分配程序,GNU libc 分配程序和 ptmalloc。 Doug Lea 的分配程序有著與我們的版本非常類似的基本結(jié)構(gòu),但是它加入了索引,這使得搜索速度更快,并且可以將多個(gè)沒有被使用的塊組合為一個(gè)大的塊。它還支持緩存,以便更快地再次使用最近釋放的內(nèi)存。 ptmalloc 是 Doug Lea Malloc 的一個(gè)擴(kuò)展版本,支持多線程。在本文后面的 參考資料部分中,有一篇描述 Doug Lea 的 Malloc 實(shí)現(xiàn)的文章。
  • BSD Malloc:BSD Malloc 是隨 4.2 BSD 發(fā)行的實(shí)現(xiàn),包含在 FreeBSD 之中,這個(gè)分配程序可以從預(yù)先確實(shí)大小的對(duì)象構(gòu)成的池中分配對(duì)象。它有一些用于對(duì)象大小的 size 類,這些對(duì)象的大小為 2 的若干次冪減去某一常數(shù)。所以,如果您請(qǐng)求給定大小的一個(gè)對(duì)象,它就簡(jiǎn)單地分配一個(gè)與之匹配的 size 類。這樣就提供了一個(gè)快速的實(shí)現(xiàn),但是可能會(huì)浪費(fèi)內(nèi)存。在 參考資料部分中,有一篇描述該實(shí)現(xiàn)的文章。
  • Hoard:編寫 Hoard 的目標(biāo)是使內(nèi)存分配在多線程環(huán)境中進(jìn)行得非常快。因此,它的構(gòu)造以鎖的使用為中心,從而使所有進(jìn)程不必等待分配內(nèi)存。它可以顯著地加快那些進(jìn)行很多分配和回收的多線程進(jìn)程的速度。在 參考資料部分中,有一篇描述該實(shí)現(xiàn)的文章。

 

眾多可用的分配程序中最有名的就是上述這些分配程序。如果您的程序有特別的分配需求,那么您可能更愿意編寫一個(gè)定制的能匹配您的程序內(nèi)存分配方式的分配程序。不過,如果不熟悉分配程序的設(shè)計(jì),那么定制分配程序通常會(huì)帶來比它們解決的問題更多的問題。要獲得關(guān)于該主題的適當(dāng)?shù)慕榻B,請(qǐng)參閱 Donald Knuth 撰寫的 The Art of Computer Programming Volume 1: Fundamental Algorithms 中的第 2.5 節(jié)“Dynamic Storage Allocation”(請(qǐng)參閱 參考資料中的鏈接)。它有點(diǎn)過時(shí),因?yàn)樗鼪]有考慮虛擬內(nèi)存環(huán)境,不過大部分算法都是基于前面給出的函數(shù)。

在 C++ 中,通過重載 operator new(),您可以以每個(gè)類或者每個(gè)模板為單位實(shí)現(xiàn)自己的分配程序。在 Andrei Alexandrescu 撰寫的 Modern C++ Design 的第 4 章(“Small Object Allocation”)中,描述了一個(gè)小對(duì)象分配程序(請(qǐng)參閱 參考資料中的鏈接)。

基于 malloc() 的內(nèi)存管理的缺點(diǎn)

不只是我們的內(nèi)存管理器有缺點(diǎn),基于 malloc() 的內(nèi)存管理器仍然也有很多缺點(diǎn),不管您使用的是哪個(gè)分配程序。對(duì)于那些需要保持長(zhǎng)期存儲(chǔ)的程序使用 malloc() 來管理內(nèi)存可能會(huì)非常令人失望。如果您有大量的不固定的內(nèi)存引用,經(jīng)常難以知道它們何時(shí)被釋放。生存期局限于當(dāng)前函數(shù)的內(nèi)存非常容易管理,但是對(duì)于生存期超出該范圍的內(nèi)存來說,管理內(nèi)存則困難得多。而且,關(guān)于內(nèi)存管理是由進(jìn)行調(diào)用的程序還是由被調(diào)用的函數(shù)來負(fù)責(zé)這一問題,很多 API 都不是很明確。

因?yàn)楣芾韮?nèi)存的問題,很多程序傾向于使用它們自己的內(nèi)存管理規(guī)則。C++ 的異常處理使得這項(xiàng)任務(wù)更成問題。有時(shí)好像致力于管理內(nèi)存分配和清理的代碼比實(shí)際完成計(jì)算任務(wù)的代碼還要多!因此,我們將研究?jī)?nèi)存管理的其他選擇。





回頁首


半自動(dòng)內(nèi)存管理策略

引用計(jì)數(shù)

引用計(jì)數(shù)是一種 半自動(dòng)(semi-automated)的內(nèi)存管理技術(shù),這表示它需要一些編程支持,但是它不需要您確切知道某一對(duì)象何時(shí)不再被使用。引用計(jì)數(shù)機(jī)制為您完成內(nèi)存管理任務(wù)。

在引用計(jì)數(shù)中,所有共享的數(shù)據(jù)結(jié)構(gòu)都有一個(gè)域來包含當(dāng)前活動(dòng)“引用”結(jié)構(gòu)的次數(shù)。當(dāng)向一個(gè)程序傳遞一個(gè)指向某個(gè)數(shù)據(jù)結(jié)構(gòu)指針時(shí),該程序會(huì)將引用計(jì)數(shù)增加 1。實(shí)質(zhì)上,您是在告訴數(shù)據(jù)結(jié)構(gòu),它正在被存儲(chǔ)在多少個(gè)位置上。然后,當(dāng)您的進(jìn)程完成對(duì)它的使用后,該程序就會(huì)將引用計(jì)數(shù)減少 1。結(jié)束這個(gè)動(dòng)作之后,它還會(huì)檢查計(jì)數(shù)是否已經(jīng)減到零。如果是,那么它將釋放內(nèi)存。

這樣做的好處是,您不必追蹤程序中某個(gè)給定的數(shù)據(jù)結(jié)構(gòu)可能會(huì)遵循的每一條路徑。每次對(duì)其局部的引用,都將導(dǎo)致計(jì)數(shù)的適當(dāng)增加或減少。這樣可以防止在使用數(shù)據(jù)結(jié)構(gòu)時(shí)釋放該結(jié)構(gòu)。不過,當(dāng)您使用某個(gè)采用引用計(jì)數(shù)的數(shù)據(jù)結(jié)構(gòu)時(shí),您必須記得運(yùn)行引用計(jì)數(shù)函數(shù)。另外,內(nèi)置函數(shù)和第三方的庫不會(huì)知道或者可以使用您的引用計(jì)數(shù)機(jī)制。引用計(jì)數(shù)也難以處理發(fā)生循環(huán)引用的數(shù)據(jù)結(jié)構(gòu)。

要實(shí)現(xiàn)引用計(jì)數(shù),您只需要兩個(gè)函數(shù) —— 一個(gè)增加引用計(jì)數(shù),一個(gè)減少引用計(jì)數(shù)并當(dāng)計(jì)數(shù)減少到零時(shí)釋放內(nèi)存。

一個(gè)示例引用計(jì)數(shù)函數(shù)集可能看起來如下所示:



清單 9. 基本的引用計(jì)數(shù)函數(shù)
            /* Structure Definitions*/
            /* Base structure that holds a refcount */
            struct refcountedstruct
            {
            int refcount;
            }
            /* All refcounted structures must mirror struct
            * refcountedstruct for their first variables
            */
            /* Refcount maintenance functions */
            /* Increase reference count */
            void REF(void *data)
            {
            struct refcountedstruct *rstruct;
            rstruct = (struct refcountedstruct *) data;
            rstruct->refcount++;
            }
            /* Decrease reference count */
            void UNREF(void *data)
            {
            struct refcountedstruct *rstruct;
            rstruct = (struct refcountedstruct *) data;
            rstruct->refcount--;
            /* Free the structure if there are no more users */
            if(rstruct->refcount == 0)
            {
            free(rstruct);
            }
            }
            

REFUNREF 可能會(huì)更復(fù)雜,這取決于您想要做的事情。例如,您可能想要為多線程程序增加鎖,那么您可能想擴(kuò)展 refcountedstruct,使它同樣包含一個(gè)指向某個(gè)在釋放內(nèi)存之前要調(diào)用的函數(shù)的指針(類似于面向?qū)ο笳Z言中的析構(gòu)函數(shù) —— 如果您的結(jié)構(gòu)中包含這些指針,那么這是 必需的)。

當(dāng)使用 REFUNREF 時(shí),您需要遵守這些指針的分配規(guī)則:

  • UNREF 分配前左端指針(left-hand-side pointer)指向的值。
  • REF 分配后左端指針(left-hand-side pointer)指向的值。

 

在傳遞使用引用計(jì)數(shù)的結(jié)構(gòu)的函數(shù)中,函數(shù)需要遵循以下這些規(guī)則:

  • 在函數(shù)的起始處 REF 每一個(gè)指針。
  • 在函數(shù)的結(jié)束處 UNREF 第一個(gè)指針。

 

以下是一個(gè)使用引用計(jì)數(shù)的生動(dòng)的代碼示例:



清單 10. 使用引用計(jì)數(shù)的示例
            /* EXAMPLES OF USAGE */
            /* Data type to be refcounted */
            struct mydata
            {
            int refcount; /* same as refcountedstruct */
            int datafield1; /* Fields specific to this struct */
            int datafield2;
            /* other declarations would go here as appropriate */
            };
            /* Use the functions in code */
            void dosomething(struct mydata *data)
            {
            REF(data);
            /* Process data */
            /* when we are through */
            UNREF(data);
            }
            struct mydata *globalvar1;
            /* Note that in this one, we don't decrease the
            * refcount since we are maintaining the reference
            * past the end of the function call through the
            * global variable
            */
            void storesomething(struct mydata *data)
            {
            REF(data); /* passed as a parameter */
            globalvar1 = data;
            REF(data); /* ref because of Assignment */
            UNREF(data); /* Function finished */
            }
            

由于引用計(jì)數(shù)是如此簡(jiǎn)單,大部分程序員都自已去實(shí)現(xiàn)它,而不是使用庫。不過,它們依賴于 mallocfree 等低層的分配程序來實(shí)際地分配和釋放它們的內(nèi)存。

在 Perl 等高級(jí)語言中,進(jìn)行內(nèi)存管理時(shí)使用引用計(jì)數(shù)非常廣泛。在這些語言中,引用計(jì)數(shù)由語言自動(dòng)地處理,所以您根本不必?fù)?dān)心它,除非要編寫擴(kuò)展模塊。由于所有內(nèi)容都必須進(jìn)行引用計(jì)數(shù),所以這會(huì)對(duì)速度產(chǎn)生一些影響,但它極大地提高了編程的安全性和方便性。以下是引用計(jì)數(shù)的益處:

  • 實(shí)現(xiàn)簡(jiǎn)單。
  • 易于使用。
  • 由于引用是數(shù)據(jù)結(jié)構(gòu)的一部分,所以它有一個(gè)好的緩存位置。

 

不過,它也有其不足之處:

  • 要求您永遠(yuǎn)不要忘記調(diào)用引用計(jì)數(shù)函數(shù)。
  • 無法釋放作為循環(huán)數(shù)據(jù)結(jié)構(gòu)的一部分的結(jié)構(gòu)。
  • 減緩幾乎每一個(gè)指針的分配。
  • 盡管所使用的對(duì)象采用了引用計(jì)數(shù),但是當(dāng)使用異常處理(比如 trysetjmp()/ longjmp())時(shí),您必須采取其他方法。
  • 需要額外的內(nèi)存來處理引用。
  • 引用計(jì)數(shù)占用了結(jié)構(gòu)中的第一個(gè)位置,在大部分機(jī)器中最快可以訪問到的就是這個(gè)位置。
  • 在多線程環(huán)境中更慢也更難以使用。

 

C++ 可以通過使用 智能指針(smart pointers)來容忍程序員所犯的一些錯(cuò)誤,智能指針可以為您處理引用計(jì)數(shù)等指針處理細(xì)節(jié)。不過,如果不得不使用任何先前的不能處理智能指針的代碼(比如對(duì) C 庫的聯(lián)接),實(shí)際上,使用它們的后果通實(shí)比不使用它們更為困難和復(fù)雜。因此,它通常只是有益于純 C++ 項(xiàng)目。如果您想使用智能指針,那么您實(shí)在應(yīng)該去閱讀 Alexandrescu 撰寫的 Modern C++ Design 一書中的“Smart Pointers”那一章。

內(nèi)存池

內(nèi)存池是另一種半自動(dòng)內(nèi)存管理方法。內(nèi)存池幫助某些程序進(jìn)行自動(dòng)內(nèi)存管理,這些程序會(huì)經(jīng)歷一些特定的階段,而且每個(gè)階段中都有分配給進(jìn)程的特定階段的內(nèi)存。例如,很多網(wǎng)絡(luò)服務(wù)器進(jìn)程都會(huì)分配很多針對(duì)每個(gè)連接的內(nèi)存 —— 內(nèi)存的最大生存期限為當(dāng)前連接的存在期。Apache 使用了池式內(nèi)存(pooled memory),將其連接拆分為各個(gè)階段,每個(gè)階段都有自己的內(nèi)存池。在結(jié)束每個(gè)階段時(shí),會(huì)一次釋放所有內(nèi)存。

在池式內(nèi)存管理中,每次內(nèi)存分配都會(huì)指定內(nèi)存池,從中分配內(nèi)存。每個(gè)內(nèi)存池都有不同的生存期限。在 Apache 中,有一個(gè)持續(xù)時(shí)間為服務(wù)器存在期的內(nèi)存池,還有一個(gè)持續(xù)時(shí)間為連接的存在期的內(nèi)存池,以及一個(gè)持續(xù)時(shí)間為請(qǐng)求的存在期的池,另外還有其他一些內(nèi)存池。因此,如果我的一系列函數(shù)不會(huì)生成比連接持續(xù)時(shí)間更長(zhǎng)的數(shù)據(jù),那么我就可以完全從連接池中分配內(nèi)存,并知道在連接結(jié)束時(shí),這些內(nèi)存會(huì)被自動(dòng)釋放。另外,有一些實(shí)現(xiàn)允許注冊(cè) 清除函數(shù)(cleanup functions),在清除內(nèi)存池之前,恰好可以調(diào)用它,來完成在內(nèi)存被清理前需要完成的其他所有任務(wù)(類似于面向?qū)ο笾械奈鰳?gòu)函數(shù))。

要在自己的程序中使用池,您既可以使用 GNU libc 的 obstack 實(shí)現(xiàn),也可以使用 Apache 的 Apache Portable Runtime。GNU obstack 的好處在于,基于 GNU 的 Linux 發(fā)行版本中默認(rèn)會(huì)包括它們。Apache Portable Runtime 的好處在于它有很多其他工具,可以處理編寫多平臺(tái)服務(wù)器軟件所有方面的事情。要深入了解 GNU obstack 和 Apache 的池式內(nèi)存實(shí)現(xiàn),請(qǐng)參閱 參考資料部分中指向這些實(shí)現(xiàn)的文檔的鏈接。

下面的假想代碼列表展示了如何使用 obstack:



清單 11. obstack 的示例代碼
            #include <obstack.h>
            #include <stdlib.h>
            /* Example code listing for using obstacks */
            /* Used for obstack macros (xmalloc is
            a malloc function that exits if memory
            is exhausted */
            #define obstack_chunk_alloc xmalloc
            #define obstack_chunk_free free
            /* Pools */
            /* Only permanent allocations should go in this pool */
            struct obstack *global_pool;
            /* This pool is for per-connection data */
            struct obstack *connection_pool;
            /* This pool is for per-request data */
            struct obstack *request_pool;
            void allocation_failed()
            {
            exit(1);
            }
            int main()
            {
            /* Initialize Pools */
            global_pool = (struct obstack *)
            xmalloc (sizeof (struct obstack));
            obstack_init(global_pool);
            connection_pool = (struct obstack *)
            xmalloc (sizeof (struct obstack));
            obstack_init(connection_pool);
            request_pool = (struct obstack *)
            xmalloc (sizeof (struct obstack));
            obstack_init(request_pool);
            /* Set the error handling function */
            obstack_alloc_failed_handler = &allocation_failed;
            /* Server main loop */
            while(1)
            {
            wait_for_connection();
            /* We are in a connection */
            while(more_requests_available())
            {
            /* Handle request */
            handle_request();
            /* Free all of the memory allocated
            * in the request pool
            */
            obstack_free(request_pool, NULL);
            }
            /* We're finished with the connection, time
            * to free that pool
            */
            obstack_free(connection_pool, NULL);
            }
            }
            int handle_request()
            {
            /* Be sure that all object allocations are allocated
            * from the request pool
            */
            int bytes_i_need = 400;
            void *data1 = obstack_alloc(request_pool, bytes_i_need);
            /* Do stuff to process the request */
            /* return */
            return 0;
            }
            

基本上,在操作的每一個(gè)主要階段結(jié)束之后,這個(gè)階段的 obstack 會(huì)被釋放。不過,要注意的是,如果一個(gè)過程需要分配持續(xù)時(shí)間比當(dāng)前階段更長(zhǎng)的內(nèi)存,那么它也可以使用更長(zhǎng)期限的 obstack,比如連接或者全局內(nèi)存。傳遞給 obstack_free()NULL 指出它應(yīng)該釋放 obstack 的全部?jī)?nèi)容。可以用其他的值,但是它們通常不怎么實(shí)用。

使用池式內(nèi)存分配的益處如下所示:

  • 應(yīng)用程序可以簡(jiǎn)單地管理內(nèi)存。
  • 內(nèi)存分配和回收更快,因?yàn)槊看味际窃谝粋€(gè)池中完成的。分配可以在 O(1) 時(shí)間內(nèi)完成,釋放內(nèi)存池所需時(shí)間也差不多(實(shí)際上是 O(n) 時(shí)間,不過在大部分情況下會(huì)除以一個(gè)大的因數(shù),使其變成 O(1))。
  • 可以預(yù)先分配錯(cuò)誤處理池(Error-handling pools),以便程序在常規(guī)內(nèi)存被耗盡時(shí)仍可以恢復(fù)。
  • 有非常易于使用的標(biāo)準(zhǔn)實(shí)現(xiàn)。

 

池式內(nèi)存的缺點(diǎn)是:

  • 內(nèi)存池只適用于操作可以分階段的程序。
  • 內(nèi)存池通常不能與第三方庫很好地合作。
  • 如果程序的結(jié)構(gòu)發(fā)生變化,則不得不修改內(nèi)存池,這可能會(huì)導(dǎo)致內(nèi)存管理系統(tǒng)的重新設(shè)計(jì)。
  • 您必須記住需要從哪個(gè)池進(jìn)行分配。另外,如果在這里出錯(cuò),就很難捕獲該內(nèi)存池。




回頁首


垃圾收集

垃圾收集(Garbage collection)是全自動(dòng)地檢測(cè)并移除不再使用的數(shù)據(jù)對(duì)象。垃圾收集器通常會(huì)在當(dāng)可用內(nèi)存減少到少于一個(gè)具體的閾值時(shí)運(yùn)行。通常,它們以程序所知的可用的一組“基本”數(shù)據(jù) —— 棧數(shù)據(jù)、全局變量、寄存器 —— 作為出發(fā)點(diǎn)。然后它們嘗試去追蹤通過這些數(shù)據(jù)連接到每一塊數(shù)據(jù)。收集器找到的都是有用的數(shù)據(jù);它沒有找到的就是垃圾,可以被銷毀并重新使用這些無用的數(shù)據(jù)。為了有效地管理內(nèi)存,很多類型的垃圾收集器都需要知道數(shù)據(jù)結(jié)構(gòu)內(nèi)部指針的規(guī)劃,所以,為了正確運(yùn)行垃圾收集器,它們必須是語言本身的一部分。

收集器的類型

  • 復(fù)制(copying): 這些收集器將內(nèi)存存儲(chǔ)器分為兩部分,只允許數(shù)據(jù)駐留在其中一部分上。它們定時(shí)地從“基本”的元素開始將數(shù)據(jù)從一部分復(fù)制到另一部分。內(nèi)存新近被占用的部分現(xiàn)在成為活動(dòng)的,另一部分上的所有內(nèi)容都認(rèn)為是垃圾。另外,當(dāng)進(jìn)行這項(xiàng)復(fù)制操作時(shí),所有指針都必須被更新為指向每個(gè)內(nèi)存條目的新位置。因此,為使用這種垃圾收集方法,垃圾收集器必須與編程語言集成在一起。
  • 標(biāo)記并清理(Mark and sweep):每一塊數(shù)據(jù)都被加上一個(gè)標(biāo)簽。不定期的,所有標(biāo)簽都被設(shè)置為 0,收集器從“基本”的元素開始遍歷數(shù)據(jù)。當(dāng)它遇到內(nèi)存時(shí),就將標(biāo)簽標(biāo)記為 1。最后沒有被標(biāo)記為 1 的所有內(nèi)容都認(rèn)為是垃圾,以后分配內(nèi)存時(shí)會(huì)重新使用它們。
  • 增量的(Incremental):增量垃圾收集器不需要遍歷全部數(shù)據(jù)對(duì)象。因?yàn)樵谑占陂g的突然等待,也因?yàn)榕c訪問所有當(dāng)前數(shù)據(jù)相關(guān)的緩存問題(所有內(nèi)容都不得不被頁入(page-in)),遍歷所有內(nèi)存會(huì)引發(fā)問題。增量收集器避免了這些問題。
  • 保守的(Conservative):保守的垃圾收集器在管理內(nèi)存時(shí)不需要知道與數(shù)據(jù)結(jié)構(gòu)相關(guān)的任何信息。它們只查看所有數(shù)據(jù)類型,并假定它們 可以全部都是指針。所以,如果一個(gè)字節(jié)序列可以是一個(gè)指向一塊被分配的內(nèi)存的指針,那么收集器就將其標(biāo)記為正在被引用。有時(shí)沒有被引用的內(nèi)存會(huì)被收集,這樣會(huì)引發(fā)問題,例如,如果一個(gè)整數(shù)域中包含一個(gè)值,該值是已分配內(nèi)存的地址。不過,這種情況極少發(fā)生,而且它只會(huì)浪費(fèi)少量?jī)?nèi)存。保守的收集器的優(yōu)勢(shì)是,它們可以與任何編程語言相集成。

 

Hans Boehm 的保守垃圾收集器是可用的最流行的垃圾收集器之一,因?yàn)樗敲赓M(fèi)的,而且既是保守的又是增量的,可以使用 --enable-redirect-malloc 選項(xiàng)來構(gòu)建它,并且可以將它用作系統(tǒng)分配程序的簡(jiǎn)易替代者(drop-in replacement)(用 malloc/ free 代替它自己的 API)。實(shí)際上,如果這樣做,您就可以使用與我們?cè)谑纠峙涑绦蛑兴褂玫南嗤?LD_PRELOAD 技巧,在系統(tǒng)上的幾乎任何程序中啟用垃圾收集。如果您懷疑某個(gè)程序正在泄漏內(nèi)存,那么您可以使用這個(gè)垃圾收集器來控制進(jìn)程。在早期,當(dāng) Mozilla 嚴(yán)重地泄漏內(nèi)存時(shí),很多人在其中使用了這項(xiàng)技術(shù)。這種垃圾收集器既可以在 Windows® 下運(yùn)行,也可以在 UNIX 下運(yùn)行。

垃圾收集的一些優(yōu)點(diǎn):

  • 您永遠(yuǎn)不必?fù)?dān)心內(nèi)存的雙重釋放或者對(duì)象的生命周期。
  • 使用某些收集器,您可以使用與常規(guī)分配相同的 API。

 

其缺點(diǎn)包括:

  • 使用大部分收集器時(shí),您都無法干涉何時(shí)釋放內(nèi)存。
  • 在多數(shù)情況下,垃圾收集比其他形式的內(nèi)存管理更慢。
  • 垃圾收集錯(cuò)誤引發(fā)的缺陷難于調(diào)試。
  • 如果您忘記將不再使用的指針設(shè)置為 null,那么仍然會(huì)有內(nèi)存泄漏。

 





回頁首


結(jié)束語

一切都需要折衷:性能、易用、易于實(shí)現(xiàn)、支持線程的能力等,這里只列出了其中的一些。為了滿足項(xiàng)目的要求,有很多內(nèi)存管理模式可以供您使用。每種模式都有大量的實(shí)現(xiàn),各有其優(yōu)缺點(diǎn)。對(duì)很多項(xiàng)目來說,使用編程環(huán)境默認(rèn)的技術(shù)就足夠了,不過,當(dāng)您的項(xiàng)目有特殊的需要時(shí),了解可用的選擇將會(huì)有幫助。下表對(duì)比了本文中涉及的內(nèi)存管理策略。

表 1. 內(nèi)存分配策略的對(duì)比

策略 分配速度 回收速度 局部緩存 易用性 通用性 實(shí)時(shí)可用 SMP 線程友好
定制分配程序 取決于實(shí)現(xiàn) 取決于實(shí)現(xiàn) 取決于實(shí)現(xiàn) 很難 取決于實(shí)現(xiàn) 取決于實(shí)現(xiàn)
簡(jiǎn)單分配程序 內(nèi)存使用少時(shí)較快 很快 容易
GNU malloc 容易
Hoard 容易
引用計(jì)數(shù) N/A N/A 非常好 是(取決于 malloc 實(shí)現(xiàn)) 取決于實(shí)現(xiàn)
非常快 極好 是(取決于 malloc 實(shí)現(xiàn)) 取決于實(shí)現(xiàn)
垃圾收集 中(進(jìn)行收集時(shí)慢) 幾乎不
增量垃圾收集 幾乎不
增量保守垃圾收集 容易 幾乎不



參考資料

  • 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文

Web 上的文檔

基本的分配程序 池式分配程序 智能指針和定制分配程序
  • Loki C++ Library 有很多為 C++ 實(shí)現(xiàn)的通用模式,包括智能指針和一個(gè)定制的小對(duì)象分配程序。
垃圾收集器 關(guān)于現(xiàn)代操作系統(tǒng)中的虛擬內(nèi)存的文章 關(guān)于 malloc 的文章 關(guān)于定制分配程序的文章 關(guān)于垃圾收集的文章 Web 上的通用參考資料 書籍
  • Michael Daconta 撰寫的 C++ Pointers and Dynamic Memory Management 介紹了關(guān)于內(nèi)存管理的很多技術(shù)。

  • Frantisek Franek 撰寫的 Memory as a Programming Concept in C and C++ 討論了有效使用內(nèi)存的技術(shù)與工具,并給出了在計(jì)算機(jī)編程中應(yīng)當(dāng)引起注意的內(nèi)存相關(guān)錯(cuò)誤的角色。

  • Richard Jones 和 Rafael Lins 合著的 Garbage Collection: Algorithms for Automatic Dynamic Memory Management 描述了當(dāng)前使用的最常見的垃圾收集算法。

  • 在 Donald Knuth 撰寫的 The Art of Computer Programming 第 1 卷 Fundamental Algorithms 的第 2.5 節(jié)“Dynamic Storage Allocation”中,描述了實(shí)現(xiàn)基本的分配程序的一些技術(shù)。

  • 在 Donald Knuth 撰寫的 The Art of Computer Programming 第 1 卷 Fundamental Algorithms 的第 2.3.5 節(jié)“Lists and Garbage Collection”中,討論了用于列表的垃圾收集算法。

  • Andrei Alexandrescu 撰寫的 Modern C++ Design 第 4 章“Small Object Allocation”描述了一個(gè)比 C++ 標(biāo)準(zhǔn)分配程序效率高得多的一個(gè)高速小對(duì)象分配程序。

  • Andrei Alexandrescu 撰寫的 Modern C++ Design 第 7 章“Smart Pointers”描述了在 C++ 中智能指針的實(shí)現(xiàn)。

  • Jonathan 撰寫的 Programming from the Ground Up 第 8 章“Intermediate Memory Topics”中有本文使用的簡(jiǎn)單分配程序的一個(gè)匯編語言版本。
來自 developerWorks
  • 自我管理數(shù)據(jù)緩沖區(qū)內(nèi)存 (developerWorks,2004 年 1 月)略述了一個(gè)用于管理內(nèi)存的自管理的抽象數(shù)據(jù)緩存器的偽 C (pseudo-C)實(shí)現(xiàn)。

  • A framework for the user defined malloc replacement feature (developerWorks,2002 年 2 月)展示了如何利用 AIX 中的一個(gè)工具,使用自己設(shè)計(jì)的內(nèi)存子系統(tǒng)取代原有的內(nèi)存子系統(tǒng)。

  • 掌握 Linux 調(diào)試技術(shù) (developerWorks,2002 年 8 月)描述了可以使用調(diào)試方法的 4 種不同情形:段錯(cuò)誤、內(nèi)存溢出、內(nèi)存泄漏和掛起。

  • 處理 Java 程序中的內(nèi)存漏洞 (developerWorks,2001 年 2 月)中,了解導(dǎo)致 Java 內(nèi)存泄漏的原因,以及何時(shí)需要考慮它們。

  • developerWorks Linux 專區(qū)中,可以找到更多為 Linux 開發(fā)人員準(zhǔn)備的參考資料。

  • 從 developerWorks 的 Speed-start your Linux app 專區(qū)中,可以下載運(yùn)行于 Linux 之上的 IBM 中間件產(chǎn)品的免費(fèi)測(cè)試版本,其中包括 WebSphere® Studio Application Developer、WebSphere Application Server、DB2® Universal Database、Tivoli® Access Manager 和 Tivoli Directory Server,查找 how-to 文章和技術(shù)支持。

  • 通過參與 developerWorks blogs 加入到 developerWorks 社區(qū)。

  • 可以在 Developer Bookstore Linux 專欄中定購(gòu) 打折出售的 Linux 書籍


關(guān)于作者

 

Jonathan Bartlett 是 Programming from the Ground Up 一書的作者,這本書介紹的是 Linux 匯編語言編程。Jonathan Bartlett 是 New Media Worx 的總開發(fā)師,負(fù)責(zé)為客戶開發(fā) Web、視頻、kiosk 和桌面應(yīng)用程序。您可以通過 johnnyb@eskimo.com 與 Jonathan 聯(lián)系。



]]>
BiJiao.javahttp://www.aygfsteel.com/xixidabao/archive/2006/10/15/75198.htmlJAVA之路JAVA之路Sat, 14 Oct 2006 16:15:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/10/15/75198.html{
?? boolean comp(Object a,Object b)
?? {
?? ? return a==b;
?? ?}
?? ?
?? ?boolean comp1(Object a,Object b)
?? ?{
?? ??return a.equals(b);
?? ?}
?? ?
??? boolean compInt(int i,int j)
?? ?{
?? ??return i==j;
?? ?}
?? ?
?? ?boolean compInt1(int i,int j)
?? ?{
?? ??return (Integer.valueOf(i)).equals(Integer.valueOf(j));
?? ?}
?? ?
??? public void biObject(Object a,Object b)
?? ?{
?? ??if(comp(a,b))
?? ??System.out.println("用\"==\"比較的結(jié)果:?????? "+"true");
?? ??else
?? ??System.out.println("用\"==\"比較的結(jié)果:?????? "+"false");
?? ??if(comp1(a,b))
?? ??System.out.println("用\"equals()\"比較的結(jié)果: "+"true");
?? ??else
?? ??System.out.println("用\"equals()\"比較的結(jié)果: "+"flase");
?? ?}
?? ?
?? ?public void biInt(int i,int j)
?? ?{
?? ??if(compInt(i,j))
?? ??System.out.println("用\"==\"比較的結(jié)果:?????? "+"true");
?? ??else
?? ??System.out.println("用\"==\"比較的結(jié)果:?????? "+"false");
?? ??if(compInt1(i,j))
?? ??System.out.println("用\"equals()\"比較的結(jié)果: "+"true");
?? ??else
?? ??System.out.println("用\"equals()\"比較的結(jié)果: "+"flase");
?? ?}
?? ??
?? ?public int test(String t)
?? ?? ?{
?? ?? ??int k=0;
?? ?? ?? String str="0123456789";
?? ?? ? for(int i=0;i<t.length();i++)
?? ?? ?{
?? ?? ??char ch1=(char)t.charAt(i);
?? ?? ??
?? ?? ??if(str.indexOf(ch1)==-1)
?? ?? ??? k++;
?? ?? ?}
?? ?? ?return k;
?? ??}
?? ??
?? ?public static void main(String[] args)
?? ?{
?? ?? BiJiao biJiao=new BiJiao();
?? ??
?? ?? if(args.length<2)
?? ?? {
?? ?? ?
?? ?? ? System.out.println("請(qǐng)輸入兩個(gè)字符或數(shù)字:");
?? ?? ? System.exit(0);
?? ?? ?}
?? ?? ?
?? ?? ?String a=args[0];
?? ?? ?String b=args[1];
?? ?? ?
?? ?? ?int k=biJiao.test(a);
?? ?? ?int p=biJiao.test(b);
?? ??
?? ?? ?
?? ?? ?if(k==0&&p==0)
?? ?? ?{
?? ?? ??System.out.println("您輸入的是數(shù)字:?????? "+a+" 和 "+b);
?? ?? ??int i=Integer.parseInt(a);
?? ?? ?? int j=Integer.parseInt(b);
?? ?? ??biJiao.biInt(i,j);
?? ?? ?}
?? ?? ?else
?? ?? ?? {
?? ?? ?? ?System.out.println("您輸入的是字符:???? "+a+" 和 "+b);
?? ?? ?? ?biJiao.biObject(a,b);
?? ?? ?? }
?? ?}
}
?? ?? ?

]]>
Java的垃圾回收(Garbage Collection)機(jī)制 http://www.aygfsteel.com/xixidabao/archive/2006/09/15/69776.htmlJAVA之路JAVA之路Thu, 14 Sep 2006 16:56:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/09/15/69776.html

??? 一。誰在做Garbage Collection?

??? 一種流行的說法:在C++里,是系統(tǒng)在做垃圾回收;而在Java里,是Java自身在做。

??? 在C++里,釋放內(nèi)存是手動(dòng)處理的,要用delete運(yùn)算符來釋放分配的內(nèi)存。這是流行的說法。確切地說,是應(yīng)用認(rèn)為不需要某實(shí)體時(shí),就需用delete告訴系統(tǒng),可以回收這塊空間了。這個(gè)要求,對(duì)編碼者來說,是件很麻煩、很難做到的事。隨便上哪個(gè)BBS,在C/C++版塊里總是有一大堆關(guān)于內(nèi)存泄漏的話題。

??? Java采用一種不同的,很方便的方法:Garbage Collection.垃圾回收機(jī)制放在JVM里。JVM完全負(fù)責(zé)垃圾回收事宜,應(yīng)用只在需要時(shí)申請(qǐng)空間,而在拋棄對(duì)象時(shí)不必關(guān)心空間回收問題。

??? 二。對(duì)象在啥時(shí)被丟棄?

??? 在C++里,當(dāng)對(duì)象離開其作用域時(shí),該對(duì)象即被應(yīng)用拋棄。

??? 是對(duì)象的生命期不再與其作用域有關(guān),而僅僅與引用有關(guān)。

??? Java的垃圾回收機(jī)制一般包含近十種算法。對(duì)這些算法中的多數(shù),我們不必予以關(guān)心。只有其中最簡(jiǎn)單的一個(gè):引用計(jì)數(shù)法,與編碼有關(guān)。

??? 一個(gè)對(duì)象,可以有一個(gè)或多個(gè)引用變量指向它。當(dāng)一個(gè)對(duì)象不再有任何一個(gè)引用變量指向它時(shí),這個(gè)對(duì)象就被應(yīng)用拋棄了。或者說,這個(gè)對(duì)象可以被垃圾回收機(jī)制回收了。

??? 這就是說,當(dāng)不存在對(duì)某對(duì)象的任何引用時(shí),就意味著,應(yīng)用告訴JVM:我不要這個(gè)對(duì)象,你可以回收了。

??? JVM的垃圾回收機(jī)制對(duì)堆空間做實(shí)時(shí)檢測(cè)。當(dāng)發(fā)現(xiàn)某對(duì)象的引用計(jì)數(shù)為0時(shí),就將該對(duì)象列入待回收列表中。但是,并不是馬上予以銷毀。

??? 三。丟棄就被回收?

??? 該對(duì)象被認(rèn)定為沒有存在的必要了,那么它所占用的內(nèi)存就可以被釋放。被回收的內(nèi)存可以用于后續(xù)的再分配。

??? 但是,并不是對(duì)象被拋棄后當(dāng)即被回收的。JVM進(jìn)程做空間回收有較大的系統(tǒng)開銷。如果每當(dāng)某應(yīng)用進(jìn)程丟棄一個(gè)對(duì)象,就立即回收它的空間,勢(shì)必會(huì)使整個(gè)系統(tǒng)的運(yùn)轉(zhuǎn)效率非常低下。

??? 前面說過,JVM的垃圾回收機(jī)制有多個(gè)算法。除了引用計(jì)數(shù)法是用來判斷對(duì)象是否已被拋棄外,其它算法是用來確定何時(shí)及如何做回收。JVM的垃圾回收機(jī)制要在時(shí)間和空間之間做個(gè)平衡。

??? 因此,為了提高系統(tǒng)效率,垃圾回收器通常只在滿足兩個(gè)條件時(shí)才運(yùn)行:即有對(duì)象要回收且系統(tǒng)需要回收。切記垃圾回收要占用時(shí)間,因此,Java運(yùn)行時(shí)系統(tǒng)只在需要的時(shí)候才使用它。因此你無法知道垃圾回收發(fā)生的精確時(shí)間。

??? 四。沒有引用變量指向的對(duì)象有用嗎?

??? 前面說了,沒掛上引用變量的對(duì)象是被應(yīng)用丟棄的,這意味著,它在堆空間里是個(gè)垃圾,隨時(shí)可能被JVM回收。

??? 不過,這里有個(gè)不是例外的例外。對(duì)于一次性使用的對(duì)象(有些書稱之為臨時(shí)對(duì)象),可以不用引用變量指向它。舉個(gè)最簡(jiǎn)單也最常見的例子:

??? System.out.println(“I am Java!”);

??? 就是創(chuàng)建了一個(gè)字符串對(duì)象后,直接傳遞給println()方法。

??? 五。應(yīng)用能干預(yù)垃圾回收嗎?

??? 許多人對(duì)Java的垃圾回收不放心,希望在應(yīng)用代碼里控制JVM的垃圾回收運(yùn)作。這是不可能的事。對(duì)垃圾回收機(jī)制來說,應(yīng)用只有兩個(gè)途徑發(fā)消息給JVM.第一個(gè)前面已經(jīng)說了,就是將指向某對(duì)象的所有引用變量全部移走。這就相當(dāng)于向JVM發(fā)了一個(gè)消息:這個(gè)對(duì)象不要了。第二個(gè)是調(diào)用庫方法System.gc(),多數(shù)書里說調(diào)用它讓Java做垃圾回收。

??? 第一個(gè)是一個(gè)告知,而調(diào)用System.gc()也僅僅是一個(gè)請(qǐng)求。JVM接受這個(gè)消息后,并不是立即做垃圾回收,而只是對(duì)幾個(gè)垃圾回收算法做了加權(quán),使垃圾回收操作容易發(fā)生,或提早發(fā)生,或回收較多而已。

??? 希望JVM及時(shí)回收垃圾,是一種需求。其實(shí),還有相反的一種需要:在某段時(shí)間內(nèi)最好不要回收垃圾。要求運(yùn)行速度最快的實(shí)時(shí)系統(tǒng),特別是嵌入式系統(tǒng),往往希望如此。

??? Java的垃圾回收機(jī)制是為所有Java應(yīng)用進(jìn)程服務(wù)的,而不是為某個(gè)特定的進(jìn)程服務(wù)的。因此,任何一個(gè)進(jìn)程都不能命令垃圾回收機(jī)制做什么、怎么做或做多少。

??? 六。對(duì)象被回收時(shí)要做的事

??? 一個(gè)對(duì)象在運(yùn)行時(shí),可能會(huì)有一些東西與其關(guān)連。因此,當(dāng)對(duì)象即將被銷毀時(shí),有時(shí)需要做一些善后工作。可以把這些操作寫在finalize()方法(常稱之為終止器)里。

??? protected void finalize()

??? {

??? // finalization code here

??? }

??? 這個(gè)終止器的用途類似于C++里的析構(gòu)函數(shù),而且都是自動(dòng)調(diào)用的。但是,兩者的調(diào)用時(shí)機(jī)不一樣,使兩者的表現(xiàn)行為有重大區(qū)別。C++的析構(gòu)函數(shù)總是當(dāng)對(duì)象離開作用域時(shí)被調(diào)用。這就是說,C++析構(gòu)函數(shù)的調(diào)用時(shí)機(jī)是確定的,且是可被應(yīng)用判知的。但是,Java終止器卻是在對(duì)象被銷毀時(shí)。由上所知,被丟棄的對(duì)象何時(shí)被銷毀,應(yīng)用是無法獲知的。而且,對(duì)于大多數(shù)場(chǎng)合,被丟棄對(duì)象在應(yīng)用終止后仍未銷毀。

??? 在編碼時(shí),考慮到這一點(diǎn)。譬如,某對(duì)象在運(yùn)作時(shí)打開了某個(gè)文件,在對(duì)象被丟棄時(shí)不關(guān)閉它,而是把文件關(guān)閉語句寫在終止器里。這樣做對(duì)文件操作會(huì)造成問題。如果文件是獨(dú)占打開的,則其它對(duì)象將無法訪問這個(gè)文件。如果文件是共享打開的,則另一訪問該文件的對(duì)象直至應(yīng)用終結(jié)仍不能讀到被丟棄對(duì)象寫入該文件的新內(nèi)容。

??? 至少對(duì)于文件操作,編碼者應(yīng)認(rèn)清Java終止器與C++析構(gòu)函數(shù)之間的差異。

??? 那么,當(dāng)應(yīng)用終止,會(huì)不會(huì)執(zhí)行應(yīng)用中的所有finalize()呢?據(jù)Bruce Eckel在Thinking in Java里的觀點(diǎn):“到程序結(jié)束的時(shí)候,并非所有收尾模塊都會(huì)得到調(diào)用”。這還僅僅是指應(yīng)用正常終止的場(chǎng)合,非正常終止呢?

??? 因此,哪些收尾操作可以放在finalize()里,是需要酌酎的。



]]>
怎樣取得class文件的路徑http://www.aygfsteel.com/xixidabao/archive/2006/09/15/69775.htmlJAVA之路JAVA之路Thu, 14 Sep 2006 16:52:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/09/15/69775.html
????在我們的應(yīng)用中,很多時(shí)候我們需要得到我們加載的類文件的路徑,那么我們?nèi)绾蔚玫侥?
????在此我使用了一種方法,也許可以給需要的朋友們提供一種參考.
????看我的一個(gè)例子代碼:
????package?org.zy.base;
????
????import?java.io.File;
????
????public?class?FileDemo?{
??????public?FileDemo()?{
????????super();
??????}
????
??????//----------------------------------------
????
??????public?String?getFileDirectory()?{
????
????????return?null;
??????}
????
??????public?String?getCurrentWorkingDirectory()?{
????????File?f?=?new?File(".");
????????return?f.getAbsolutePath();
??????}
????
??????public?String?getClassDirectory()?{
????????String?cla?=?this.getClass().getClassLoader().getResource(".").getPath();
????????return?cla;
??????}
????
??????//----------------------------------------------------------------------------
????
??????public?static?void?main(String[]?args)?{
????????FileDemo?filedemo?=?new?FileDemo();
????????System.out.println(filedemo.getCurrentWorkingDirectory());
????????System.out.println(filedemo.getClassDirectory());
??????}
????}
????
????運(yùn)行的結(jié)果是:
????G:\develop\demo\JSFDemo\.
????/G:/develop/demo/JSFDemo/classes/
????由此,我們看到,我們可以通過類加載器來取得其在加載類時(shí)的類路徑.
????getCurrentWorkingDirectory()方法得到市當(dāng)前的工程的工作目錄.
????以上是我的淺見,以供參考.

]]>
Java虛擬機(jī)類裝載的原理及實(shí)現(xiàn)http://www.aygfsteel.com/xixidabao/archive/2006/09/15/69774.htmlJAVA之路JAVA之路Thu, 14 Sep 2006 16:45:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/09/15/69774.html一、引言

  Java虛擬機(jī)(JVM)的類裝載就是指將包含在類文件中的字節(jié)碼裝載到JVM中, 并使其成為JVM一部分的過程。JVM的類動(dòng)態(tài)裝載技術(shù)能夠在運(yùn)行時(shí)刻動(dòng)態(tài)地加載或者替換系統(tǒng)的某些功能模塊, 而不影響系統(tǒng)其他功能模塊的正常運(yùn)行。本文將分析JVM中的類裝載系統(tǒng),探討JVM中類裝載的原理、實(shí)現(xiàn)以及應(yīng)用。
  
?? 二、Java虛擬機(jī)的類裝載實(shí)現(xiàn)與應(yīng)用

  2.1 裝載過程簡(jiǎn)介

  所謂裝載就是尋找一個(gè)類或是一個(gè)接口的二進(jìn)制形式并用該二進(jìn)制形式來構(gòu)造代表這個(gè)類或是這個(gè)接口的class對(duì)象的過程,其中類或接口的名稱是給定了的。當(dāng)然名稱也可以通過計(jì)算得到,但是更常見的是通過搜索源代碼經(jīng)過編譯器編譯后所得到的二進(jìn)制形式來構(gòu)造。

  在Java中,類裝載器把一個(gè)類裝入Java虛擬機(jī)中,要經(jīng)過三個(gè)步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗(yàn)、準(zhǔn)備和解析三步,除了解析外,其它步驟是嚴(yán)格按照順序完成的,各個(gè)步驟的主要工作如下:

  裝載:查找和導(dǎo)入類或接口的二進(jìn)制數(shù)據(jù);

  鏈接:執(zhí)行下面的校驗(yàn)、準(zhǔn)備和解析步驟,其中解析步驟是可以選擇的;

  校驗(yàn):檢查導(dǎo)入類或接口的二進(jìn)制數(shù)據(jù)的正確性;

  準(zhǔn)備:給類的靜態(tài)變量分配并初始化存儲(chǔ)空間;

  解析:將符號(hào)引用轉(zhuǎn)成直接引用;

  初始化:激活類的靜態(tài)變量的初始化Java代碼和靜態(tài)Java代碼塊。

  至于在類裝載和虛擬機(jī)啟動(dòng)的過程中的具體細(xì)節(jié)和可能會(huì)拋出的錯(cuò)誤,請(qǐng)參看《Java虛擬機(jī)規(guī)范》以及《深入Java虛擬機(jī)》。 由于本文的討論重點(diǎn)不在此就不再多敘述。

  2.2 裝載的實(shí)現(xiàn)

  JVM中類的裝載是由ClassLoader和它的子類來實(shí)現(xiàn)的,Java ClassLoader 是一個(gè)重要的Java運(yùn)行時(shí)系統(tǒng)組件。它負(fù)責(zé)在運(yùn)行時(shí)查找和裝入類文件的類。

  在Java中,ClassLoader是一個(gè)抽象類,它在包java.lang中,可以這樣說,只要了解了在ClassLoader中的一些重要的方法,再結(jié)合上面所介紹的JVM中類裝載的具體的過程,對(duì)動(dòng)態(tài)裝載類這項(xiàng)技術(shù)就有了一個(gè)比較大概的掌握,這些重要的方法包括以下幾個(gè):

  ①loadCass方法 loadClass(String name ,boolean resolve)其中name參數(shù)指定了JVM需要的類的名稱,該名稱以包表示法表示,如Java.lang.Object;resolve參數(shù)告訴方法是否需要解析類,在初始化類之前,應(yīng)考慮類解析,并不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要解析。這個(gè)方法是ClassLoader 的入口點(diǎn)。

  ②defineClass方法 這個(gè)方法接受類文件的字節(jié)數(shù)組并把它轉(zhuǎn)換成Class對(duì)象。字節(jié)數(shù)組可以是從本地文件系統(tǒng)或網(wǎng)絡(luò)裝入的數(shù)據(jù)。它把字節(jié)碼分析成運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)、校驗(yàn)有效性等等。

  ③findSystemClass方法 findSystemClass方法從本地文件系統(tǒng)裝入文件。它在本地文件系統(tǒng)中尋找類文件,如果存在,就使用defineClass將字節(jié)數(shù)組轉(zhuǎn)換成Class對(duì)象,以將該文件轉(zhuǎn)換成類。當(dāng)運(yùn)行Java應(yīng)用程序時(shí),這是JVM 正常裝入類的缺省機(jī)制。

  ④resolveClass方法 resolveClass(Class c)方法解析裝入的類,如果該類已經(jīng)被解析過那么將不做處理。當(dāng)調(diào)用loadClass方法時(shí),通過它的resolve 參數(shù)決定是否要進(jìn)行解析。

  ⑤findLoadedClass方法 當(dāng)調(diào)用loadClass方法裝入類時(shí),調(diào)用findLoadedClass 方法來查看ClassLoader是否已裝入這個(gè)類,如果已裝入,那么返回Class對(duì)象,否則返回NULL。如果強(qiáng)行裝載已存在的類,將會(huì)拋出鏈接錯(cuò)誤。
 
2.3 裝載的應(yīng)用

  一般來說,我們使用虛擬機(jī)的類裝載時(shí)需要繼承抽象類java.lang.ClassLoader,其中必須實(shí)現(xiàn)的方法是loadClass(),對(duì)于這個(gè)方法需要實(shí)現(xiàn)如下操作:(1) 確認(rèn)類的名稱;(2) 檢查請(qǐng)求要裝載的類是否已經(jīng)被裝載;(3) 檢查請(qǐng)求加載的類是否是系統(tǒng)類;(4) 嘗試從類裝載器的存儲(chǔ)區(qū)獲取所請(qǐng)求的類;(5) 在虛擬機(jī)中定義所請(qǐng)求的類;(6) 解析所請(qǐng)求的類;(7) 返回所請(qǐng)求的類。

  所有的Java 虛擬機(jī)都包括一個(gè)內(nèi)置的類裝載器,這個(gè)內(nèi)置的類庫裝載器被稱為根裝載器(bootstrap ClassLoader)。根裝載器的特殊之處是它只能夠裝載在設(shè)計(jì)時(shí)刻已知的類,因此虛擬機(jī)假定由根裝載器所裝載的類都是安全的、可信任的,可以不經(jīng)過安全認(rèn)證而直接運(yùn)行。當(dāng)應(yīng)用程序需要加載并不是設(shè)計(jì)時(shí)就知道的類時(shí),必須使用用戶自定義的裝載器(user-defined ClassLoader)。下面我們舉例說明它的應(yīng)用。

public abstract class MultiClassLoader extends ClassLoader{
 ...
 public synchronized Class loadClass(String s, boolean flag)
  throws ClassNotFoundException
  {
   /* 檢查類s是否已經(jīng)在本地內(nèi)存*/
   Class class1 = (Class)classes.get(s);

   /* 類s已經(jīng)在本地內(nèi)存*/
   if(class1 != null) return class1;
   try/*用默認(rèn)的ClassLoader 裝入類*/ {
    class1 = super.findSystemClass(s);
    return class1;
   }
   catch(ClassNotFoundException _ex) {
    System.out.println(">> Not a system class.");
   }

   /* 取得類s的字節(jié)數(shù)組*/
   byte abyte0[] = loadClassBytes(s);
   if(abyte0 == null) throw new ClassNotFoundException();
   /* 將類字節(jié)數(shù)組轉(zhuǎn)換為類*/
   class1 = defineClass(null, abyte0, 0, abyte0.length);
   if(class1 == null) throw new ClassFormatError();
   if(flag) resolveClass(class1); /*解析類*/
   /* 將新加載的類放入本地內(nèi)存*/
   classes.put(s, class1);
   System.out.println(">> Returning newly loaded class.");

   /* 返回已裝載、解析的類*/
   return class1;
  }
  ...
}

  三、Java虛擬機(jī)的類裝載原理

  前面我們已經(jīng)知道,一個(gè)Java應(yīng)用程序使用兩種類型的類裝載器:根裝載器(bootstrap)和用戶定義的裝載器(user-defined)。根裝載器是Java虛擬機(jī)實(shí)現(xiàn)的一部分,舉個(gè)例子來說,如果一個(gè)Java虛擬機(jī)是在現(xiàn)在已經(jīng)存在并且正在被使用的操作系統(tǒng)的頂部用C程序來實(shí)現(xiàn)的,那么根裝載器將是那些C程序的一部分。根裝載器以某種默認(rèn)的方式將類裝入,包括那些Java API的類。在運(yùn)行期間一個(gè)Java程序能安裝用戶自己定義的類裝載器。根裝載器是虛擬機(jī)固有的一部分,而用戶定義的類裝載器則不是,它是用Java語言寫的,被編譯成class文件之后然后再被裝入到虛擬機(jī),并像其它的任何對(duì)象一樣可以被實(shí)例化。 Java類裝載器的體系結(jié)構(gòu)如下所示:


圖1 Java的類裝載的體系結(jié)構(gòu)

  Java的類裝載模型是一種代理(delegation)模型。當(dāng)JVM 要求類裝載器CL(ClassLoader)裝載一個(gè)類時(shí),CL首先將這個(gè)類裝載請(qǐng)求轉(zhuǎn)發(fā)給他的父裝載器。只有當(dāng)父裝載器沒有裝載并無法裝載這個(gè)類時(shí),CL才獲得裝載這個(gè)類的機(jī)會(huì)。這樣, 所有類裝載器的代理關(guān)系構(gòu)成了一種樹狀的關(guān)系。樹的根是類的根裝載器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根裝載器以外的類裝載器有且僅有一個(gè)父裝載器。在創(chuàng)建一個(gè)裝載器時(shí), 如果沒有顯式地給出父裝載器, 那么JVM將默認(rèn)系統(tǒng)裝載器為其父裝載器。Java的基本類裝載器代理結(jié)構(gòu)如圖2所示:


圖2 Java類裝載的代理結(jié)構(gòu)

  下面針對(duì)各種類裝載器分別進(jìn)行詳細(xì)的說明。

  根(Bootstrap) 裝載器:該裝載器沒有父裝載器,它是JVM實(shí)現(xiàn)的一部分,從sun.boot.class.path裝載運(yùn)行時(shí)庫的核心代碼。

  擴(kuò)展(Extension) 裝載器:繼承的父裝載器為根裝載器,不像根裝載器可能與運(yùn)行時(shí)的操作系統(tǒng)有關(guān),這個(gè)類裝載器是用純Java代碼實(shí)現(xiàn)的,它從java.ext.dirs (擴(kuò)展目錄)中裝載代碼。

  系統(tǒng)(System or Application) 裝載器:裝載器為擴(kuò)展裝載器,我們都知道在安裝JDK的時(shí)候要設(shè)置環(huán)境變量(CLASSPATH ),這個(gè)類裝載器就是從java.class.path(CLASSPATH 環(huán)境變量)中裝載代碼的,它也是用純Java代碼實(shí)現(xiàn)的,同時(shí)還是用戶自定義類裝載器的缺省父裝載器。

  小應(yīng)用程序(Applet) 裝載器: 裝載器為系統(tǒng)裝載器,它從用戶指定的網(wǎng)絡(luò)上的特定目錄裝載小應(yīng)用程序代碼。

  在設(shè)計(jì)一個(gè)類裝載器的時(shí)候,應(yīng)該滿足以下兩個(gè)條件:

  對(duì)于相同的類名,類裝載器所返回的對(duì)象應(yīng)該是同一個(gè)類對(duì)象

  如果類裝載器CL1將裝載類C的請(qǐng)求轉(zhuǎn)給類裝載器CL2,那么對(duì)于以下的類或接口,CL1和CL2應(yīng)該返回同一個(gè)類對(duì)象:a)S為C的直接超類;b)S為C的直接超接口;c)S為C的成員變量的類型;d)S為C的成員方法或構(gòu)建器的參數(shù)類型;e)S為C的成員方法的返回類型。

  每個(gè)已經(jīng)裝載到JVM中的類都隱式含有裝載它的類裝載器的信息。類方法getClassLoader 可以得到裝載這個(gè)類的類裝載器。一個(gè)類裝載器認(rèn)識(shí)的類包括它的父裝載器認(rèn)識(shí)的類和它自己裝載的類,可見類裝載器認(rèn)識(shí)的類是它自己裝載的類的超集。注意我們可以得到類裝載器的有關(guān)的信息,但是已經(jīng)裝載到JVM中的類是不能更改它的類裝載器的。

  Java中的類的裝載過程也就是代理裝載的過程。比如:Web瀏覽器中的JVM需要裝載一個(gè)小應(yīng)用程序TestApplet。JVM調(diào)用小應(yīng)用程序裝載器ACL(Applet ClassLoader)來完成裝載。ACL首先請(qǐng)求它的父裝載器, 即系統(tǒng)裝載器裝載TestApplet是否裝載了這個(gè)類, 由于TestApplet不在系統(tǒng)裝載器的裝載路徑中, 所以系統(tǒng)裝載器沒有找到這個(gè)類, 也就沒有裝載成功。接著ACL自己裝載TestApplet。ACL通過網(wǎng)絡(luò)成功地找到了TestApplet.class 文件并將它導(dǎo)入到了JVM中。在裝載過程中, JVM發(fā)現(xiàn)TestAppet是從超類java.applet.Applet繼承的。所以JVM再次調(diào)用ACL來裝載java.applet.Applet類。ACL又再次按上面的順序裝載Applet類, 結(jié)果ACL發(fā)現(xiàn)他的父裝載器已經(jīng)裝載了這個(gè)類, 所以ACL就直接將這個(gè)已經(jīng)裝載的類返回給了JVM , 完成了Applet類的裝載。接下來,Applet類的超類也一樣處理。最后, TestApplet及所有有關(guān)的類都裝載到了JVM中。

  四、結(jié)論

  類的動(dòng)態(tài)裝載機(jī)制是JVM的一項(xiàng)核心技術(shù), 也是容易被忽視而引起很多誤解的地方。本文介紹了JVM中類裝載的原理、實(shí)現(xiàn)以及應(yīng)用,尤其分析了ClassLoader的結(jié)構(gòu)、用途以及如何利用自定義的ClassLoader裝載并執(zhí)行Java類,希望能使讀者對(duì)JVM中的類裝載有一個(gè)比較深入的理解。




]]>
在 Java 應(yīng)用程序中訪問USB設(shè)備http://www.aygfsteel.com/xixidabao/archive/2006/06/18/53628.htmlJAVA之路JAVA之路Sun, 18 Jun 2006 12:42:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/06/18/53628.html閱讀全文

]]>
java編寫TCP&&UDP方式的通信程序http://www.aygfsteel.com/xixidabao/archive/2006/05/27/48514.htmlJAVA之路JAVA之路Sat, 27 May 2006 12:55:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/27/48514.htmlJava API中封裝了大量的函數(shù),供編寫網(wǎng)絡(luò)通信程序時(shí)使用. 這使得java在網(wǎng)絡(luò)方面具有強(qiáng)大的功能. 用java編寫TCP方式的通信程序比較簡(jiǎn)單,但也有一些問題需要注意. 以下為監(jiān)聽主程序,監(jiān)聽程序在發(fā)現(xiàn)客戶端連接后,啟動(dòng)一個(gè)會(huì)話socket線程,以實(shí)現(xiàn)實(shí)時(shí)發(fā)送,接收信息 和多客戶端同時(shí)工作. import java.io.*; import java.lang.*; import java.net.ServerSocket; import java.net.Socket; //主程序一直處于監(jiān)聽狀態(tài),有連接則啟動(dòng)一個(gè)線程進(jìn)行處理,以實(shí)現(xiàn)多個(gè)客戶端 public class listenserve { private ServerSocket ss; private boolean listening=true; public listenserve() { Init();//初始化 lisn();//啟動(dòng)監(jiān)聽 } public void Init() { try { ss=new ServerSocket(10015,10); } catch(IOException ie) { System.out.println("無法在10015端口監(jiān)聽"); ie.printStackTrace(); } } public void lisn() { try { while(listening) new Thread(new dialogserve(ss.accept())).start(); } catch(IOException ie) {ie.printStackTrace();} } public static void main(String args[]) { new listenserve(); } } //以下為會(huì)話主程序 應(yīng)該特別注意,如果客戶端先關(guān)閉,會(huì)話socket中可能拋出socketexception:connection reset 這應(yīng)該在程序中進(jìn)行處理,這也是較易忽略的問題. import java.io.*; import java.lang.*; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; public class dialogserve implements Runnable { private Socket s; private InputStream in; private String rev,temp; private byte b[]; private int len; public dialogserve(Socket ss) { s=ss; b=new byte[1024]; try { in=s.getInputStream(); }catch(IOException ie) { ie.printStackTrace(); } rev=""; } public void run() { try { while(s.isConnected()==true) { if((len=in.read(b))!=-1) { temp=new String(b,0,len); rev+=temp; System.out.print(rev); temp=null; Thread.sleep(1000); } } in.close(); s.close(); System.out.println("會(huì)話socket已斷開!"); } catch(SocketException se) { System.out.println("客戶端已斷開!"); System.exit(0); } catch(IOException io) { io.printStackTrace(); System.exit(0); } catch(InterruptedException ire) { ire.printStackTrace();} } } //以下為客戶端主程序 import java.io.*; import java.net.Socket; import java.lang.*; public class client { private Socket con;//客戶端連接socket private OutputStream out; private String sen; private byte b[]; public client() { clientInit(); } public void clientInit() { try { con=new Socket("localhost",10015); con.setSoTimeout(10000); b=new byte[1024]; OutputStream out=con.getOutputStream(); sen="hello serve,以TCP方式發(fā)送數(shù)據(jù)!"; b=sen.getBytes(); out.write(b); out.flush(); out.close(); con.close(); } catch(IOException ie) { ie.toString(); } } public static void main(String args[]) { new client(); } } 總的來說,以上所列代碼較為簡(jiǎn)單,但已基本反映出java編寫簡(jiǎn)單tcp通信程序的原理. 希望各位朋友批評(píng).大家共同學(xué)習(xí)交流.
什么是UDP協(xié)議

  UDP協(xié)議的全稱是用戶數(shù)據(jù)報(bào),在網(wǎng)絡(luò)中它與TCP協(xié)議一樣用于處理數(shù)據(jù)包。在OSI模型中,在第四層——傳輸層,處于IP協(xié)議的上一層。UDP有不提供數(shù)據(jù)報(bào)分組、組裝和不能對(duì)數(shù)據(jù)包的排序的缺點(diǎn),也就是說,當(dāng)報(bào)文發(fā)送之后,是無法得知其是否安全完整到達(dá)的。

  為什么要使用UDP

  在選擇使用協(xié)議的時(shí)候,選擇UDP必須要謹(jǐn)慎。在網(wǎng)絡(luò)質(zhì)量令人不十分滿意的環(huán)境下,UDP協(xié)議數(shù)據(jù)包丟失會(huì)比較嚴(yán)重。但是由于UDP的特性:它不屬于連接型協(xié)議,因而具有資源消耗小,處理速度快的優(yōu)點(diǎn),所以通常音頻、視頻和普通數(shù)據(jù)在傳送時(shí)使用UDP較多,因?yàn)樗鼈兗词古紶杹G失一兩個(gè)數(shù)據(jù)包,也不會(huì)對(duì)接收結(jié)果產(chǎn)生太大影響。比如我們聊天用的ICQ和OICQ就是使用的UDP協(xié)議。

  在Java中操縱UDP

  使用位于JDK中Java.net包下的DatagramSocket和DatagramPacket類,可以非常方便地控制用戶數(shù)據(jù)報(bào)文。

  在描述它們之前,必須了解位于同一個(gè)位置的InetAddress類。InetAddress實(shí)現(xiàn)了Java.io. Serializable接口,不允許繼承。它用于描述和包裝一個(gè)Internet IP地址,通過三個(gè)方法返回InetAddress實(shí)例:

  getLocalhost():返回封裝本地地址的實(shí)例。

  getAllByName(String host):返回封裝Host地址的InetAddress實(shí)例數(shù)組。

  getByName(String host):返回一個(gè)封裝Host地址的實(shí)例。其中,Host可以是域名或者是一個(gè)合法的IP地址。

  DatagramSocket類用于創(chuàng)建接收和發(fā)送UDP的Socket實(shí)例。和Socket類依賴SocketImpl類一樣,DatagramSocket類的實(shí)現(xiàn)也依靠專門為它設(shè)計(jì)的DatagramScoketImplFactory類。DatagramSocket類有3個(gè)構(gòu)建器:

  DatagramSocket():創(chuàng)建實(shí)例。這是個(gè)比較特殊的用法,通常用于客戶端編程,它并沒有特定監(jiān)聽的端口,僅僅使用一個(gè)臨時(shí)的。

  DatagramSocket(int port):創(chuàng)建實(shí)例,并固定監(jiān)聽Port端口的報(bào)文。

  DatagramSocket(int port, InetAddress localAddr):這是個(gè)非常有用的構(gòu)建器,當(dāng)一臺(tái)機(jī)器擁有多于一個(gè)IP地址的時(shí)候,由它創(chuàng)建的實(shí)例僅僅接收來自LocalAddr的報(bào)文。

  值得注意的是,在創(chuàng)建DatagramSocket類實(shí)例時(shí),如果端口已經(jīng)被使用,會(huì)產(chǎn)生一個(gè)SocketException的異常拋出,并導(dǎo)致程序非法終止,這個(gè)異常應(yīng)該注意捕獲。DatagramSocket類最主要的方法有4個(gè):

  Receive(DatagramPacket d):接收數(shù)據(jù)報(bào)文到d中。receive方法產(chǎn)生一個(gè)“阻塞”。

  Send(DatagramPacket d):發(fā)送報(bào)文d到目的地。

  SetSoTimeout(int timeout):設(shè)置超時(shí)時(shí)間,單位為毫秒。

  Close():關(guān)閉DatagramSocket。在應(yīng)用程序退出的時(shí)候,通常會(huì)主動(dòng)釋放資源,關(guān)閉Socket,但是由于異常地退出可能造成資源無法回收。所以,應(yīng)該在程序完成時(shí),主動(dòng)使用此方法關(guān)閉Socket,或在捕獲到異常拋出后關(guān)閉Socket。

  “阻塞”是一個(gè)專業(yè)名詞,它會(huì)產(chǎn)生一個(gè)內(nèi)部循環(huán),使程序暫停在這個(gè)地方,直到一個(gè)條件觸發(fā)。

  DatagramPacket類用于處理報(bào)文,它將Byte數(shù)組、目標(biāo)地址、目標(biāo)端口等數(shù)據(jù)包裝成報(bào)文或者將報(bào)文拆卸成Byte數(shù)組。應(yīng)用程序在產(chǎn)生數(shù)據(jù)包是應(yīng)該注意,TCP/IP規(guī)定數(shù)據(jù)報(bào)文大小最多包含65507個(gè),通常主機(jī)接收548個(gè)字節(jié),但大多數(shù)平臺(tái)能夠支持8192字節(jié)大小的報(bào)文。DatagramPacket類的構(gòu)建器共有4個(gè):

  DatagramPacket(byte[] buf, int length, InetAddress addr, int port):從Buf數(shù)組中,取出Length長(zhǎng)的數(shù)據(jù)創(chuàng)建數(shù)據(jù)包對(duì)象,目標(biāo)是Addr地址,Port端口。

  DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):從Buf數(shù)組中,取出Offset開始的、Length長(zhǎng)的數(shù)據(jù)創(chuàng)建數(shù)據(jù)包對(duì)象,目標(biāo)是Addr地址,Port端口。

  DatagramPacket(byte[] buf, int offset, int length):將數(shù)據(jù)包中從Offset開始、Length長(zhǎng)的數(shù)據(jù)裝進(jìn)Buf數(shù)組。

  DatagramPacket(byte[] buf, int length):將數(shù)據(jù)包中Length長(zhǎng)的數(shù)據(jù)裝進(jìn)Buf數(shù)組。

  DatagramPacket類最重要的方法就是getData()了,它從實(shí)例中取得報(bào)文的Byte數(shù)組編碼。

  ★簡(jiǎn)單的實(shí)例說明

{接收數(shù)據(jù)的服務(wù)器}
byte[] buf = new byte[1000];
DatagramSocket ds = new DatagramSocket(12345);
//開始監(jiān)視12345端口
DatagramPacket ip = new DatagramPacket(buf, buf.length);
//創(chuàng)建接收數(shù)據(jù)報(bào)的實(shí)例
while (true)
  {
  ds.receive(ip);
  //阻塞,直到收到數(shù)據(jù)報(bào)后將數(shù)據(jù)裝入IP中
  System.out.println(new String(buf));
  }
  {發(fā)送數(shù)據(jù)的客戶端}
  InetAddress target = InetAddress.getByName(“www.xxx.com“);
  //得到目標(biāo)機(jī)器的地址實(shí)例
  DatagramSocket ds = new DatagramSocket(9999);
  //從9999端口發(fā)送數(shù)據(jù)報(bào)
  String hello = “Hello, I am come in!”;
  //要發(fā)送的數(shù)據(jù)
  byte[] buf = hello.getBytes();
  //將數(shù)據(jù)轉(zhuǎn)換成Byte類型
  op = new DatagramPacket(buf, buf.length, target, 12345);
  //將BUF緩沖區(qū)中的數(shù)據(jù)打包
  ds.send(op);
  //發(fā)送數(shù)據(jù)
  ds.close();
  //關(guān)閉連接


]]>
實(shí)例分析Java SE 6.0新增功能http://www.aygfsteel.com/xixidabao/archive/2006/05/26/48346.htmlJAVA之路JAVA之路Fri, 26 May 2006 08:09:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/26/48346.html  Java 6.0標(biāo)準(zhǔn)版(Mustang)包含了大量使Java開發(fā)更為容易的特性。在本文中,我們將討論通過部分新特性來幫助你實(shí)現(xiàn)如下功能:

  · 設(shè)置文件和目錄許可權(quán)

  · 獲取分區(qū)上自由空間和可用空間數(shù)

  · 把Component對(duì)象添加到JTabbedPane的選項(xiàng)卡上

  · 在你的Java基礎(chǔ)類/Swing(JFC/Swing)應(yīng)用程序中使用流行的SwingWorker類

  因此,如果JSR 270專家組同意采納這些特征,那么在Mustang的下一個(gè)發(fā)行版本中你就會(huì)看到這些特征。

  注意:為了運(yùn)行本文中的源碼,你必須下載并安裝Mustang的最新版本。

  一、 設(shè)置文件和目錄權(quán)限

  現(xiàn)在,從Mustang build 31開始,你可以在本地文件系統(tǒng)中設(shè)置一個(gè)文件的可讀、可寫和可執(zhí)行標(biāo)志。這項(xiàng)功能已經(jīng)被添加到j(luò)ava.io.File類中,并通過使用下列方法來實(shí)現(xiàn):

public boolean setReadable(boolean readable, boolean ownerOnly)
public boolean setReadable(boolean readable)
public boolean setWritable(boolean writable, boolean ownerOnly)
public boolean setWritable(boolean writable)
public boolean setExecutable(boolean executable, boolean ownerOnly)
public boolean setExecutable(boolean executable)

  如果你曾某種UNIX系統(tǒng)上工作過,那么你應(yīng)該對(duì)這些方法非常熟悉-其實(shí)它們實(shí)現(xiàn)了chmod命令的一些功能。這些方法試圖設(shè)置由現(xiàn)在的File對(duì)象所描述的文件或目錄的適當(dāng)權(quán)限。如果把第二個(gè)可選參數(shù)設(shè)置為true,那么該權(quán)限將僅應(yīng)用于當(dāng)前所有者標(biāo)志。否則,這些方法將應(yīng)用到所有用戶。注意,如果底層文件系統(tǒng)沒法區(qū)分該所有者和其他所有者的權(quán)限(在一些版本的Windows中就是這樣),那么這一權(quán)限將應(yīng)用到每一個(gè)人,而不管傳遞的是什么值。

  如果你是一個(gè)使用NT文件系統(tǒng)的Windows用戶,那么你應(yīng)該讀一下這個(gè)文檔,它解釋了如何使用各種不同的選項(xiàng)來控制不同用戶的文件存取權(quán)限問題。

  如你所想,如果用戶沒有權(quán)限來改變這個(gè)抽象路徑名的存取權(quán)限,那么第一個(gè)方法就會(huì)失敗(也就是說,返回false);而且,這些方法也會(huì)拋出一個(gè)java.lang.SecurityException異常-如果存在一個(gè)Java安全管理器并且它的checkRead()/checkWrite()/checkExecute()方法不允許存取該文件的話。

  下表1顯示了在多種文件系統(tǒng)上運(yùn)行這些命令的典型結(jié)果,以及這些命令在不同目標(biāo)操作系統(tǒng)上的可用性。

  表1.在常用OS文件系統(tǒng)上的java.io.File權(quán)限操作

命令在Windows XP系統(tǒng)上的返回值在Linux系統(tǒng)上的返回值在solaris系統(tǒng)上的返回值
setReadable(true)true True(等價(jià)于chmod+r)True(等價(jià)于chmod+r)
setReadable(false)False(在Windows中文件可讀性不能被設(shè)置為False)True(等價(jià)于chmod-r)True(等價(jià)于chmod-r)
setWritable(true)True(切換Windows的只讀文件屬性)True(等價(jià)于chmod+w)True(等價(jià)于chmod+w)
setWritable(false) true(切換Windows的只讀文件屬性)True(等價(jià)于chmod-w)True(等價(jià)于chmod-w)
setExecutable(true)trueTrue(等價(jià)于chmod+x)True(等價(jià)于chmod+x)
setExecutable(false)false(在Windows中文件可執(zhí)行屬性不能被設(shè)置為False)True(等價(jià)于chmod-x)True(等價(jià)于chmod-x)

  決定是否文件是可讀,可寫或可執(zhí)行的方法與這個(gè)平臺(tái)的前一個(gè)版本-Java 2平臺(tái),標(biāo)準(zhǔn)版(J2SE)5.0-保持一致。

public boolean canRead();
public boolean canWrite();
public boolean canExecute();

  二、 取得硬盤分配空間

  除了允許你設(shè)置文件和目錄權(quán)限外,Mustang還為你提供了三個(gè)新方法來決定當(dāng)前磁盤分區(qū)中的可用空間數(shù),這是由一個(gè)java.io.File對(duì)象來描述的:

public long getTotalSpace();
public long getFreeSpace();
public long getUsableSpace();

  每一個(gè)這些方法返回要求的由java.io.File所描述的分區(qū)的字節(jié)大小,否則,如果從File對(duì)象中無法取得一個(gè)分區(qū)則返回值為0L。

  借助于getFreeSpace()和getUsableSpace()方法,未分配字節(jié)的返回?cái)?shù)是(根據(jù)有關(guān)文檔):"這僅是一種提示而不是保證-有可能使用大多數(shù)或所有這些字節(jié);但緊跟這個(gè)調(diào)用之后的未分配的字節(jié)數(shù)很可能是準(zhǔn)確的,當(dāng)然也有可能因某些外部I/O操作(包括在該虛擬機(jī)外面所作的系統(tǒng)調(diào)用)而導(dǎo)致不準(zhǔn)確。"

  那么,在這個(gè)兩個(gè)方法之間有什么區(qū)別呢?getFreeSpace()方法返回分區(qū)的自由空間數(shù)量的一個(gè)即時(shí)數(shù)。而getUsableSpace()方法還包含了另外一些功能來檢查寫許可和其它操作系統(tǒng)限制,這將返回一個(gè)可用空間數(shù)的更好的估計(jì)值。如果你想決定在寫向一個(gè)文件之前是否你有足夠的磁盤空間,那么,典型情況下getUsableSpace()將給你一個(gè)更精確的估計(jì)值。注意,如果安裝了一個(gè)安全管理器并且它不允許對(duì)于RuntimePermission("getFileSystemAttributes")進(jìn)行調(diào)用,那么這兩個(gè)方法都將拋出一個(gè)SecurityException異常。


 三、 使用Component描述JTabbedPane中的選項(xiàng)卡

  這是Swing的JtabbedPane中的一處微妙但是很有價(jià)值的改進(jìn)。在以前的JtabbedPane中,你被限制僅用一個(gè)字符串,一個(gè)圖標(biāo)(或二者的結(jié)合)來描述一個(gè)選項(xiàng)卡。另外,如果你想的話,你還可以把一個(gè)提示小窗加到該選項(xiàng)卡上去。從Mustang的build 39開始,現(xiàn)在有可能使用一個(gè)Component來描述JtabbedPane中的一個(gè)選項(xiàng)卡。盡管這可能帶來一系列的問題,但是,這種特性的最常用的方式是:添加一個(gè)Close按鈕-它將從JTabbedPane中刪除該選項(xiàng)卡。

  Sun程序員和Swing工程師Alexander Potochkin在他的最近的一個(gè)有關(guān)這個(gè)主題的博客日志中指出,這三個(gè)新方法已經(jīng)被添加到JTabbedPane。

  你可以使用下列方法把Component設(shè)置為一個(gè)選項(xiàng)卡:

public void setTabComponentAt(int index, Component component)

  你可以使用下列方法得到這個(gè)組件:

public Component getTabComponentAt(int index)

  你可以使用下列這個(gè)方法來測(cè)試是否有組件被應(yīng)用于這個(gè)JtabbedPane中:

public int indexOfTabComponent(Component tabComponent)

  下面是一個(gè)選項(xiàng)卡面板示例源代碼-它允許你從一個(gè)JTabbedPane中動(dòng)態(tài)地添加和刪除選項(xiàng)卡。注意,在這個(gè)例子中我們創(chuàng)建了一個(gè)Jpanel,它包含兩個(gè)組件:一個(gè)位于面板左邊(BorderLayout.WEST)的JLabel和一個(gè)位于面板右邊(BorderLayout.EAST)的帶有一個(gè)ImageIcon的按鈕。這里所用的圖形是一個(gè)10x10像素大小的gif文件-它包含了一個(gè)小X。為了確保按鈕的尺寸小一些,我們把它的尺寸重置為圖標(biāo)的寬度和高度各自加上2個(gè)像素。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TabbedPaneExample implements ActionListener {
 private JFrame frame;
 private JTabbedPane tabbedPane;
 private JButton addTabButton;
 private ImageIcon closeXIcon;
 private Dimension closeButtonSize;
 private int tabCounter = 0;
 public TabbedPaneExample() {
  //創(chuàng)建選項(xiàng)卡面板
  tabbedPane = new JTabbedPane();
  //創(chuàng)建一個(gè)按鈕-用戶可用來添加一個(gè)選項(xiàng)卡到選項(xiàng)卡面板
  addTabButton = new JButton("Add Tab");
  addTabButton.addActionListener(this);
  //創(chuàng)建一個(gè)框架來包含這個(gè)選項(xiàng)卡面板
  frame = new JFrame();
  //創(chuàng)建一個(gè)圖像圖標(biāo)'X'以實(shí)現(xiàn)在每一個(gè)選項(xiàng)卡上的關(guān)閉功能。加載的gif是一個(gè)10x10圖形(非黑色部分是透明的)
  closeXIcon = new ImageIcon("C:/CloseX.gif");
  //創(chuàng)建一個(gè)Dimension用來調(diào)整close按鈕的大小
  closeButtonSize = new Dimension(
   closeXIcon.getIconWidth()+2,
   closeXIcon.getIconHeight()+2);
   //所選項(xiàng)卡面板添加到圖形中央,把"Add Tab"按鈕置于南面。然后包裝它,調(diào)整其大小并顯示它。
  frame.add(tabbedPane, BorderLayout.CENTER);
  frame.add(addTabButton, BorderLayout.SOUTH);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.pack();
  frame.setMinimumSize(new Dimension(300, 300));
  frame.setVisible(true);
 }
 public void actionPerformed(ActionEvent e) {
  final JPanel content = new JPanel();
  //創(chuàng)建一個(gè)描述該選項(xiàng)卡的面板并確保它是透明的
  JPanel tab = new JPanel();
  tab.setOpaque(false);
  //為該選項(xiàng)卡創(chuàng)建一個(gè)標(biāo)簽和一個(gè)Close按鈕。一定要
  //把它的尺寸設(shè)置為幾乎該圖標(biāo)的大小,并且
  //創(chuàng)建一個(gè)行為聽取器-它將定位該選項(xiàng)卡并且從選項(xiàng)卡面板上刪除它
  JLabel tabLabel = new JLabel("Tab " + (++tabCounter));
  JButton tabCloseButton = new JButton(closeXIcon);
  tabCloseButton.setPreferredSize(closeButtonSize);
  tabCloseButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
    int closeTabNumber = tabbedPane.indexOfComponent(content);
    tabbedPane.removeTabAt(closeTabNumber);
   }
  });
  tab.add(tabLabel, BorderLayout.WEST);
  tab.add(tabCloseButton, BorderLayout.EAST);
  //把該選項(xiàng)卡添加到選項(xiàng)卡面板。注意,
  //第一個(gè)參數(shù)(它正常是一個(gè)描述選項(xiàng)卡標(biāo)題的String
  //),為null.
  tabbedPane.addTab(null, content);
  //不是在選項(xiàng)卡上使用String/Icon的結(jié)合,
  //而是使用我們的面板。
  tabbedPane.setTabComponentAt(tabbedPane.getTabCount()-1, tab);
 }
 public static void main(String[] args) {
  TabbedPaneExample main = new TabbedPaneExample();
 }
}

  結(jié)果顯示于圖1中。

圖1.一個(gè)把多個(gè)JComponent用作選項(xiàng)卡的JTabbedPane

  注意,Alexander Potochkin的博客中提供了另外一種不同的方法,它子類化JButton-重載paintComponent()并且畫出它自己的("X")。如果你不想使用你的代碼發(fā)布一個(gè)gif文件,那么使用這種更為復(fù)雜的方法是非常有用的。


四、 SwingWorker現(xiàn)在包含到Mustang中

  大多數(shù)Swing程序員知道,無論什么時(shí)候編寫事件驅(qū)動(dòng)的代碼,例如當(dāng)一個(gè)按鈕按下時(shí)調(diào)用ActionListener,都需要快速處理事件。你永遠(yuǎn)不需要花費(fèi)比你必須處理事件驅(qū)動(dòng)的線程更多的時(shí)間,否則,你的Swing GUI將成為不可響應(yīng)并且不能有效地重繪它自己。在事件調(diào)度線程中實(shí)現(xiàn)一項(xiàng)較大的任務(wù)經(jīng)常意味著你要從事件調(diào)度線程中"剔除"一個(gè)獨(dú)立工作者線程并且讓該線程運(yùn)行于后臺(tái)。這樣以來,當(dāng)使用Swing編寫一個(gè)多線程的應(yīng)用程序時(shí),程序員需要牢記下面兩條規(guī)則:

  · 如果把耗時(shí)的任務(wù)安排到一個(gè)調(diào)度線程中,那么這有可能導(dǎo)致應(yīng)用程序具有不可響應(yīng)性。因此,這樣的任務(wù)應(yīng)該用一個(gè)工作者線程來專門實(shí)現(xiàn)。

  · 對(duì)Swing組件的更新應(yīng)該僅安排給一個(gè)事件調(diào)度線程來完成。

  因?yàn)檫@意味著,至少有兩個(gè)線程在同時(shí)運(yùn)行,因此創(chuàng)建一個(gè)處理線程間通訊的類是很有幫助的。有一個(gè)消息是,最新的Mustang發(fā)行版本中加入了對(duì)SwingWorker類(它是前一段時(shí)間Swing程序員使用的一種流行的解決方案)的支持。

  下面是來自于Javadoc的SwingWorker的正式聲明。

public abstract class SwingWorker<T,V> extends Object
implements RunnableFuture<T>

  注意,這里的聲明包含了兩個(gè)泛型類型變量:T和V。如果你還不熟悉泛型類型變量的話,那么你可以先讀一下有關(guān)泛型的基礎(chǔ)知識(shí),這是在J2SE 5.0中引入的一種特性。下面是定義:

  · T:由這個(gè)SwingWorker的doInBackground()和get()方法返回的結(jié)果類型

  · V:被這個(gè)SwingWorker的publish()和process()方法用來執(zhí)行中間結(jié)果的類型

  一會(huì)之后,我們?cè)儆懻撨@些方法。然而,首先,讓我們介紹一下SwingWorker中所使用的線程架構(gòu)。這里援引Javadoc的描述:在一個(gè)SwingWorker的生命周期中共包含三個(gè)線程:

  · 當(dāng)前線程:execute()方法。它調(diào)度SwingWorker在一個(gè)工作者線程上的執(zhí)行并且立即返回。你可以使用兩個(gè)get()方法之一來等待SwingWorker完成。

  · 工作者線程:這個(gè)線程上調(diào)用doInBackground()方法。這正是所有后臺(tái)活動(dòng)發(fā)生的地方。為了通知PropertyChangeListeners關(guān)于綁定屬性的變化,你可以使用firePropertyChange和getPropertyChangeSupport()方法。默認(rèn)情況下,有兩個(gè)綁定屬性可用-state和progress。

  · 事件調(diào)度線程:所有的Swing相關(guān)的活動(dòng)都發(fā)生在這種線程中。SwingWorker調(diào)用process()和done()方法并且通知這個(gè)線程上的任何PropertyChangeListeners。

  典型地,你在其上實(shí)例化SwingWorker子類的當(dāng)前線程是事件調(diào)度線程。這個(gè)SwingWorker通常響應(yīng)下列一些事件:

  1. execute()方法被調(diào)用或運(yùn)行于這個(gè)事件調(diào)度線程上。

  2. SwingWorker通知任何PropertyChangeListeners其狀態(tài)已經(jīng)變?yōu)镾wingWorker.StateValue.STARTED。

  3. doInBackground()方法在工作者線程上執(zhí)行。

  4. 一旦doInBackground()方法完成,即在當(dāng)前線程上執(zhí)行done()方法。

  5. SwingWorker通知任何PropertyChangeListeners其狀態(tài)已經(jīng)變?yōu)镾wingWorker.StateValue.DONE。

  當(dāng)在doInBackground()方法中時(shí),你可以設(shè)置一個(gè)整型的progress屬性-它使用下列方法指示工作者線程的當(dāng)前進(jìn)度:

protected void setProgress(int progress);

  一旦調(diào)用這個(gè)方法,SwingWorker就向所有的已經(jīng)登記的聽者激發(fā)一個(gè)屬性事件來通知它們已經(jīng)得到更新的進(jìn)度值。

  你可以設(shè)置或添加工作者線程的最后結(jié)果-通過使用下面兩個(gè)方法之一:

protected void process(V... chunks);
protected void publish(V... chunks);

  第二個(gè)方法,publish(),將使用一些中間類型V的一些可變個(gè)數(shù)的對(duì)象并且把它們發(fā)送到process()方法中進(jìn)行處理。典型情況下,你將從doInBackground()線程中調(diào)用process()方法。這個(gè)process()方法應(yīng)該總是被重載以接收輸入的V參數(shù)并且把這些中間對(duì)象以某種形式連接成一個(gè)T類型。當(dāng)然,至于process()方法如何實(shí)現(xiàn)這一任務(wù)要依賴于參數(shù)類型-它在你的SwingWorker子類中指定。

  同時(shí),在當(dāng)前線程中,你可以調(diào)用兩個(gè)get()方法之一來檢索工作者線程的結(jié)果。第一個(gè)get()方法,在工作者線程完成其任務(wù)之前將會(huì)無限地阻塞。第二個(gè)方法在檢索已經(jīng)被處理的結(jié)果寬之前將阻塞一段指定的時(shí)間。

public T get();
public T get(long timeout,TimeUnit unit);

  如果你希望取消這個(gè)工作者線程,那么在它完成執(zhí)行之前,你可以從當(dāng)前線程中調(diào)用cancel()方法。

public final boolean cancel(boolean mayInterruptIfRunning)

  這里的mayInterruptIfRunning參數(shù)指定,在試圖停止這項(xiàng)任務(wù)時(shí)是否執(zhí)行該任務(wù)的線程應(yīng)該被中斷。注意,調(diào)用cancel()方法將失敗-如果該任務(wù)已經(jīng)完成,如果該任務(wù)已經(jīng)被取消或如果它因某些理由可能無法被取消。然而,如果該方法調(diào)用返回true并且當(dāng)調(diào)用cancel()方法時(shí)這項(xiàng)任務(wù)還沒有開始,那么SwingWorker永遠(yuǎn)不應(yīng)該執(zhí)行。

  五、 結(jié)論

  盡管本文中介紹的這些特征基本上相互獨(dú)立,但是它們的確代表Mustang開發(fā)團(tuán)隊(duì)希望滿足Java開發(fā)社區(qū)提出的一小部分實(shí)現(xiàn)請(qǐng)求。特別是,當(dāng)創(chuàng)建Swing應(yīng)用程序時(shí),學(xué)習(xí)使用SwingWorker類是必須的-它可以把程序員從復(fù)雜的GUI線程問題中解脫出來。記住,和往常一樣,這些特征在最終成為Java SE 6的最后發(fā)行版本之前要征得JSR 270專家組的同意。



]]>
用Java實(shí)現(xiàn)HTTP文件隊(duì)列下載http://www.aygfsteel.com/xixidabao/archive/2006/05/20/47218.htmlJAVA之路JAVA之路Sat, 20 May 2006 13:17:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/20/47218.html 許多用戶可能會(huì)遇到這樣的情況:在網(wǎng)站上發(fā)現(xiàn)一個(gè)很好的資源,但是這個(gè)資源是分成了很多個(gè)文件存放的,如果想把它保存到本地,只有靠用戶點(diǎn)擊另存來完成保存,如果資源分了幾百甚至上千上萬,那簡(jiǎn)直是個(gè)災(zāi)難。   在Internet上很多的資源分成多個(gè)文件存放時(shí),它的文件命名是有一定的規(guī)則的;正因如此,我們就可以用程序來完成這個(gè)資源的完全下載。   1. 基礎(chǔ)知識(shí)   在Internet上,我們要下載網(wǎng)站上的某個(gè)資源,我們會(huì)獲得一個(gè)URL(Uniform Resource Locator),它是一個(gè)服務(wù)器資源定位的描述,下載的過程總是如下步驟:   步驟1:客戶端發(fā)起連接請(qǐng)求一個(gè)URL   步驟2:服務(wù)器解析URL,并將指定的資源返回一個(gè)輸入流給客戶   步驟3:客戶端接收輸入流,將流中的內(nèi)容存到文件   2. 網(wǎng)絡(luò)連接的建立   Java提供了對(duì)URL訪問和大量的流操作的的API,我們可以很容易的完成對(duì)網(wǎng)絡(luò)上資源的存取,下面的代碼段就完成了對(duì)一個(gè)網(wǎng)站的資源進(jìn)行訪問: ...... destUrl="http://www.ebook.com/java/網(wǎng)絡(luò)編程001.zip"; url = new URL(destUrl); httpUrl = (HttpURLConnection) url.openConnection(); //連接指定的網(wǎng)絡(luò)資源 httpUrl.connect(); //獲取網(wǎng)絡(luò)輸入流 bis = new BufferedInputStream(httpUrl.getInputStream()); ......   3. 代理的訪問   Java 中通過代理服務(wù)器訪問外網(wǎng)的方法已經(jīng)是世人皆知的秘密了。這里就不再多描述了,訪問的JAVA代碼如下: //設(shè)置代理服務(wù)器 System.getProperties().put("proxySet", "true"); System.getProperties().put("proxyHost", "10.154.134.110"); System.getProperties().put("proxyPort", "8080");   4. 網(wǎng)絡(luò)資源的保存   在上節(jié)中,我們已經(jīng)獲取了指定網(wǎng)絡(luò)資源的輸入流,接下來我們要完成的就是讀取輸入流中的所以內(nèi)容,并將其保存在文件中。示例代碼: ...... fos = new FileOutputStream(fileName); if (this.DEBUG) System.out.println("正在獲取鏈接[" + destUrl + "]的內(nèi)容...\n將其保存為文件[" + fileName +"]"); //保存文件 while ( (size = bis.read(buf)) != -1) fos.write(buf, 0, size); ......   上面的示例代碼就將網(wǎng)絡(luò)資源的內(nèi)容保存到了本地指定的文件中。   5. 代碼清單 import java.io.*; import java.net.*; import java.util.*; /** * <p>Title: 個(gè)人開發(fā)的API</p> * <p>Description: 將指定的HTTP網(wǎng)絡(luò)資源在本地以文件形式存放</p> * <p>Copyright: Copyright (c) 2004</p> * <p>Company: NewSky</p> * @author MagicLiao * @version 1.0 */ public class HttpGet {  public final static boolean DEBUG = true;//調(diào)試用  private static int BUFFER_SIZE = 8096;//緩沖區(qū)大小  private Vector vDownLoad = new Vector();//URL列表  private Vector vFileList = new Vector();//下載后的保存文件名列表  /**  * 構(gòu)造方法  */  public HttpGet() {}  /**  * 清除下載列表  */  public void resetList() {   vDownLoad.clear();   vFileList.clear();  }  /**  * 增加下載列表項(xiàng)  *  * @param url String  * @param filename String  */ public void addItem(String url, String filename) {  vDownLoad.add(url);  vFileList.add(filename); }  /**  * 根據(jù)列表下載資源  */ public void downLoadByList() {  String url = null;  String filename = null;  //按列表順序保存資源  for (int i = 0; i < vDownLoad.size(); i++) {   url = (String) vDownLoad.get(i);   filename = (String) vFileList.get(i);   try {    saveToFile(url, filename);   }   catch (IOException err) {    if (DEBUG) {     System.out.println("資源[" + url + "]下載失敗!!!");    }   }  }  if (DEBUG) {   System.out.println("下載完成!!!");  } } /** * 將HTTP資源另存為文件 * * @param destUrl String * @param fileName String * @throws Exception */ public void saveToFile(String destUrl, String fileName) throws IOException {  FileOutputStream fos = null;  BufferedInputStream bis = null;  HttpURLConnection httpUrl = null;  URL url = null;  byte[] buf = new byte[BUFFER_SIZE];  int size = 0;  //建立鏈接  url = new URL(destUrl);  httpUrl = (HttpURLConnection) url.openConnection();  //連接指定的資源  httpUrl.connect();  //獲取網(wǎng)絡(luò)輸入流  bis = new BufferedInputStream(httpUrl.getInputStream());  //建立文件  fos = new FileOutputStream(fileName);  if (this.DEBUG)   System.out.println("正在獲取鏈接[" + destUrl + "]的內(nèi)容...\n將其保存為文件[" + fileName + "]");  //保存文件  while ( (size = bis.read(buf)) != -1)   fos.write(buf, 0, size);  fos.close();  bis.close();  httpUrl.disconnect(); } /** * 設(shè)置代理服務(wù)器 * * @param proxy String * @param proxyPort String */ public void setProxyServer(String proxy, String proxyPort) {  //設(shè)置代理服務(wù)器  System.getProperties().put("proxySet", "true");  System.getProperties().put("proxyHost", proxy);  System.getProperties().put("proxyPort", proxyPort); } /** * 設(shè)置認(rèn)證用戶名與密碼 * * @param uid String * @param pwd String */ public void setAuthenticator(String uid, String pwd) { Authenticator.setDefault(new MyAuthenticator(uid, pwd)); } /** * 主方法(用于測(cè)試) * * @param argv String[] */ public static void main(String argv[]) {  HttpGet oInstance = new HttpGet();  try {   //增加下載列表(此處用戶可以寫入自己代碼來增加下載列表)   oInstance.addItem("http://www.ebook.com/java/網(wǎng)絡(luò)編程001.zip","./網(wǎng)絡(luò)編程1.zip");   oInstance.addItem("http://www.ebook.com/java/網(wǎng)絡(luò)編程002.zip","./網(wǎng)絡(luò)編程2.zip");   oInstance.addItem("http://www.ebook.com/java/網(wǎng)絡(luò)編程003.zip","./網(wǎng)絡(luò)編程3.zip");   oInstance.addItem("http://www.ebook.com/java/網(wǎng)絡(luò)編程004.zip","./網(wǎng)絡(luò)編程4.zip");   oInstance.addItem("http://www.ebook.com/java/網(wǎng)絡(luò)編程005.zip","./網(wǎng)絡(luò)編程5.zip");   oInstance.addItem("http://www.ebook.com/java/網(wǎng)絡(luò)編程006.zip","./網(wǎng)絡(luò)編程6.zip");   oInstance.addItem("http://www.ebook.com/java/網(wǎng)絡(luò)編程007.zip","./網(wǎng)絡(luò)編程7.zip");   //開始下載   oInstance.downLoadByList();  }  catch (Exception err) {   System.out.println(err.getMessage());  } } }

]]>
Java程序類加載完全揭密http://www.aygfsteel.com/xixidabao/archive/2006/05/20/47160.htmlJAVA之路JAVA之路Sat, 20 May 2006 05:08:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/20/47160.html
  類加載是java語言提供的最強(qiáng)大的機(jī)制之一。盡管類加載并不是討論的熱點(diǎn)話題,但所有的編程人員都應(yīng)該了解其工作機(jī)制,明白如何做才能讓其滿足我們的需要。這能有效節(jié)省我們的編碼時(shí)間,從不斷調(diào)試ClassNotFoundException, ClassCastException的工作中解脫出來。

  這篇文章從基礎(chǔ)講起,比如代碼與數(shù)據(jù)的不同之處是什么,他們是如何構(gòu)成一個(gè)實(shí)例或?qū)ο蟮摹H缓笊钊胩接?a class="bluekey" target="_blank">java虛擬機(jī)(JVM)是如何利用類加載器讀取代碼,以及java中類加載器的主要類型。接著用一個(gè)類加載的基本算法看一下類加載器如何加載一個(gè)內(nèi)部類。本文的下一節(jié)演示一段代碼來說明擴(kuò)展和開發(fā)屬于自己的類加載器的必要性。緊接著解釋如何使用定制的類加載器來完成一個(gè)一般意義上的任務(wù),使其可以加載任意遠(yuǎn)端客戶的代碼,在JVM中定義,實(shí)例化并執(zhí)行它。本文包括了J2EE關(guān)于類加載的規(guī)范——事實(shí)上這已經(jīng)成為了J2EE的標(biāo)準(zhǔn)之一。

  類與數(shù)據(jù)

  一個(gè)類代表要執(zhí)行的代碼,而數(shù)據(jù)則表示其相關(guān)狀態(tài)。狀態(tài)時(shí)常改變,而代碼則不會(huì)。當(dāng)我們將一個(gè)特定的狀態(tài)與一個(gè)類相對(duì)應(yīng)起來,也就意味著將一個(gè)類事例化。盡管相同的類對(duì)應(yīng)的實(shí)例其狀態(tài)千差萬別,但其本質(zhì)都對(duì)應(yīng)著同一段代碼。在JAVA中,一個(gè)類通常有著一個(gè).class文件,但也有例外。在JAVA的運(yùn)行時(shí)環(huán)境中(Java runtime),每一個(gè)類都有一個(gè)以第一類(first-class)的Java對(duì)象所表現(xiàn)出現(xiàn)的代碼,其是java.lang.Class的實(shí)例。我們編譯一個(gè)JAVA文件,編譯器都會(huì)嵌入一個(gè)public, static, final修飾的類型為java.lang.Class,名稱為class的域變量在其字節(jié)碼文件中。因?yàn)槭褂昧藀ublic修飾,我們可以采用如下的形式對(duì)其訪問:

java.lang.Class klass = Myclass.class;

  一旦一個(gè)類被載入JVM中,同一個(gè)類就不會(huì)被再次載入了(切記,同一個(gè)類)。這里存在一個(gè)問題就是什么是“同一個(gè)類”?正如一個(gè)對(duì)象有一個(gè)具體的狀態(tài),即標(biāo)識(shí),一個(gè)對(duì)象始終和其代碼(類)相關(guān)聯(lián)。同理,載入JVM的類也有一個(gè)具體的標(biāo)識(shí),我們接下來看。

  在Java中,一個(gè)類用其完全匹配類名(fully qualified class name)作為標(biāo)識(shí),這里指的完全匹配類名包括包名和類名。但在JVM中一個(gè)類用其全名和一個(gè)加載類ClassLoader的實(shí)例作為唯一標(biāo)識(shí)。因此,如果一個(gè)名為Pg的包中,有一個(gè)名為Cl的類,被類加載器KlassLoader的一個(gè)實(shí)例kl1加載,Cl的實(shí)例,即C1.class在JVM中表示為(Cl, Pg, kl1)。這意味著兩個(gè)類加載器的實(shí)例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的,被它們所加載的類也因此完全不同,互不兼容的。那么在JVM中到底有多少種類加載器的實(shí)例?下一節(jié)我們揭示答案。

  類加載器

  在JVM中,每一個(gè)類都被java.lang.ClassLoader的一些實(shí)例來加載.類ClassLoader是在包中java.lang里,開發(fā)者可以自由地繼承它并添加自己的功能來加載類。

  無論何時(shí)我們鍵入java MyMainClass來開始運(yùn)行一個(gè)新的JVM,“引導(dǎo)類加載器(bootstrap class loader)”負(fù)責(zé)將一些關(guān)鍵的Java類,如java.lang.Object和其他一些運(yùn)行時(shí)代碼先加載進(jìn)內(nèi)存中。運(yùn)行時(shí)的類在JRE\lib\rt.jar包文件中。因?yàn)檫@屬于系統(tǒng)底層執(zhí)行動(dòng)作,我們無法在JAVA文檔中找到引導(dǎo)類加載器的工作細(xì)節(jié)。基于同樣的原因,引導(dǎo)類加載器的行為在各JVM之間也是大相徑庭。
同理,如果我們按照如下方式:

log(java.lang.String.class.getClassLoader());

  來獲取java的核心運(yùn)行時(shí)類的加載器,就會(huì)得到null。

  接下來介紹java的擴(kuò)展類加載器。擴(kuò)展庫提供比java運(yùn)行代碼更多的特性,我們可以把擴(kuò)展庫保存在由java.ext.dirs屬性提供的路徑中。

  (編輯注:java.ext.dirs屬性指的是系統(tǒng)屬性下的一個(gè)key,所有的系統(tǒng)屬性可以通過System.getProperties()方法獲得。在編者的系統(tǒng)中,java.ext.dirs的value是” C:\Program Files\Java\jdk1.5.0_04\jre\lib\ext”。下面將要談到的如java.class.path也同屬系統(tǒng)屬性的一個(gè)key。)

  類ExtClassLoader專門用來加載所有java.ext.dirs下的.jar文件。開發(fā)者可以通過把自己的.jar文件或庫文件加入到擴(kuò)展目錄的classpath,使其可以被擴(kuò)展類加載器讀取。

  從開發(fā)者的角度,第三種同樣也是最重要的一種類加載器是AppClassLoader。這種類加載器用來讀取所有的對(duì)應(yīng)在java.class.path系統(tǒng)屬性的路徑下的類。

  Sun的java指南中,文章“理解擴(kuò)展類加載”(Understanding Extension Class Loading)對(duì)以上三個(gè)類加載器路徑有更詳盡的解釋,這是其他幾個(gè)JDK中的類加載器

  ●java.net.URLClassLoader

  ●java.security.SecureClassLoader

  ●java.rmi.server.RMIClassLoader

  ●sun.applet.AppletClassLoader

  java.lang.Thread,包含了public ClassLoader getContextClassLoader()方法,這一方法返回針對(duì)一具體線程的上下文環(huán)境類加載器。此類加載器由線程的創(chuàng)建者提供,以供此線程中運(yùn)行的代碼在需要加載類或資源時(shí)使用。如果此加載器未被建立,缺省是其父線程的上下文類加載器。原始的類加載器一般由讀取應(yīng)用程序的類加載器建立。


類加載是java語言提供的最強(qiáng)大的機(jī)制之一。盡管類加載并不是討論的熱點(diǎn)話題,但所有的編程人員都應(yīng)該了解其工作機(jī)制,明白如何做才能讓其滿足我們的需要。

  類加載器如何工作?

  除了引導(dǎo)類加載器,所有的類加載器都有一個(gè)父類加載器,不僅如此,所有的類加載器也都是java.lang.ClassLoader類型。以上兩種類加載器是不同的,而且對(duì)于開發(fā)者自訂制的類加載器的正常運(yùn)行也至關(guān)重要。最重要的方面是正確設(shè)置父類加載器。任何類加載器,其父類加載器是加載該類加載器的類加載器實(shí)例。(記住,類加載器本身也是一個(gè)類!)

  使用loadClass()方法可以從類加載器中獲得該類。我們可以通過java.lang.ClassLoader的源代碼來了解該方法工作的細(xì)節(jié),如下:

protected synchronized Class<?> loadClass (String name, boolean resolve) throws ClassNotFoundException
{
 // First check if the class is already loaded
 Class c = findLoadedClass(name);
 if (c == null)
 {
  try
  {
   if (parent != null)
   {
    c = parent.loadClass(name, false);
   } else {
    c = findBootstrapClass0(name);
   }
  } catch (ClassNotFoundException e) {
   // If still not found, then invoke // findClass to find the class.
   c = findClass(name);
  }
 }
 if (resolve)
 {
  resolveClass(c);
 }
 return c;
}

  我們可以使用ClassLoader的兩種構(gòu)造方法來設(shè)置父類加載器:

public class MyClassLoader extends ClassLoader
{
 public MyClassLoader()
 {
  super(MyClassLoader.class.getClassLoader());
 }
}

  或

public class MyClassLoader extends ClassLoader
{
 public MyClassLoader()
 {
  super(getClass().getClassLoader());
 }
}

  第一種方式較為常用,因?yàn)橥ǔ2唤ㄗh在構(gòu)造方法里調(diào)用getClass()方法,因?yàn)閷?duì)象的初始化只是在構(gòu)造方法的出口處才完全完成。因此,如果父類加載器被正確建立,當(dāng)要示從一個(gè)類加載器的實(shí)例獲得一個(gè)類時(shí),如果它不能找到這個(gè)類,它應(yīng)該首先去訪問其父類。如果父類不能找到它(即其父類也不能找不這個(gè)類,等等),而且如果findBootstrapClass0()方法也失敗了,則調(diào)用findClass()方法。findClass()方法的缺省實(shí)現(xiàn)會(huì)拋出ClassNotFoundException,當(dāng)它們繼承java.lang.ClassLoader來訂制類加載器時(shí)開發(fā)者需要實(shí)現(xiàn)這個(gè)方法。findClass()的缺省實(shí)現(xiàn)方式如下:

protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }

  在findClass()方法內(nèi)部,類加載器需要獲取任意來源的字節(jié)碼。來源可以是文件系統(tǒng),URL,數(shù)據(jù)庫,可以產(chǎn)生字節(jié)碼的另一個(gè)應(yīng)用程序,及其他類似的可以產(chǎn)生java規(guī)范的字節(jié)碼的來源。你甚至可以使用BCEL (Byte Code Engineering Library:字節(jié)碼工程庫),它提供了運(yùn)行時(shí)創(chuàng)建類的捷徑。BCEL已經(jīng)被成功地使用在以下方面:編譯器,優(yōu)化器,混淆器,代碼產(chǎn)生器及其他分析工具。一旦字節(jié)碼被檢索,此方法就會(huì)調(diào)用defineClass()方法,此行為對(duì)不同的類加載實(shí)例是有差異的。因此,如果兩個(gè)類加載實(shí)例從同一個(gè)來源定義一個(gè)類,所定義的結(jié)果是不同的。

  JAVA語言規(guī)范(Java language specification)詳細(xì)解釋了JAVA執(zhí)行引擎中的類或接口的加載(loading),鏈接(linking)或初始化(initialization)過程。

  圖一顯示了一個(gè)主類稱為MyMainClass的應(yīng)用程序。依照之前的闡述,MyMainClass.class會(huì)被AppClassLoader加載。 MyMainClass創(chuàng)建了兩個(gè)類加載器的實(shí)例:CustomClassLoader1 和 CustomClassLoader2,他們可以從某數(shù)據(jù)源(比如網(wǎng)絡(luò))獲取名為Target的字節(jié)碼。這表示類Target的類定義不在應(yīng)用程序類路徑或擴(kuò)展類路徑。在這種情況下,如果MyMainClass想要用自定義的類加載器加載Target類,CustomClassLoader1和CustomClassLoader2會(huì)分別獨(dú)立地加載并定義Target.class類。這在java中有重要的意義。如果Target類有一些靜態(tài)的初始化代碼,并且假設(shè)我們只希望這些代碼在JVM中只執(zhí)行一次,而這些代碼在我們目前的步驟中會(huì)執(zhí)行兩次——分別被不同的CustomClassLoaders加載并執(zhí)行。如果類Target被兩個(gè)CustomClassLoaders加載并創(chuàng)建兩個(gè)實(shí)例Target1和Target2,如圖一顯示,它們不是類型兼容的。換句話說,在JVM中無法執(zhí)行以下代碼:

Target target3 = (Target) target2;

  以上代碼會(huì)拋出一個(gè)ClassCastException。這是因?yàn)镴VM把他們視為分別不同的類,因?yàn)樗麄儽徊煌念惣虞d器所定義。這種情況當(dāng)我們不是使用兩個(gè)不同的類加載器CustomClassLoader1 和 CustomClassLoader2,而是使用同一個(gè)類加載器CustomClassLoader的不同實(shí)例時(shí),也會(huì)出現(xiàn)同樣的錯(cuò)誤。這些會(huì)在本文后邊用具體代碼說明。



圖1. 在同一個(gè)JVM中多個(gè)類加載器加載同一個(gè)目標(biāo)類



 為什么我們需要我們自己的類加載器

  原因之一為開發(fā)者寫自己的類加載器來控制JVM中的類加載行為,java中的類靠其包名和類名來標(biāo)識(shí),對(duì)于實(shí)現(xiàn)了java.io.Serializable接口的類,serialVersionUID扮演了一個(gè)標(biāo)識(shí)類版本的重要角色。這個(gè)唯一標(biāo)識(shí)是一個(gè)類名、接口名、成員方法及屬性等組成的一個(gè)64位的哈希字段,而且也沒有其他快捷的方式來標(biāo)識(shí)一個(gè)類的版本。嚴(yán)格說來,如果以上的都匹配,那么則屬于同一個(gè)類。

  但是讓我們思考如下情況:我們需要開發(fā)一個(gè)通用的執(zhí)行引擎。可以執(zhí)行實(shí)現(xiàn)某一特定接口的任何任務(wù)。當(dāng)任務(wù)被提交到這個(gè)引擎,首先需要加載這個(gè)任務(wù)的代碼。假設(shè)不同的客戶對(duì)此引擎提交了不同的任務(wù),湊巧,這些所有的任務(wù)都有一個(gè)相同的類名和包名。現(xiàn)在面臨的問題就是這個(gè)引擎是否可以針對(duì)不同的用戶所提交的信息而做出不同的反應(yīng)。這一情況在下文的參考一節(jié)有可供下載的代碼樣例,samepath 和 differentversions,這兩個(gè)目錄分別演示了這一概念。 圖2 顯示了文件目錄結(jié)構(gòu),有三個(gè)子目錄samepath, differentversions, 和 differentversionspush,里邊是例子:


圖2. 文件夾結(jié)構(gòu)組織示例

  在samepath 中,類version.Version保存在v1和v2兩個(gè)子目錄里,兩個(gè)類具有同樣的類名和包名,唯一不同的是下邊這行:

public void fx(){ log("this = " + this + "; Version.fx(1)."); }

  V1中,日志記錄中有Version.fx(1),而在v2中則是Version.fx(2)。把這個(gè)兩個(gè)存在細(xì)微不同的類放在一個(gè)classpath下,然后運(yùn)行Test類:

set CLASSPATH=.;%CURRENT_ROOT%\v1;%CURRENT_ROOT%\v2
%JAVA_HOME%\bin\java Test

  圖3顯示了控制臺(tái)輸出。我們可以看到對(duì)應(yīng)著Version.fx(1)的代碼被執(zhí)行了,因?yàn)轭惣虞d器在classpath首先看到此版本的代碼。


圖3. 在類路徑中samepath測(cè)試排在最前面的version 1

  再次運(yùn)行,類路徑做如下微小改動(dòng)。

set CLASSPATH=.;%CURRENT_ROOT%\v2;%CURRENT_ROOT%\v1
%JAVA_HOME%\bin\java Test

  控制臺(tái)的輸出變?yōu)閳D4。對(duì)應(yīng)著Version.fx(2)的代碼被加載,因?yàn)轭惣虞d器在classpath中首先找到它的路徑。


圖4. 在類路徑中samepath測(cè)試排在最前面的version 2

  根據(jù)以上例子可以很明顯地看出,類加載器加載在類路徑中被首先找到的元素。如果我們?cè)趘1和v2中刪除了version.Version,做一個(gè)非version.Version形式的.jar文件,如myextension.jar,把它放到對(duì)應(yīng)java.ext.dirs的路徑下,再次執(zhí)行后看到version.Version不再被AppClassLoader加載,而是被擴(kuò)展類加載器加載。如圖5所示。


圖5. AppClassLoader及ExtClassLoader

  繼續(xù)這個(gè)例子,文件夾differentversions包含了一個(gè)RMI執(zhí)行引擎,客戶端可以提供給執(zhí)行引擎任何實(shí)現(xiàn)了common.TaskIntf接口的任務(wù)。子文件夾client1 和 client2包含了類client.TaskImpl有個(gè)細(xì)微不同的兩個(gè)版本。兩個(gè)類的區(qū)別在以下幾行:

static
{
 log("client.TaskImpl.class.getClassLoader (v1) : " + TaskImpl.class.getClassLoader());
}

public void execute(){ log("this = " + this + "; execute(1)"); }

  在client1和client2里分別有g(shù)etClassLoader(v1) 與 execute(1)和getClassLoader(v2) 與 execute(2)的的log語句。并且,在開始執(zhí)行引擎RMI服務(wù)器的代碼中,我們隨意地將client2的任務(wù)實(shí)現(xiàn)放在類路徑的前面。

CLASSPATH=%CURRENT_ROOT%\common;%CURRENT_ROOT%\server;
%CURRENT_ROOT%\client2;%CURRENT_ROOT%\client1
%JAVA_HOME%\bin\java server.Server

  如圖6,7,8的屏幕截圖,在客戶端VM,各自的client.TaskImpl類被加載、實(shí)例化,并發(fā)送到服務(wù)端的VM來執(zhí)行。從服務(wù)端的控制臺(tái),可以明顯看到client.TaskImpl代碼只被服務(wù)端的VM執(zhí)行一次,這個(gè)單一的代碼版本在服務(wù)端多次生成了許多實(shí)例,并執(zhí)行任務(wù)。


圖6. 執(zhí)行引擎服務(wù)器控制臺(tái)

  圖6顯示了服務(wù)端的控制臺(tái),加載并執(zhí)行兩個(gè)不同的客戶端的請(qǐng)求,如圖7、8所示。需要注意的是,代碼只被加載了一次(從靜態(tài)初始化塊的日志中也可以明顯看出),但對(duì)于客戶端的調(diào)用這個(gè)方法被執(zhí)行了兩次。


圖7. 執(zhí)行引擎客戶端 1控制臺(tái) 

  圖7中,客戶端VM加載了含有client.TaskImpl.class.getClassLoader(v1)的日志內(nèi)容的類TaskImpl的代碼,并提供給服務(wù)端的執(zhí)行引擎。圖8的客戶端VM加載了另一個(gè)TaskImpl的代碼,并發(fā)送給服務(wù)端。


圖8. 執(zhí)行引擎客戶端 2控制臺(tái) 

  在客戶端的VM中,類client.TaskImpl被分別加載,初始化,并發(fā)送到服務(wù)端執(zhí)行。圖6還揭示了client.TaskImpl的代碼只在服務(wù)端的VM中加載了一次,但這“唯一的一次”卻在服務(wù)端創(chuàng)造了許多實(shí)例并執(zhí)行。或許客戶端1該不高興了因?yàn)椴⒉皇撬腸lient.TaskImpl(v1)的方法調(diào)用被服務(wù)端執(zhí)行了,而是其他的一些代碼。如何解決這一問題?答案就是實(shí)現(xiàn)定制的類加載器。




]]>
Java 5.0泛型編程之泛型類型http://www.aygfsteel.com/xixidabao/archive/2006/05/19/47063.htmlJAVA之路JAVA之路Fri, 19 May 2006 07:17:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/19/47063.html
  為了使用泛型類型,你應(yīng)該為類型變量詳細(xì)指明實(shí)際的類型,形成一個(gè)就像List<String>類似的參數(shù)化類型。[1]指明這些額外的類型信息的原因是編譯器據(jù)此能夠在編譯期為您提供很強(qiáng)的類型檢查,增強(qiáng)您的程序的類型安全性。舉個(gè)例子來說,您有一個(gè)只能保持String對(duì)象的List,那么這種類型檢查就能夠阻止您往里面加入String[]對(duì)象。同樣的,增加的類型信息使編譯器能夠?yàn)槟鲆恍╊愋娃D(zhuǎn)換的事情。比如,編譯器知道了一個(gè)List<String>有個(gè)get()方法,其返回值是一個(gè)String對(duì)象,因此您不再需要去將返回值由一個(gè)Object強(qiáng)制轉(zhuǎn)換為String。

  Java.util包中的集合類在java5.0中已經(jīng)被做成了泛型,也許您將會(huì)在您的程序中頻繁的使用到他們。類型安全的集合類就是一個(gè)泛型類型的典型案例。即便您從沒有定義過您自己的泛型類型甚至從未用過除了java.util中的集合類以外的泛型類型,類型安全的集合類的好處也是極有意義的一個(gè)標(biāo)志——他們證明了這個(gè)主要的新語言特性的復(fù)雜性。

  我們從探索類型安全的集合類中的基本的泛型用法開始,進(jìn)而研究更多使用泛型類型的復(fù)雜細(xì)節(jié)。然后我們討論類型參數(shù)通配符和有界通配符。描繪了如何使用泛型以后,我們闡明如何編寫自己的泛型類型和泛型方法。我們對(duì)于泛型的討論將結(jié)束于一趟對(duì)于JavaAPI的核心中重要的泛型類型的旅行。這趟旅程將探索這些類型以及他們的用法,旅程的目的是為了讓您對(duì)泛型如何工作這個(gè)問題有個(gè)深入的理解。

  類型安全集合類

  Java.util類包包含了Java集合框架(Java Collections Framework),這是一批包含對(duì)象的set、對(duì)象的list以及基于key-value的map。第五章將談到集合類。這里,我們討論的是在java5.0中集合類使用類型參數(shù)來界定集合中的對(duì)象的類型。這個(gè)討論并不適合java1.4或更早期版本。如果沒有泛型,對(duì)于集合類的使用需要程序員記住每個(gè)集合中元素的類型。當(dāng)您在java1.4種創(chuàng)建了一個(gè)集合,您知道您放入到集合中的對(duì)象的類型,但是編譯器不知道。您必須小心地往其中加入一個(gè)合適類型的元素,當(dāng)需要從集合中獲取元素時(shí),您必須顯式的寫強(qiáng)制類型轉(zhuǎn)換以將他們從Object轉(zhuǎn)換為他們真是的類型。考察下邊的java1.4的代碼。

public static void main(String[] args) {
????// This list is intended to hold only strings.
????// The compiler doesn't know that so we have to remember ourselves.
????List wordlist = new ArrayList();??

????// Oops! We added a String[] instead of a String.
????// The compiler doesn't know that this is an error.
????wordlist.add(args);

????// Since List can hold arbitrary objects, the get() method returns
????// Object.??Since the list is intended to hold strings, we cast the
????// return value to String but get a ClassCastException because of
????// the error above.
????String word = (String)wordlist.get(0);
}
  泛型類型解決了這段代碼中的顯示的類型安全問題。Java.util中的List或是其他集合類已經(jīng)使用泛型重寫過了。就像前面提到的, List被重新定義為一個(gè)list,它中間的元素類型被一個(gè)類型可變的名稱為E的占位符描述。Add()方法被重新定義為期望一個(gè)類型為E的參數(shù),用于替換以前的Object,get()方法被重新定義為返回一個(gè)E,替換了以前的Object。

  在java5.0中,當(dāng)我們申明一個(gè)List或者創(chuàng)建一個(gè)ArrayList的實(shí)例的時(shí)候,我們需要在泛型類型的名字后面緊跟一對(duì)“<>”,尖括號(hào)中寫入我們需要的實(shí)際的類型。比如,一個(gè)保持String的List應(yīng)該寫成“List<String>”。需要注意的是,這非常象給一個(gè)方法傳一個(gè)參數(shù),區(qū)別是我們使用類型而不是值,同時(shí)使用尖括號(hào)而不是圓括號(hào)

  Java.util的集合類中的元素必須是對(duì)象化的,他們不能是基本類型。泛型的引入并沒有改變這點(diǎn)。泛型不能使用基本類型:我們不能這樣來申明——Set<char>或者List<int>。記住,無論如何,java5.0中的自動(dòng)打包和自動(dòng)解包特性使得使用Set<Character>或者List<Integer>和直接使用char和int值一樣方便。(查看第二章以了解更多關(guān)于自動(dòng)打包和自動(dòng)解包的細(xì)節(jié))。

  在Java5.0中,上面的例子將被重寫為如下方式:

public static void main(String[] args) {
????// This list can only hold String objects
????List<String> wordlist = new ArrayList<String>();

????// args is a String[], not String, so the compiler won't let us do this
????wordlist.add(args);??// Compilation error!

????// We can do this, though.??
????// Notice the use of the new for/in looping statement
????for(String arg : args) wordlist.add(arg);

????// No cast is required.??List<String>.get() returns a String.
????String word = wordlist.get(0);
}
  值得注意的是代碼量其實(shí)并沒有比原來那個(gè)沒有泛型的例子少多少。使用“(String)”這樣的類型轉(zhuǎn)換被替換成了類型參數(shù)“<String>”。 不同的是類型參數(shù)需要且僅需要聲明一次,而list能夠被使用任何多次,不需要類型轉(zhuǎn)換。在更長(zhǎng)點(diǎn)的例子代碼中,這一點(diǎn)將更加明顯。即使在那些看上去泛型語法比非泛型語法要冗長(zhǎng)的例子里,使用泛型依然是非常有價(jià)值的——額外的類型信息允許編譯器在您的代碼里執(zhí)行更強(qiáng)的錯(cuò)誤檢查。以前只能在運(yùn)行起才能發(fā)現(xiàn)的錯(cuò)誤現(xiàn)在能夠在編譯時(shí)就被發(fā)現(xiàn)。此外,以前為了處理類型轉(zhuǎn)換的異常,我們需要添加額外的代碼行。如果沒有泛型,那么當(dāng)發(fā)生類型轉(zhuǎn)換異常的時(shí)候,一個(gè)ClassCastException異常就會(huì)被從實(shí)際代碼中拋出。

  就像一個(gè)方法可以使用任意數(shù)量的參數(shù)一樣,類允許使用多個(gè)類型變量。接口Java.util.Map就是一個(gè)例子。一個(gè)Map體現(xiàn)了從一個(gè)key的對(duì)象到一個(gè)value的對(duì)象的映射關(guān)系。接口Map申明了一個(gè)類型變量來描述key的類型而另一個(gè)類型變量來描述value的類型。舉個(gè)例子來說,假設(shè)您希望做一個(gè)String對(duì)象到Integer對(duì)象的映射關(guān)系:

public static void main(String[] args) {
????// A map from strings to their position in the args[] array
????Map<String,Integer> map = new HashMap<String,Integer>();

????// Note that we use autoboxing to wrap i in an Integer object.
????for(int i=0; i < args.length; i++) map.put(args[i], i);??

????// Find the array index of a word.??Note no cast is required!
????Integer position = map.get("hello");

????// We can also rely on autounboxing to convert directly to an int,
????// but this throws a NullPointerException if the key does not exist
????// in the map
????int pos = map.get("world");
}
  象List<String>這個(gè)一個(gè)參數(shù)類型其本身也是也一個(gè)類型,也能夠被用于當(dāng)作其他類型的一個(gè)類型變量值。您可能會(huì)看到這樣的代碼:

// Look at all those nested angle brackets!
Map<String, List<List<int[]>>> map = getWeirdMap();

// The compiler knows all the types and we can write expressions
// like this without casting.??We might still get NullPointerException
// or ArrayIndexOutOfBounds at runtime, of course.
int value = map.get(key).get(0).get(0)[0];

// Here's how we break that expression down step by step.
List<List<int[]>> listOfLists = map.get(key);
List<int[]> listOfIntArrays = listOfLists.get(0);
int[] array = listOfIntArrays.get(0);
int element = array[0];
  在上面的代碼里,java.util.List<E>和java.util.Map<K,V>的get()方法返回一個(gè)類型為E的list元素或者一個(gè)類型為V的map元素。注意,無論如何,泛型類型能夠更精密的使用他們的變量。在本書中的參考章節(jié)查看List<E>,您將會(huì)看到它的iterator( )方法被聲明為返回一個(gè)Iterator<E>。這意味著,這個(gè)方法返回一個(gè)跟list的實(shí)際的參數(shù)類型一樣的一個(gè)參數(shù)類型的實(shí)例。為了具體的說明這點(diǎn),下面的例子提供了不使用get(0)方法來獲取一個(gè)List<String>的第一個(gè)元素的方法

List<String> words = // ...initialized elsewhere...
Iterator<String> iterator = words.iterator();
String firstword = iterator.next();





  理解泛型類型
 本段將對(duì)泛型類型的使用細(xì)節(jié)做進(jìn)一步的探討,以嘗試說明下列問題:

  不帶類型參數(shù)的使用泛型的后果

  參數(shù)化類型的體系

  一個(gè)關(guān)于編譯期泛型類型的類型安全的漏洞和一個(gè)用于確保運(yùn)行期類型安全的補(bǔ)丁

  為什么參數(shù)化類型的數(shù)組不是類型安全的

  未經(jīng)處理的類型和不被檢查的警告

  即使被重寫的Java集合類帶來了泛型的好處,在使用他們的時(shí)候您也不被要求說明類型變量。一個(gè)不帶類型變量的泛型類型被認(rèn)為是一個(gè)未經(jīng)處理的類型(raw type)。這樣,5.0版本以前的java代碼仍然能夠運(yùn)行:您顯式的編寫所有類型轉(zhuǎn)換就像您已經(jīng)這樣寫的一樣,您可能會(huì)被一些來自編譯器的麻煩所困擾。查看下列存儲(chǔ)不同類型的對(duì)象到一個(gè)未經(jīng)處理的List:

List l = new ArrayList();
l.add("hello");??
l.add(new Integer(123));
Object o = l.get(0);
  這段代碼在java1.4下運(yùn)行得很好。如果您用java5.0來編譯它,javac編譯了,但是會(huì)打印出這樣的“抱怨”:

Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.


  如果我們加入-Xlint參數(shù)后重新編譯,我們會(huì)看到這些警告:

Test.java:6: warning: [unchecked]
????unchecked call to add(E) as a member of the raw type java.util.List
????????l.add("hello");??
???????? ^
Test.java:7: warning: [unchecked]
????unchecked call to add(E) as a member of the raw type java.util.List
????????l.add(new Integer(123));

???????? ^

  編譯在add()方法的調(diào)用上給出了警告,因?yàn)樗荒軌虼_信加入到list中的值具有正確的類型。它告訴我們說我們使用了一個(gè)未經(jīng)處理的類型,它不能驗(yàn)證我們的代碼是類型安全的。注意,get()方法的調(diào)用是沒有問題的,因?yàn)槟軌虮猾@得的元素已經(jīng)安全的存在于list中了。

  如果您不想使用任何的java5.0的新特性,您可以簡(jiǎn)單的通過帶-source1.4標(biāo)記來編譯他們,這樣編譯器就不會(huì)再“抱怨”了。如果您不能這樣做,您可以忽略這些警告,通過使用一個(gè)“@SuppressWarnings("unchecked")”注解(查看本章的4.3節(jié))隱瞞這些警告信息或者升級(jí)您的代碼,加入類型變量描述。[2]下列示例代碼,編譯的時(shí)候不再會(huì)有警告但仍然允許您往list中放入不同的類型的對(duì)象。

List<Object> l = new ArrayList<Object>();
l.add("hello");??
l.add(123);??????????????// autoboxing
Object o = l.get(0);
  參數(shù)化類型的體系

  參數(shù)化類型有類型體系,就像一般的類型一樣。這個(gè)體系基于對(duì)象的類型,而不是變量的類型。這里有些例子您可以嘗試:

ArrayList<Integer> l = new ArrayList<Integer>();
List<Integer> m = l;????????????????????????????// okay
Collection<Integer> n = l;??????????????????????// okay
ArrayList<Number> o = l;????????????????????????// error
Collection<Object> p = (Collection<Object>)l;?? // error, even with cast
  一個(gè)List<Integer>是一個(gè)Collection<Integer>,但不是一個(gè)List<Object>。這句話不容易理解,如果您想理解為什么泛型這樣做,這段值得看一下。考察這段代碼:

List<Integer> li = new ArrayList<Integer>();
li.add(123);

// The line below will not compile.??But for the purposes of this
// thought-experiment, assume that it does compile and see how much
// trouble we get ourselves into.
List<Object> lo = li;??

// Now we can retrieve elements of the list as Object instead of Integer
Object number = lo.get(0);

// But what about this?
lo.add("hello world");

// If the line above is allowed then the line below throws ClassCastException
Integer i = li.get(1);??// Can't cast a String to Integer!
  這就是為什么List<Integer>不是一個(gè)List<Object>的原因,雖然List<Integer>中所有的元素事實(shí)上是一個(gè)Object的實(shí)例。如果允許轉(zhuǎn)換成List<Object>,那么轉(zhuǎn)換后,理論上非整型的對(duì)象也將被允許添加到list中。


  運(yùn)行時(shí)類型安全

  就像我們所見到的,一個(gè)List<X>不允許被轉(zhuǎn)換為一個(gè)List<Y>,即使這個(gè)X能夠被轉(zhuǎn)換為Y。然而,一個(gè)List<X>能夠被轉(zhuǎn)換為一個(gè)List,這樣您就可以通過繼承的方法來做這樣的事情。

  這種將參數(shù)化類型轉(zhuǎn)換為非參數(shù)化類型的能力對(duì)于向下兼容是必要的,但是它會(huì)在泛型所帶來的類型安全體系上鑿個(gè)漏洞:

// Here's a basic parameterized list.
List<Integer> li = new ArrayList<Integer>();

// It is legal to assign a parameterized type to a nonparameterized variable
List l = li;??

// This line is a bug, but it compiles and runs.
// The Java 5.0 compiler will issue an unchecked warning about it.
// If it appeared as part of a legacy class compiled with Java 1.4, however,
// then we'd never even get the warning.??
l.add("hello");

// This line compiles without warning but throws ClassCastException at runtime.
// Note that the failure can occur far away from the actual bug.
Integer i = li.get(0);
  泛型僅提供了編譯期的類型安全。如果您使用java5.0的編譯器來編譯您的代碼并且沒有得到任何警告,這些編譯器的檢查能夠確保您的代碼在運(yùn)行期也是類型安全的。如果您獲得了警告或者使用了像未經(jīng)處理的類型那樣修改您的集合的代碼,那么您需要增加一些步驟來確保運(yùn)行期的類型安全。您可以通過使用java.util.Collections中的checkedList()和checkedMap( )方法來做到這一步。這些方法將把您的集合打包成一個(gè)wrapper集合,從而在運(yùn)行時(shí)檢查確認(rèn)只有正確類型的值能夠被置入集合眾。下面是一個(gè)能夠補(bǔ)上類型安全漏洞的一個(gè)例子:

// Here's a basic parameterized list.
List<Integer> li = new ArrayList<Integer>();

// Wrap it for runtime type safety
List<Integer> cli = Collections.checkedList(li, Integer.class);

// Now widen the checked list to the raw type
List l = cli;??

// This line compiles but fails at runtime with a ClassCastException.
// The exception occurs exactly where the bug is, rather than far away
l.add("hello");
  參數(shù)化類型的數(shù)組

  在使用泛型類型的時(shí)候,數(shù)組需要特別的考慮。回憶一下,如果T是S的父類(或者接口),那么類型為S的數(shù)組S[],同時(shí)又是類型為T的數(shù)組T[]。正因?yàn)槿绱耍看文娣乓粋€(gè)對(duì)象到數(shù)組中時(shí),Java解釋器都必須進(jìn)行檢查以確保您放入的對(duì)象類型與要存放的數(shù)組所允許的類型是匹對(duì)的。例如,下列代碼在運(yùn)行期會(huì)檢查失敗,拋出一個(gè)ArrayStoreException異常:

String[] words = new String[10];
Object[] objs = words;
objs[0] = 1;??// 1 autoboxed to an Integer, throws ArrayStoreException
  雖然編譯時(shí)obj是一個(gè)Object[],但是在運(yùn)行時(shí)它是一個(gè)String[],它不允許被用于存放一個(gè)Integer。

  當(dāng)我們使用泛型類型的時(shí)候,僅僅依靠運(yùn)行時(shí)的數(shù)組存放異常檢查是不夠的,因?yàn)橐粋€(gè)運(yùn)行時(shí)進(jìn)行的檢查并不能夠獲取編譯時(shí)的類型參數(shù)信息。查看下列代碼:

List<String>[] wordlists = new ArrayList<String>[10];
ArrayList<Integer> ali = new ArrayList<Integer>();
ali.add(123);
Object[] objs = wordlists;
objs[0] = ali;?????????????????????? // No ArrayStoreException
String s = wordlists[0].get(0);??????// ClassCastException!
  如果上面的代碼被允許,那么運(yùn)行時(shí)的數(shù)組存儲(chǔ)檢查將會(huì)成功:沒有編譯時(shí)的類型參數(shù),代碼簡(jiǎn)單地存儲(chǔ)一個(gè)ArrayList到一個(gè)ArrayList[]數(shù)組,非常正確。既然編譯器不能阻止您通過這個(gè)方法來戰(zhàn)勝類型安全,那么它轉(zhuǎn)而阻止您創(chuàng)建一個(gè)參數(shù)化類型的數(shù)組。所以上述情節(jié)永遠(yuǎn)不會(huì)發(fā)生,編譯器在第一行就開始拒絕編譯了。

  注意這并不是一個(gè)在使用數(shù)組時(shí)使用泛型的全部的約束,這僅僅是一個(gè)創(chuàng)建一個(gè)參數(shù)化類型數(shù)組的約束。我們將在學(xué)習(xí)如何寫泛型方法時(shí)再來討論這個(gè)話題。

  類型參數(shù)通配符

  假設(shè)我們需要寫一個(gè)方法來顯示一個(gè)List中的元素。[3]在以前,我們只需要象這樣寫段代碼:

public static void printList(PrintWriter out, List list) {
????for(int i=0, n=list.size(); i < n; i++) {
????????if (i > 0) out.print(", ");
????????out.print(list.get(i).toString());
????}
}



在Java5.0中,List是一個(gè)泛型類型,如果我們?cè)噲D編譯這個(gè)方法,我們將會(huì)得到unchecked警告。為了解決這些警告,您可能需要這樣來修改這個(gè)方法:

public static void printList(PrintWriter out, List<Object> list) {
????for(int i=0, n=list.size(); i < n; i++) {
????????if (i > 0) out.print(", ");
????????out.print(list.get(i).toString());
????}
}
  這段代碼能夠編譯通過同時(shí)不會(huì)有警告,但是它并不是非常地有效,因?yàn)橹挥心切┍宦暶鳛長(zhǎng)ist<Object>的list才會(huì)被允許使用這個(gè)方法。還記得么,類似于List<String>和List<Integer>這樣的List并不能被轉(zhuǎn)型為L(zhǎng)ist<Object>。事實(shí)上我們需要一個(gè)類型安全的printList()方法,它能夠接受我們傳入的任何List,而不關(guān)心它被參數(shù)化為什么。解決辦法是使用類型參數(shù)通配符。方法可以被修改成這樣:

public static void printList(PrintWriter out, List<?> list) {
????for(int i=0, n=list.size(); i < n; i++) {
????????if (i > 0) out.print(", ");
????????Object o = list.get(i);
????????out.print(o.toString());
????}
}
  這個(gè)版本的方法能夠被編譯過,沒有警告,而且能夠在任何我們希望使用的地方使用。通配符“?”表示一個(gè)未知類型,類型List<?>被讀作“List of unknown”
作為一般原則,如果類型是泛型的,同時(shí)您并不知道或者并不關(guān)心值的類型,您應(yīng)該使用“?”通配符來代替一個(gè)未經(jīng)處理的類型。未經(jīng)處理的類型被允許僅是為了向下兼容,而且應(yīng)該只能夠被允許出現(xiàn)在老的代碼中。注意,無論如何,您不能在調(diào)用構(gòu)造器時(shí)使用通配符。下面的代碼是非法的:

List<?> l = new ArrayList<?>();

  創(chuàng)建一個(gè)不知道類型的List是毫無道理的。如果您創(chuàng)建了它,那么您必須知道它將保持的元素是什么類型的。您可以在隨后的方法中不關(guān)心元素類型而去遍歷這里list,但是您需要在您創(chuàng)建它的時(shí)候描述元素的類型。如果你確實(shí)需要一個(gè)List來保持任何類型,那么您只能這么寫:

List<Object> l = new ArrayList<Object>();
  從上面的printList()例子中,必須要搞清楚List<?>既不是List<Object>也不是一個(gè)未經(jīng)處理的List。一個(gè)使用通配符的List<?>有兩個(gè)重要的特性。第一,考察類似于get()的方法,他們被聲明返回一個(gè)值,這個(gè)值的類型是類型參數(shù)中指定的。在這個(gè)例子中,類型是“unknown”,所以這些方法返回一個(gè)Object。既然我們期望的是調(diào)用這個(gè)object的toString()方法,程序能夠很好的滿足我們的意愿。

  第二,考察List的類似add()的方法,他們被聲明為接受一個(gè)參數(shù),這個(gè)參數(shù)被類型參數(shù)所定義。出人意料的是,當(dāng)類型參數(shù)是未確定的,編譯器不允許您調(diào)用任何有不確定參數(shù)類型的方法——因?yàn)樗荒艽_認(rèn)您傳入了一個(gè)恰當(dāng)?shù)闹怠R粋€(gè)List(?)實(shí)際上是只讀的——既然編譯器不允許我們調(diào)用類似于add(),set(),addAll()這類的方法。

  界定通配符

  讓我們?cè)谖覀冊(cè)瓉淼睦由献餍┬⌒〉纳晕?fù)雜一點(diǎn)的改動(dòng)。假設(shè)我們希望寫一個(gè)sumList()方法來計(jì)算list中Number類型的值的合計(jì)。在以前,我們使用未經(jīng)處理的List,但是我們不想放棄類型安全,同時(shí)不得不處理來自編譯器的unchecked警告。或者我們可以使用List<Number>,那樣的話我們就不能調(diào)用List<Integer>、List<Double>中的方法了,而事實(shí)上我們需要調(diào)用。如果我們使用通配符,那么我們實(shí)際上不能得到我們期望的類型安全,我們不能確定我們的方法被什么樣的List所調(diào)用,Number?還是Number的子類?甚至,String?這樣的一個(gè)方法也許會(huì)被寫成這樣:

public static double sumList(List<?> list) {
????double total = 0.0;
????for(Object o : list) {
????????Number n = (Number) o;??// A cast is required and may fail
????????total += n.doubleValue();
????}
????return total;
}
  要修改這個(gè)方法讓它變得真正的類型安全,我們需要使用界定通配符(bounded wildcard),能夠確保List的類型參數(shù)是未知的,但又是Number或者Number的子類。下面的代碼才是我們想要的:

public static double sumList(List<? extends Number> list) {
????double total = 0.0;
????for(Number n : list) total += n.doubleValue();
????return total;
}
  類型List<? extends Number>可以被理解為“Number未知子類的List”。理解這點(diǎn)非常重要,在這段文字中,Number被認(rèn)為是其自身的子類。

  注意,這樣的話,那些類型轉(zhuǎn)換已經(jīng)不再需要了。我們并不知道list中元素的具體類型,但是我們知道他們能夠向上轉(zhuǎn)型為Number,因此我們可以把他們從list中把他們當(dāng)作一個(gè)Number對(duì)象取出。使用一個(gè)for/in循環(huán)能夠稍微封裝一下從list中取出元素的過程。普遍性的原則是當(dāng)您使用一個(gè)界定通配符時(shí),類似于List中的get()方法的那些方法將返回一個(gè)類型為上界的值。因此如果我們?cè)趂or/in循環(huán)中調(diào)用list.get(),我們將得到一個(gè)Number。在前一節(jié)說到使用通配符時(shí)類似于list.add()這種方法中的限制依然有效:舉個(gè)例子來說,如果編譯器允許我們調(diào)用這類方法,我們就可以將一個(gè)Integer放到一個(gè)聲明為僅保持Short值的list中去。

  同樣可行的是使用下界通配符,不同的是用super替換extends。這個(gè)技巧在被調(diào)用的方法上有一點(diǎn)不同的作用。在實(shí)際應(yīng)用中,下界通配符要比上界通配符用得少。我們將在后面的章節(jié)里討論這個(gè)問題。



]]>
java程序如何穿透帶有密碼驗(yàn)證的代理http://www.aygfsteel.com/xixidabao/archive/2006/05/10/45558.htmlJAVA之路JAVA之路Wed, 10 May 2006 14:57:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/10/45558.html
use?inner?instead?of?extends?is?the?most?different?

網(wǎng)上也有一些文章但是大多數(shù)涉及帶有授權(quán)驗(yàn)證的proxy都有問題,
主要問題就是出在對(duì)?Authenticator.setDefault的使用,以及base64編碼的問題上
代碼是最沒有二義性的文檔,實(shí)現(xiàn)原理不再解釋,請(qǐng)看代碼去體會(huì)。
如果轉(zhuǎn)載請(qǐng)注明代碼出處。

chimae@cnjsp.org

package?org.chimae.net;

import?java.io.BufferedReader;
import?java.io.IOException;
import?java.io.InputStream;
import?java.io.InputStreamReader;
import?java.net.Authenticator;
import?java.net.HttpURLConnection;
import?java.net.PasswordAuthentication;
import?java.net.URL;

/**
?*?@author?chimae@cnjsp.org
?*/
public?class?ProxyConnTest?{
????
????public?static?void?initProxy(String?host,?int?port,?final?String?username,
????????????final?String?password)?{
????????Authenticator.setDefault(new?Authenticator()?{
????????????protected?PasswordAuthentication?getPasswordAuthentication()?{
????????????????return?new?PasswordAuthentication(username,
????????????????????????new?String(password).toCharArray());
????????????}
????????});

????
????????System.setProperty(\"http.proxyType\",?\"4\");
????????System.setProperty(\"http.proxyPort\",?Integer.toString(port));
????????System.setProperty(\"http.proxyHost\",?host);
????????System.setProperty(\"http.proxySet\",?\"true\");
????}
????
????
????public?static?void?main(String[]?args)?throws?IOException?{
????????String?url?=?\"http://java.sun.com/\";
????????String?proxy?=?\"yourProxy\";
????????int?port?=8080;
????????String?username?=\"username\";
????????String?password?=\"password\";
????????String?curLine?=?\"\";
????????String?content?=?\"\";????????
????????URL?server?=?new?URL(url);
????????initProxy(proxy,port,username,password);
????????HttpURLConnection?connection?=?(HttpURLConnection)server.openConnection();
????????connection.connect();
????????InputStream?is?=?connection.getInputStream();
????????BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(is));
????????while?((curLine?=?reader.readLine())?!=?null)?{
????????????????content?+=?curLine;
????????????}

????????System.out.println(\"content=?\"?+?content);
????????is.close();
????}

}



]]>
Ruby,Java的勁敵http://www.aygfsteel.com/xixidabao/archive/2006/05/08/44933.htmlJAVA之路JAVA之路Sun, 07 May 2006 16:18:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/08/44933.html??? Bruce Tate:一石激起千層浪

??? Bruce Tate并不是作為一個(gè)局外者寫就《超越Java》這邊書的。他的顧問公司專注于Java 持久化框架和輕量級(jí)開發(fā)方法,同時(shí)他也是這些流行的Java圖書的作者, Spring: A Developer's Notebook, Better, Faster, Lighter Java, 以及 Bitter Java.

??? 1,在《超越Java》中你花費(fèi)了大量的時(shí)間在Ruby上面,看起來是它像在你說那些將超越Java競(jìng)爭(zhēng)者中出類拔萃。你覺得是什么使Ruby比 PHP,Python這類語言優(yōu)越?

??? 這些都是好語言,但是都有一些缺點(diǎn)。對(duì)大型應(yīng)用,PHP和Perl不能連續(xù)地產(chǎn)生可讀的代碼。Lisp,Python和Smalltalk這些就缺少了偉大語言好像應(yīng)該擁有的催化劑。Ruby是一種好語言,和催化劑(Rails)提供了引人注目得新價(jià)值(以效率的角度)以及還在飛速地增長(zhǎng)。Ruby不一定是最好的語言,但是它將是我所見過最有可能的。Ruby不大可能在委員會(huì)那里超過Java.它很有可能首先在一個(gè)更小但是卻重要的環(huán)境中取得好成績(jī)。這個(gè)環(huán)境也就是一個(gè)有web UI大的胖關(guān)系數(shù)據(jù)庫。

??? 2,是否Rails就意味著Ruby?其他語言包括Java難道就不能實(shí)現(xiàn)同樣的思想?

??? 如今,Rails就是超過象Netscape之類語言的催化劑,具有Java一樣的功能,可通過網(wǎng)絡(luò)實(shí)現(xiàn)應(yīng)用的傳送。但是我認(rèn)為Rails很有可能僅僅是Ruby元編程框架浪潮的第一波。

??? 3,你的書中很多都基于典型的“將一個(gè)web接口連接到數(shù)據(jù)庫”場(chǎng)景,Ruby的成功案例看上去也僅僅是一兩個(gè)開發(fā)人員的小項(xiàng)目。但是你也承認(rèn)了Java的重量級(jí)企業(yè)框架對(duì)一些項(xiàng)目的價(jià)值(即大型系統(tǒng)上的大型應(yīng)用)。什么情況下一個(gè)項(xiàng)目對(duì)于RoR來說過于大的呢?如果一個(gè)RoR在那方面的特性發(fā)展緩慢呢?

??? 有Ruby和小團(tuán)隊(duì)你可以做很多事情。基礎(chǔ)代碼幾乎都是一個(gè)人寫就的,但卻關(guān)乎整個(gè)公司的生計(jì)。在一些主要的公司開始進(jìn)行認(rèn)真的嘗試之前,我們不知道你可以利用ruby或者rails到什么程度。其中一個(gè)最吸引我的事情是經(jīng)濟(jì)的規(guī)模,更小的規(guī)模。萬一生產(chǎn)力的數(shù)字是真實(shí)的呢?萬一確實(shí)可以得到5X的增長(zhǎng)?那么你可以在一個(gè)部門內(nèi)劃分工作,將工作劃分給團(tuán)隊(duì)中的一個(gè)。交流將很少會(huì)成為問題。管理和疏忽也很少會(huì)成為問題了。我們都知道對(duì)于一間公司增長(zhǎng), tipping points意味著什么。因?yàn)樵黾訙贤ê凸芾淼募?jí)別會(huì)產(chǎn)生很多的障礙, 所以一間公司增長(zhǎng)要超過1,5,10,40,甚至100倍是很困難的。但是,在這一點(diǎn)上, Ruby on Rails的可擴(kuò)展性是非常的好。

??? 4,你是否看到Java開發(fā)人員轉(zhuǎn)向Ruby嗎,還是Ruby將會(huì)給新一代的開發(fā)人員采用?

??? 我覺得兩者都有可能。有開發(fā)人員不能容忍學(xué)習(xí)servlets, Spring, XML, Hibernate, Struts 然后還要學(xué)習(xí)一些 UI 粘合的框架。在Rails中,他們將會(huì)完全給釋放出來。同時(shí)也有Java開發(fā)人員已經(jīng)在尋找更加優(yōu)勢(shì)的方法,他們發(fā)現(xiàn)了Ruby on Rails.接受了Rails的Java夢(mèng)想家們的數(shù)目是令人驚愕的,他們有Thought Works,James Duncan Davidson,Stuart Halloway 更有 David Geary.

??? 5,難道Java本身就不能做一些事情來維持它的杰出地位?如果過于復(fù)雜和膨脹,什么可以阻止開發(fā)人員倒退到j(luò)dk 1.4?

??? Java將會(huì)繼續(xù)處于頂峰,并在企業(yè)應(yīng)用上保持良好的表現(xiàn),但是時(shí)間不會(huì)停滯不前。在某種意味上它終將會(huì)給替代。我們將需要一個(gè)更高級(jí)別的抽象。我認(rèn)為我們最好的希望就是在JVM上做充足的投入,更好地支持動(dòng)態(tài)語言, 擁抱新的事物,對(duì)于舊有的java代碼,則最好是保留保守的態(tài)度。

??? 6,我們應(yīng)該期望Ruby在其他領(lǐng)域引起轟動(dòng)?如果對(duì)于開發(fā)web應(yīng)用它是如此不錯(cuò),假如Ruby有的可以使用的合適的UI框架,會(huì)不會(huì)在桌面應(yīng)用也實(shí)用呢?

??? 現(xiàn)在說什么還為時(shí)過早。如今,盡管Ruby是有催化作用(Rails)的語言,但是它僅僅是一個(gè)候選。以后將會(huì)發(fā)生什么?我想誰也不知道。

??? James Duncan Davidson:嘗試新事務(wù)

??? 如果你使用Tomcat或者Ant(認(rèn)真地說,什么Java開發(fā)人員什么使用過?)那么你就熟悉了James Duncan Davidson的工作了。在Sun,他致力把這些項(xiàng)目開源并且把他們捐獻(xiàn)給Apache基金會(huì)。并且他也編寫了Servlet API的最初兩個(gè)版本,還有處理XML的Java API.離開Sun之后,他做起了Mac OS 的X開發(fā)。編寫《Running Mac OS X Panther》和參與編寫了《Running Mac OS X Tiger》,《 Mac OS X Panther Hacks》,《 Cocoa in a Nutshell》和《Learning Cocoa with Objective-C, 2nd Edition》

??? 1,上一次我們見到你的時(shí)候,你還是那個(gè)《Mac desktop apps in Cocoa》家伙。而現(xiàn)在,我在你的blog上看到你已經(jīng)深深地陷入了Rails.那是什么回事?

??? 我當(dāng)時(shí)窮的要命和急切地需要錢。那時(shí)我剛剛買了一幢新房,并且抵押付款期限就快到期了――噢,等會(huì),你想我認(rèn)真點(diǎn)嗎?好吧,事實(shí)是我和我的幾個(gè)朋友已經(jīng)一直在想一起工作一段時(shí)間了。當(dāng)恰當(dāng)?shù)臅r(shí)機(jī)到的時(shí)候,我們給項(xiàng)目做了技術(shù)評(píng)估,Rails成了首選。那時(shí)我還沒用過Rails或者Ruby.但是我是不會(huì)讓小小的需要學(xué)習(xí)阻礙我去做那個(gè)項(xiàng)目的。今年我已經(jīng)學(xué)習(xí)了三種,可能四種語言了。我不再相信一種語言可以做任何事了。如果我需要學(xué)習(xí)一些新知識(shí)去一些事情,我將全力以赴去學(xué)好它。

??? 2,你對(duì)Rails有什么看法?

??? 主要是簡(jiǎn)單性。完成事情的容易程度。我做的那個(gè)應(yīng)用的第一個(gè)項(xiàng)目原來是一個(gè)基于Java的web應(yīng)用。每個(gè)人都知道一定會(huì)有一種更好,更快,更容易的方法的。Ruby一直都是一種好語言――并且是一種有趣的語言――因此建立于它之上的這個(gè)框架,它應(yīng)得到關(guān)注。

??? 3,Ruby的晦澀和Rails的新穎對(duì)客戶來說會(huì)不會(huì)是一個(gè)問題?

??? 不全是。如今事實(shí)上恰恰相反。有太多潛在的工作, 缺并沒有足夠的人在真正地開發(fā)Ruby on Rails應(yīng)用。

??? 4,為什么Ruby會(huì)如此特殊?難道Rails就不能在其他語言中實(shí)現(xiàn)?難道它就不能給Java實(shí)現(xiàn)?

??? 很少有其他語言可以完成Rails,或者像Rails那樣的。Java不在他們之列。Rails從Ruby中獲取了一些妙不可言的東西,嘗試用另一種語言復(fù)制它不僅是對(duì)Rails所做的是一個(gè)浪費(fèi),對(duì)其他語言來說也是一個(gè)浪費(fèi)。但是它的概念一定會(huì)在其他非常動(dòng)態(tài)的,動(dòng)態(tài)類型語言中得到很好的應(yīng)用。

??? 確實(shí),我很興奮的看到其他項(xiàng)目正實(shí)現(xiàn)一些從Rails衍生的主意到其他平臺(tái)中。例如作為一個(gè)Python里的Rails版本,Django得到了一些固定的發(fā)展。但是,實(shí)際上它是Python自己的龐然大物,它如何成長(zhǎng)將會(huì)非常有趣。

??? 現(xiàn)在,我已經(jīng)說過了你不能用Java來實(shí)現(xiàn)Rails.但卻并不意味著你不能用Java做一些同樣優(yōu)秀的東西。Java的力量可以以一種有趣的,神奇方式應(yīng)用到一種全新的框架上。只是還沒人做那些事情。每個(gè)人都對(duì)J2EE這個(gè)糕點(diǎn)趨之若騖,以致于沒人以一種更加激烈,更加動(dòng)態(tài)的方式來重新考慮問題。盡管有人提出一個(gè)基于Java的殺手級(jí)的框架可以與Rails做同樣多工作, 它一定也不能做的象Rails一樣。

??? 5,具有良好設(shè)計(jì)的Java應(yīng)用能夠很好地支持特性的擴(kuò)展――設(shè)計(jì)好你的類和包,那么你的心情將舒暢好長(zhǎng)的一段時(shí)間。能否有團(tuán)隊(duì)編寫出一個(gè)真正大型的Ruby應(yīng)用?它是否具可維護(hù)性?或者還是RoR只能小打小鬧?

??? 設(shè)計(jì)良好的應(yīng)用無論是以何種語言編寫的都能夠很好地支持特性的擴(kuò)展。糟糕的設(shè)計(jì)無論是何種語言就不能了。同時(shí)也有了如何才是大型應(yīng)用的定義的問題。我用Ruby寫的第一個(gè)rails應(yīng)用部署到生產(chǎn)也不夠5,000行代碼,但是我之前用其他語言編寫的同樣大小的應(yīng)用卻達(dá)到了50,000行代碼,所以如何定義大型是個(gè)問題。

??? 有團(tuán)隊(duì)可以編寫一個(gè)可以支持大量特性,運(yùn)行良好,時(shí)間上具備可維護(hù)性的Ruby on rails應(yīng)用嗎?是的,毫無疑問。在使用了Ruby on Rails一段時(shí)間后,我將有信心用Rails解決任何尺寸的web應(yīng)用問題。但是,那是因?yàn)槲以谒厦婊ㄙM(fèi)了一些時(shí)間,認(rèn)識(shí)到編寫一個(gè)具有良好設(shè)計(jì)的應(yīng)用是有可能的。

??? 也就是說,很有可能現(xiàn)在正有幾十個(gè)垃圾的Ruby on rails應(yīng)用在編寫中。幾百或者幾千個(gè)都有可能。如果你不知道你正在做什么,你將會(huì)編寫一個(gè)垃圾的應(yīng)用。

??? 6,那么我們回到了web應(yīng)用,你可以在桌面上使用ruby,或者我們是否一直要用C#,面向?qū)ο驝還是OS服務(wù)商支持的語言編寫UI?

??? 嗯,我的生活的一部分就是回到web應(yīng)用。它對(duì)我來說是一個(gè)很好的還環(huán)境,因?yàn)樽詮?994年開始我就一直在做基于web的工作。但是現(xiàn)在我將開發(fā)基于桌面的應(yīng)用。而且人們對(duì)桌面應(yīng)用的需求還很大。我可不想要一個(gè)網(wǎng)絡(luò)的office.你也不想把一些象Aperture的東西建造成一個(gè)web應(yīng)用吧。

??? 你現(xiàn)在可以使用Ruby去建造一個(gè)引人注目的桌面應(yīng)用嗎?不,相關(guān)的工具包還不存在。但是如果存在了恰當(dāng)?shù)墓ぞ甙D―這是有可能的。那就沒有什么東西可以阻止它成為一個(gè)好的桌面應(yīng)用語言了。那是說,我已經(jīng)發(fā)現(xiàn)利用平臺(tái)的最好的方法就是盡量的本地化-貼近平臺(tái),不管它是一個(gè)操作系統(tǒng)或者還是一個(gè)web應(yīng)用框架。當(dāng)我在Mac上的桌面工作,我需要寫面向?qū)ο驝和Cocoa.當(dāng)我用Rails的web工作,那意味著使用Ruby.而對(duì)于操作系統(tǒng)方面的工作,我需要用到C和shell.在這個(gè)討論中不會(huì)只有唯一的答案。

??? 我認(rèn)為這就是最近對(duì)Ruby on Rails關(guān)注和屏棄以有色Java眼鏡看待世界的真正勝利。Ruby并不會(huì)成為下一個(gè)Java,完全不。而是Ruby on Rails將會(huì)幫助打破了這樣的一個(gè)觀點(diǎn)—— “只有一個(gè)正確的方法”,不是的。解決問題的方法有千百條。真正的,他們中沒有一個(gè)是明顯的勝者。只有解決方案有優(yōu)勢(shì)的位置。

??? 我想就像我們?cè)谄渲泄ぷ鳎燥埡途幼〉慕ㄖ镆粯印R恍┙ㄖ镒詈檬怯盟嗪弯摻钪臁F渌氖怯闷鲋_€有其他的最好是用木材,并且那樣做是有理由的。沒有人會(huì)跳起來說“所有的建筑物一定要用磚頭筑造!”,那樣太愚蠢了!同樣的道理,不是所以的應(yīng)用都應(yīng)該要用Ruby on Rails或者Django或者J2EE或者Perl來編寫的。對(duì)于任何一個(gè)特定的工作都有大量的工具。還有新的工具等待去發(fā)掘呢。訣竅就是決定最優(yōu)秀的那個(gè)。

??? 讓我們從夸夸其談回到你的問題:在web應(yīng)用的范疇,很容易出現(xiàn)一個(gè)新的框架的,因?yàn)槟悴⒉皇桥c視頻卡,GUI和應(yīng)用在上面跑的整個(gè)系統(tǒng)之類打交道。除非是你愿意開發(fā)一個(gè)自己的框架,你必須面對(duì)選擇使用哪個(gè)框架的選擇。在桌面上也是同樣的道理。你可以創(chuàng)建你自己的框架,做任何你想要的做的,但是該建議卻不比你自己為web創(chuàng)建一個(gè)新框架容易。

??? Robert Cooper:帶上利器

??? ONJava的博客Robert Cooper最近在他的blog上撰寫了“It's the End of the World as We Know It."”來回應(yīng)一些“Java時(shí)代末日”的言論。Cooper是亞特蘭大地區(qū)致力于企業(yè)集成和web/web服務(wù)應(yīng)用的J2EE開發(fā)人員,同時(shí)也是資訊和娛樂站點(diǎn)screaming-penguin.com的經(jīng)營(yíng)者。

??? 1,你曾經(jīng)說過“長(zhǎng)期以來‘企業(yè)級(jí)’Java一直未能逃脫的一個(gè)很悲哀的事實(shí)是500個(gè)應(yīng)用才需要‘企業(yè)級(jí)’的功能。”為什么Java開發(fā)人員采用了比他們實(shí)際需要更加復(fù)雜的框架?

??? 好的,有一些因素導(dǎo)致了這樣現(xiàn)象。一個(gè)是“buzzword compliance”。 你想使用你“應(yīng)該”要使用的東西。我記得在99年一些大項(xiàng)目采用了entity bean作為數(shù)據(jù)模型,但是我們很快發(fā)現(xiàn)了性能是如此的恐怖以致于我們最終又轉(zhuǎn)到了手寫的DAO層。

??? 最近對(duì)javax.persistence的修改,一定程度也表明了,EJB的失敗一直都是缺乏不同級(jí)別的支持。理想的情況是,如果我僅需要基本的,簡(jiǎn)單的ORM-類型功能,我就能夠很快的得到。如果我想深入真正復(fù)雜的東西,給我一個(gè)“更深層”的有分布式事務(wù)的視圖。然而,盡管在那樣高的層次上,在EJB1.1/1.2的世界里,看看你需要多少行代碼1)從JNDI獲得Home存根,2)做一個(gè)find by,3)做改變,4)提交事務(wù)。對(duì)于一般的應(yīng)用,答案沒有理由只應(yīng)獨(dú)一無‘二’。然而更新穎的Java框架(閱讀:Spring + Hibernate)使你獲得了那一個(gè)‘二’,但是你也要做一大堆的配置。那樣,很多方面, 混淆了你的代碼。大量的因素促成我我的演說“擁有一個(gè)有效的默認(rèn)配置/操作”,但是那是不同的故事了。

??? 2,你一向不屑把Ruby on Rails看作是technorati中的后起之秀。你是根本就不想接受還是只是厭惡這種夸夸其談呢?

??? 并不是我真的這樣不屑。Rails在很多重要的方面來說非常優(yōu)秀。事實(shí)上,如果PHP是那要死的飛行意粉怪,并且要給Ruby替代,我想那將是一個(gè)大進(jìn)步。然而,盡管Ruby確實(shí)掃除了過去的錯(cuò)誤,它仍然缺乏Java那么多的功能,但是Ruby為快速開發(fā)提供了一個(gè)引人注目新的開發(fā)模型。你可能反對(duì),這僅僅是時(shí)間問題,假以時(shí)日,它一定可以的。然而,我對(duì)Ruby/Rails有一些敵意, 是因?yàn)槲乙恍┮恢倍荚谙M鹙ava能夠擁有的特性,一直長(zhǎng)期希望J2EE能夠擁有簡(jiǎn)單性。

??? 3,那么是什么促使你繼續(xù)留在Java陣營(yíng),你看中它的什么呢?

??? 按照我日常的工作,在400上沒有必要使用Ruby調(diào)用PCML/RPG程序。同樣,那些大量的java擁有的 “企業(yè)級(jí)”特性很重要,更不用說它是一個(gè)統(tǒng)一的打包和部署的框架。

??? 4,你說“然而,Java像是變成了無所不包的了,它不是‘web的語言’,也不是桌面應(yīng)用的‘一等公民’。”Java是否應(yīng)該放棄一些野心,專注在一個(gè)更小應(yīng)用空間集合里?如果那樣,放棄哪個(gè)?

??? 你也知道,我在我的站點(diǎn)上和一個(gè)紳士有很長(zhǎng)的討論。他指出了Java在J2ME世界的成功,TME/TiVo,置頂盒——或許是下一代DVD的混戰(zhàn)。這些對(duì)于開發(fā)來說都是有效的領(lǐng)域,但是我認(rèn)為如果Java變成了這樣的一個(gè)系統(tǒng),那將是一個(gè)損失。

??? 使我惱怒的是Java發(fā)明的“applet”,你看看Flash(加上Flex/Laszlo),它的“Cool”(快速的用戶體驗(yàn))和“強(qiáng)大的”(我免費(fèi)的得到數(shù)據(jù)綁定/SOAP/XML-RPC等等)使applets無地自容。“強(qiáng)大的”缺不需要JRE的事實(shí)立馬扼殺了applet的生存空間,如果有人能以接近數(shù)目的代碼行數(shù)向我展示Laszlo Dashboard demo,我可能已經(jīng)在那個(gè)方面得到了一個(gè)核心發(fā)展。Cool是需要很大的代價(jià)的。

??? 5,Java越來越多的復(fù)雜性,越來越多的競(jìng)爭(zhēng)框架,這是你之前批評(píng)很多次的。我們用JDK 1.2的語義編碼,手工編寫servlet是不是更好呢?

??? 我認(rèn)為的復(fù)雜性是很難管理。例如,如果你從一個(gè)VB背景開始關(guān)注使用Swing,那是非常令人難受的。當(dāng)你需要做一些技巧性的東西,沒有一個(gè)“簡(jiǎn)單”接口可以Cast成更加“高級(jí)”的接口。坦白的說,最近出現(xiàn)的一個(gè)有用的東西就是JAX-P了。在我頭腦中,還有一些東西jre沒有的,但是卻是必須存在的。Swing存在有多久了?然而還沒有東西可以給你像VB5的數(shù)據(jù)綁定表格控制的功能。

??? 我想JDK 1.5的提升是非常顯著的。當(dāng)我談到“降低復(fù)雜性”,我真正指的是A)對(duì)于一件事情, 給予更多標(biāo)準(zhǔn)的途徑來完成,因此如果我真正需要一些不同的特性,我僅僅需要一個(gè)外部的框架。B)設(shè)計(jì)更加友好的API――認(rèn)真的說,看看JavaMail的JavaDoc,看看研究出如何發(fā)送一個(gè)HTML格式的email要花費(fèi)你多長(zhǎng)時(shí)間。C)增加更加通用的功能到核心運(yùn)行環(huán)境,提供一個(gè)可以分別與基于Flash,RIA和桌面領(lǐng)域的Avalon/Sparkle相比擬的風(fēng)格和性能。同樣的,我記得以前天真地從VB/VC++轉(zhuǎn)到Java世界,想道“天哪,本來一直就應(yīng)該是這樣的。”幾年前,我不能說我看到任何增加到Java的東西都是和我有同樣的想法(除了將要來臨的JAX-WS API)。看看Rails,你會(huì)有同樣的感覺。看看Flex你也有同樣的感覺。看看Avalon你也有同樣的感覺。不是我不喜歡Ruby,只是看上去Java不再可以與時(shí)俱進(jìn)讓我很沮喪。

??? Bill Venners:發(fā)行人的觀點(diǎn)

??? Artima是很多Java開發(fā)人員高度關(guān)注的站點(diǎn)。長(zhǎng)久以來它的發(fā)行人Bill Venners是一個(gè)Java著作者和顧問。同時(shí)也是一個(gè)JavaWorld的專欄作家,Inside the Java Virtual Machine的作者。所以,當(dāng)我們注意到Artima上的Ruby內(nèi)容,我們必須找出背后的故事。

??? 1,Artima在很多人眼中一直都是作為一個(gè)Java站點(diǎn),但是你剛創(chuàng)建一個(gè)一個(gè)新的Ruby版面, Artima當(dāng)今很多的特色文章都是關(guān)于Ruby的,是什么促使了這種改變?

??? 沒有改變。Artima曾經(jīng)是一個(gè)清一色的Java站點(diǎn),但是幾年前我們擴(kuò)展了更一般開發(fā)焦點(diǎn),開始涵蓋其他語言。例如,我們開始在“Python Buzz”集成Python Blogs,在“The C++ Source”刊發(fā)C++文章。我們創(chuàng)建了Ruby Code & Style簡(jiǎn)報(bào)來作為Ruby社區(qū)通過高質(zhì)量,編輯的文章分享信息的地方。

??? 2,你是否認(rèn)為你的Ruby報(bào)道是作為一種趨勢(shì),或者服務(wù)已作出改變的開發(fā)人員?

??? 我們創(chuàng)建Ruby簡(jiǎn)報(bào)僅僅是為了服務(wù)Ruby社區(qū)。我不知道是不是有一個(gè)趨勢(shì),我也沒有看到很多Java開發(fā)人員轉(zhuǎn)到Ruby.人們并不只是僅僅需要用一種語言編程。我想掌握一種系統(tǒng)語言是有好處的,例如Java或者C++,和一種腳本語言,例如Ruby或者Python,而且能夠用兩者工作。那樣的話你就可以使用你手中最好的工具來工作了。

??? 3,你的最初少數(shù)Ruby文章幾乎沒有涉及Rails.你是否認(rèn)為Rails背后有一個(gè)大的Ruby故事?你還知道有什么東西使用了Ruby?

??? 除了知道Rails在市場(chǎng)上很有賣點(diǎn),我對(duì)Rails了解的不多。Rails商人一遍又一遍傳遞了這樣的一個(gè)信息,就是Rails能夠助你很快的創(chuàng)建web應(yīng)用。每個(gè)人都很清楚的收到了這個(gè)信息。我認(rèn)為這是一個(gè)非常好營(yíng)銷工作。我也相信這個(gè)信息,但是快速的創(chuàng)建一個(gè)web應(yīng)用不僅僅是人們所關(guān)心的。有時(shí)人們也關(guān)系與數(shù)據(jù)庫的集成,應(yīng)用服務(wù)器的集群,在這種情形,其他工具可能比Rails更有效率。就Ruby而言,我認(rèn)為它是一種適合腳本和創(chuàng)建系統(tǒng)的多用途的編程語言,與Python同種類別。

??? 4,即使在Rails以前,對(duì)比于其他“敏捷”語言,人們都談?wù)摰絉uby獨(dú)特的吸引Java開發(fā)人員。你認(rèn)為Ruby有什么特別之處呢?為什么它對(duì)于Java移民這么好?

??? 我不相信將會(huì)有很多Java移民或者Ruby尤其適合Java程序員。現(xiàn)在大肆宣稱圍繞著Ruby,或許是因?yàn)镽ails的買賣,所以或許你印象中的移民就是來自于那些宣稱的印象。Ruby是一種好的語言,但是Java也是,Python也是。

??? 5,你是否認(rèn)為我們將會(huì)看到很多Java開發(fā)人員開始學(xué)習(xí)Ruby或者轉(zhuǎn)到Ruby,或者我們將看到一個(gè)新一代直接跳過Java而用Ruby代之?

??? Java不會(huì)離我們而去。在Artima,我們選擇了Java作為新的體系架構(gòu),而不是Ruby,或者Python,就是因?yàn)樗且粋€(gè)成熟的擁有免費(fèi)和商用的大量工具和API的生態(tài)系統(tǒng)。相對(duì)于Java,是的,當(dāng)使用Ruby或者Python編程的時(shí)候是有一些速度的提升,但是有了現(xiàn)代的像IntelliJ,Eclipse和NetBeans的 Java IDE,你可以在Java里走的更快。但是用Ruby編程是很愜意的,同時(shí),如果有人可以從Ruby中找到他們的職業(yè)生涯,那么請(qǐng)全力以赴去實(shí)現(xiàn)。

??? 結(jié)語

??? 是否Ruby將橫掃Java?不僅僅是虔誠(chéng)的Ruby狂熱者在預(yù)言這個(gè)場(chǎng)景。開發(fā)人員的需要觀點(diǎn), 就像Venners提出的“手上對(duì)工作最優(yōu)的工具”。 至關(guān)重要的是,開發(fā)人員必須對(duì)正確理解和使用這些工具負(fù)責(zé)。也就不難看出Coopper對(duì)于EJB 1.0的大肆宣稱的記憶和Davidson的預(yù)言“如今很有可能有很多垃圾的Ruby on Rails應(yīng)用在編寫中”的聯(lián)系了。無視技術(shù),讓市場(chǎng)的浪潮沖走是很危險(xiǎn)的。不僅如此,很多人正在告訴我們使用Ruby會(huì)有相當(dāng)大的效率提升,它確實(shí)是一個(gè)理想的工具,因此我們應(yīng)該給予一定的關(guān)注。

??? 作者感謝Bruce Tate, James Duncan Davidson, Robert Cooper, 和 Bill Venners.感謝占用了他們的時(shí)間與ONJava的讀者分享他們的思想。

??? Chris Adamson是ONJava和Java.net的編輯,專攻Java,Mac Os X和多媒體開發(fā)的亞特蘭大地區(qū)的顧問。

  版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必保留以下作者信息和鏈接
  原文: http://www.onjava.com/pub/a/onjava/2005/11/16/ruby-the-rival.html
  譯文: http://www.matrix.org.cn/resource/article/44/44288_Ruby+Java.html






]]>
Java學(xué)習(xí)之——java面試題集http://www.aygfsteel.com/xixidabao/archive/2006/05/07/44929.htmlJAVA之路JAVA之路Sun, 07 May 2006 15:53:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/07/44929.html  基礎(chǔ)知識(shí):1.C++或Java中的異常處理機(jī)制的簡(jiǎn)單原理和應(yīng)用。

  當(dāng)JAVA程序違反了JAVA的語義規(guī)則時(shí),JAVA虛擬機(jī)就會(huì)將發(fā)生的錯(cuò)誤表示為一個(gè)異常。違反語義規(guī)則包括2種情況。一種是JAVA類庫內(nèi)置的語義檢查。例如數(shù)組下標(biāo)越界,會(huì)引發(fā)IndexOutOfBoundsException;訪問null的對(duì)象時(shí)會(huì)引發(fā)NullPointerException.另一種情況就是JAVA允許程序員擴(kuò)展這種語義檢查,程序員可以創(chuàng)建自己的異常,并自由選擇在何時(shí)用throw關(guān)鍵字引發(fā)異常。所有的異常都是java.lang.Thowable的子類。

  2. Java的接口和C++的虛類的相同和不同處。

  由于Java不支持多繼承,而有可能某個(gè)類或?qū)ο笠褂梅謩e在幾個(gè)類或?qū)ο罄锩娴姆椒ɑ驅(qū)傩裕F(xiàn)有的單繼承機(jī)制就不能滿足要求。與繼承相比,接口有更高的靈活性,因?yàn)榻涌谥袥]有任何實(shí)現(xiàn)代碼。當(dāng)一個(gè)類實(shí)現(xiàn)了接口以后,該類要實(shí)現(xiàn)接口里面所有的方法和屬性,并且接口里面的屬性在默認(rèn)狀態(tài)下面都是public static,所有方法默認(rèn)情況下是public.一個(gè)類可以實(shí)現(xiàn)多個(gè)接口。

  3. 垃圾回收的優(yōu)點(diǎn)和原理。并考慮2種回收機(jī)制。

  Java語言中一個(gè)顯著的特點(diǎn)就是引入了垃圾回收機(jī)制,使c++程序員最頭疼的內(nèi)存管理的問題迎刃而解,它使得Java程序員在編寫程序的時(shí)候不再需要考慮內(nèi)存管理。由于有個(gè)垃圾回收機(jī)制,Java中的對(duì)象不再有“作用域”的概念,只有對(duì)象的引用才有“作用域”。垃圾回收可以有效的防止內(nèi)存泄露,有效的使用可以使用的內(nèi)存。垃圾回收器通常是作為一個(gè)單獨(dú)的低級(jí)別的線程運(yùn)行,不可預(yù)知的情況下對(duì)內(nèi)存堆中已經(jīng)死亡的或者長(zhǎng)時(shí)間沒有使用的對(duì)象進(jìn)行清楚和回收,程序員不能實(shí)時(shí)的調(diào)用垃圾回收器對(duì)某個(gè)對(duì)象或所有對(duì)象進(jìn)行垃圾回收。回收機(jī)制有分代復(fù)制垃圾回收和標(biāo)記垃圾回收,增量垃圾回收。

  4. 請(qǐng)說出你所知道的線程同步的方法。

  wait():使一個(gè)線程處于等待狀態(tài),并且釋放所持有的對(duì)象的lock.

  sleep():使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài),是一個(gè)靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException異常。

  notify():?jiǎn)拘岩粋€(gè)處于等待狀態(tài)的線程,注意的是在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且不是按優(yōu)先級(jí)。

  Allnotity():?jiǎn)拘阉刑幦氲却隣顟B(tài)的線程,注意并不是給所有喚醒線程一個(gè)對(duì)象的鎖,而是讓它們競(jìng)爭(zhēng)。

  5. 請(qǐng)講一講析構(gòu)函數(shù)和虛函數(shù)的用法和作用。

  6. Error與Exception有什么區(qū)別?

  Error表示系統(tǒng)級(jí)的錯(cuò)誤和程序不必處理的異常,

  Exception表示需要捕捉或者需要程序進(jìn)行處理的異常。

  7. 在java中一個(gè)類被聲明為final類型,表示了什么意思?

  表示該類不能被繼承,是頂級(jí)類。

  8. 描述一下你最常用的編程風(fēng)格。

  9. heap和stack有什么區(qū)別。

  棧是一種線形集合,其添加和刪除元素的操作應(yīng)在同一段完成。棧按照后進(jìn)先出的方式進(jìn)行處理。

  堆是棧的一個(gè)組成元素

  10. 如果系統(tǒng)要使用超大整數(shù)(超過long長(zhǎng)度范圍),請(qǐng)你設(shè)計(jì)一個(gè)數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)這種超大型數(shù)字以及設(shè)計(jì)一種算法來實(shí)現(xiàn)超大整數(shù)加法運(yùn)算)。

  public class B{

  int[] ArrOne = new ArrOne[1000];

  String intString="";

  public int[] Arr(String s)

  {

  intString = s;

  for(int i=0;i {

  11. 如果要設(shè)計(jì)一個(gè)圖形系統(tǒng),請(qǐng)你設(shè)計(jì)基本的圖形元件(Point,Line,Rectangle,Triangle)的簡(jiǎn)單實(shí)現(xiàn)

  12,談?wù)刦inal, finally, finalize的區(qū)別。

  final?修飾符(關(guān)鍵字)如果一個(gè)類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個(gè)類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們?cè)谑褂弥胁槐桓淖儭1宦暶鳛閒inal的變量必須在聲明時(shí)給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載。

  finally?再異常處理時(shí)提供 finally 塊來執(zhí)行任何清除操作。如果拋出一個(gè)異常,那么相匹配的 catch 子句就會(huì)執(zhí)行,然后控制就會(huì)進(jìn)入 finally 塊(如果有的話)。

  finalize?方法名。Java 技術(shù)允許使用 finalize() 方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。這個(gè)方法是由垃圾收集器在確定這個(gè)對(duì)象沒有被引用時(shí)對(duì)這個(gè)對(duì)象調(diào)用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作。finalize() 方法是在垃圾收集器刪除對(duì)象之前對(duì)這個(gè)對(duì)象調(diào)用的。

  13,Anonymous Inner Class (匿名內(nèi)部類) 是否可以extends(繼承)其它類,是否可以implements(實(shí)現(xiàn))interface(接口)?

  匿名的內(nèi)部類是沒有名字的內(nèi)部類。不能extends(繼承) 其它類,但一個(gè)內(nèi)部類可以作為一個(gè)接口,由另一個(gè)內(nèi)部類實(shí)現(xiàn)。

  14,Static Nested Class 和 Inner Class的不同,說得越多越好(面試題有的很籠統(tǒng))。

  Nested Class (一般是C++的說法),Inner Class (一般是JAVA的說法)。Java內(nèi)部類與C++嵌套類最大的不同就在于是否有指向外部的引用上。具體可見http: //www.frontfree.net/articles/services/view.ASP?id=704&page=1

  注: 靜態(tài)內(nèi)部類(Inner Class)意味著1創(chuàng)建一個(gè)static內(nèi)部類的對(duì)象,不需要一個(gè)外部類對(duì)象,2不能從一個(gè)static內(nèi)部類的一個(gè)對(duì)象訪問一個(gè)外部類對(duì)象

  第四,&和&&的區(qū)別。

  &是位運(yùn)算符。&&是布爾邏輯運(yùn)算符。

  15,HashMap和Hashtable的區(qū)別。

  都屬于Map接口的類,實(shí)現(xiàn)了將惟一鍵映射到特定的值上。

  HashMap 類沒有分類或者排序。它允許一個(gè) null 鍵和多個(gè) null 值。

  Hashtable 類似于 HashMap,但是不允許 null 鍵和 null 值。它也比 HashMap 慢,因?yàn)樗峭降摹?/p>

  16,Collection 和 Collections的區(qū)別。

  Collections是個(gè)java.util下的類,它包含有各種有關(guān)集合操作的靜態(tài)方法。

  Collection是個(gè)java.util下的接口,它是各種集合結(jié)構(gòu)的父接口。

  17,什么時(shí)候用assert.

  斷言是一個(gè)包含布爾表達(dá)式的語句,在執(zhí)行這個(gè)語句時(shí)假定該表達(dá)式為 true.如果表達(dá)式計(jì)算為 false,那么系統(tǒng)會(huì)報(bào)告一個(gè) Assertionerror.它用于調(diào)試目的:

  assert(a > 0); // throws an Assertionerror if a <= 0

  斷言可以有兩種形式:

  assert Expression1 ;

  assert Expression1 : Expression2 ;

  Expression1 應(yīng)該總是產(chǎn)生一個(gè)布爾值。

  Expression2 可以是得出一個(gè)值的任意表達(dá)式。這個(gè)值用于生成顯示更多調(diào)試信息的 String 消息。

  斷言在默認(rèn)情況下是禁用的。要在編譯時(shí)啟用斷言,需要使用 source 1.4 標(biāo)記:

  javac -source 1.4 Test.java

  要在運(yùn)行時(shí)啟用斷言,可使用 -enableassertions 或者 -ea 標(biāo)記。

  要在運(yùn)行時(shí)選擇禁用斷言,可使用 -da 或者 -disableassertions 標(biāo)記。

  要系統(tǒng)類中啟用斷言,可使用 -esa 或者 -dsa 標(biāo)記。還可以在包的基礎(chǔ)上啟用或者禁用斷言。

  可以在預(yù)計(jì)正常情況下不會(huì)到達(dá)的任何位置上放置斷言。斷言可以用于驗(yàn)證傳遞給私有方法的參數(shù)。不過,斷言不應(yīng)該用于驗(yàn)證傳遞給公有方法的參數(shù),因?yàn)椴还苁欠駟⒂昧藬嘌裕蟹椒ǘ急仨殭z查其參數(shù)。不過,既可以在公有方法中,也可以在非公有方法中利用斷言測(cè)試后置條件。另外,斷言不應(yīng)該以任何方式改變程序的狀態(tài)。

  18,GC是什么? 為什么要有GC? (基礎(chǔ))。

  GC是垃圾收集器。Java 程序員不用擔(dān)心內(nèi)存管理,因?yàn)槔占鲿?huì)自動(dòng)進(jìn)行管理。要請(qǐng)求垃圾收集,可以調(diào)用下面的方法之一:

  System.gc()

  Runtime.getRuntime()。gc()

  19,String s = new String("xyz");創(chuàng)建了幾個(gè)String Object?

  兩個(gè)對(duì)象,一個(gè)是“xyx”,一個(gè)是指向“xyx”的引用對(duì)象s.

  20,Math.round(11.5)等於多少? Math.round(-11.5)等於多少?

  Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11;

  21,short s1 = 1; s1 = s1 + 1;有什么錯(cuò)? short s1 = 1; s1 += 1;有什么錯(cuò)?

  short s1 = 1; s1 = s1 + 1;有錯(cuò),s1是short型,s1+1是int型,不能顯式轉(zhuǎn)化為short型。可修改為s1 =(short)(s1 + 1) .short s1 = 1; s1 += 1正確。

  22,sleep() 和 wait() 有什么區(qū)別? 搞線程的最愛

  sleep()方法是使線程停止一段時(shí)間的方法。在sleep 時(shí)間間隔期滿后,線程不一定立即恢復(fù)執(zhí)行。這是因?yàn)樵谀莻€(gè)時(shí)刻,其它線程可能正在運(yùn)行而且沒有被調(diào)度為放棄執(zhí)行,除非(a)“醒來”的線程具有更高的優(yōu)先級(jí) (b)正在運(yùn)行的線程因?yàn)槠渌蚨枞?/p>

  wait()是線程交互時(shí),如果線程對(duì)一個(gè)同步對(duì)象x 發(fā)出一個(gè)wait()調(diào)用,該線程會(huì)暫停執(zhí)行,被調(diào)對(duì)象進(jìn)入等待狀態(tài),直到被喚醒或等待時(shí)間到。

  23,Java有沒有g(shù)oto?

  Goto?java中的保留字,現(xiàn)在沒有在java中使用。

  24,數(shù)組有沒有l(wèi)ength()這個(gè)方法? String有沒有l(wèi)ength()這個(gè)方法?

  數(shù)組沒有l(wèi)ength()這個(gè)方法,有l(wèi)ength的屬性。

  String有有l(wèi)ength()這個(gè)方法。

  25,Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型?

  方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)。重寫Overriding是父類與子類之間多態(tài)性的一種表現(xiàn),重載Overloading是一個(gè)類中多態(tài)性的一種表現(xiàn)。如果在子類中定義某方法與其父類有相同的名稱和參數(shù),我們說該方法被重寫 (Overriding)。子類的對(duì)象使用這個(gè)方法時(shí),將調(diào)用子類中的定義,對(duì)它而言,父類中的定義如同被“屏蔽”了。如果在一個(gè)類中定義了多個(gè)同名的方法,它們或有不同的參數(shù)個(gè)數(shù)或有不同的參數(shù)類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。

  26,Set里的元素是不能重復(fù)的,那么用什么方法來區(qū)分重復(fù)與否呢? 是用==還是equals()? 它們有何區(qū)別?

  Set里的元素是不能重復(fù)的,那么用iterator()方法來區(qū)分重復(fù)與否。equals()是判讀兩個(gè)Set是否相等。

  equals()和==方法決定引用值是否指向同一對(duì)象equals()在類中被覆蓋,為的是當(dāng)兩個(gè)分離的對(duì)象的內(nèi)容和類型相配的話,返回真值。




igInt()



]]>
Java認(rèn)證考試心得:順利通過SCJP測(cè)試全接觸 http://www.aygfsteel.com/xixidabao/archive/2006/05/04/44488.htmlJAVA之路JAVA之路Wed, 03 May 2006 16:18:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/04/44488.html  Java以其平臺(tái)無關(guān)性、面向?qū)ο蟆⒅С侄嗑€程等優(yōu)點(diǎn)成為越來越多的程序開發(fā)人員的新寵,Java技術(shù)以其獨(dú)特的優(yōu)勢(shì)在越來越多的領(lǐng)域得以使用和發(fā)展。作為程序開發(fā)人員,為了檢驗(yàn)對(duì)Java掌握的程度,參加Sun公司的Java認(rèn)證考試是比較好的一個(gè)方法。兩周前,我以83%成績(jī)通過了SCJP1.4的認(rèn)證考試,有一些體會(huì)想與大家分享。
  
  想順利通過考試,可以從以下幾個(gè)方面著手:
  
  1.參加培訓(xùn)或自學(xué)Java經(jīng)典課程。如果有C語言和面向?qū)ο蟮幕A(chǔ),只需自學(xué)SL275課程以及JDK1.4 API,否則應(yīng)該參加相應(yīng)的培訓(xùn)班進(jìn)行學(xué)習(xí)。我對(duì)C語言和面向?qū)ο蠖加幸恍┗A(chǔ),同時(shí)又參加了SL275+Weblogic+JSP課程的學(xué)習(xí)。
  
  2.確定考試版本,明確考查的知識(shí)點(diǎn)及知識(shí)點(diǎn)的考查難度。目前SCJP考試有兩個(gè)版本1.2和1.4,兩個(gè)版本的比較如下表:
  

  
表1

  其中選擇題有兩種形式:(1)給出一段代碼讓選擇其運(yùn)行結(jié)果;(2)給出關(guān)于某基本概念的一些描述語句讓選擇正確的答案。填空題一般是給出一段正確的代碼,不提供選擇項(xiàng),而讓你填寫上運(yùn)行結(jié)果。
  
  兩個(gè)版本考查的知識(shí)點(diǎn)最大的差別在于310-025包含I/O、AWT,且對(duì)各個(gè)知識(shí)點(diǎn)的考查難度相對(duì)較容易,而310-035中去掉了I/O和AWT部分的內(nèi)容,但增加了對(duì)Assertion的考查,同時(shí)對(duì)Wrapper classes、Collections、hashcode( )和equals( )考查的難度有所加大。所以要根據(jù)自己的情況選擇合適的版本。我在復(fù)習(xí)時(shí)覺得I/O這一部分的內(nèi)容較多且不好記憶,就選擇了310-035,這個(gè)版本較難,也是對(duì)自己的一個(gè)挑戰(zhàn)。
  
  3.根據(jù)考查的知識(shí)點(diǎn)做練習(xí)題。由于認(rèn)證考試是以試題的形式來考查對(duì)知識(shí)點(diǎn)的掌握情況,所以多做題是通過考試的最有效方法之一,通過對(duì)SL275課程系統(tǒng)的學(xué)習(xí),掌握了有關(guān)知識(shí)點(diǎn)的基礎(chǔ)知識(shí)以后,必須通過做題來測(cè)試自己對(duì)知識(shí)點(diǎn)的掌握情況,書中寫的有關(guān)知識(shí)點(diǎn)的內(nèi)容比較概括,有時(shí)自己覺得已經(jīng)掌握了這部分內(nèi)容,但做題時(shí)可能會(huì)出現(xiàn)各式各樣的錯(cuò)誤,比如在聲明main( )方法時(shí)應(yīng)為public static void main( String args[ ] ){……},如果在聲明時(shí)漏寫了static,則在編譯時(shí)可以通過,而運(yùn)行時(shí)會(huì)提示出錯(cuò)信息;如果main( )中的參數(shù)寫string args[ ],則在編譯時(shí)會(huì)提示出錯(cuò),在考試時(shí)有類似的題目,故意設(shè)一些陷阱,一不小心看似簡(jiǎn)單的問題卻有可能失分。對(duì)于給出一段程序代碼要求選擇其運(yùn)行結(jié)果的題目,最好親手調(diào)試并分析其結(jié)果。
  
  對(duì)考查Wrapper classes、Collections、hashcode( )和equals( )方法的題目要認(rèn)真查看API文檔(Sun官方網(wǎng)站提供了下載的鏈接,也可以在Sun網(wǎng)站上在線查看)并做好筆記供以后查閱。通過做題鞏固知識(shí)點(diǎn)、適應(yīng)考試題型、找出容易出錯(cuò)的地方,同時(shí)對(duì)所學(xué)知識(shí)點(diǎn)也起到了查漏補(bǔ)缺的作用。
  
  4.下載相應(yīng)的模擬環(huán)境進(jìn)行實(shí)戰(zhàn)訓(xùn)練,或者在線測(cè)試。知識(shí)點(diǎn)和題型都掌握以后,還不要急于考試,應(yīng)該從網(wǎng)上下載一個(gè)模擬考試環(huán)境的軟件進(jìn)行實(shí)戰(zhàn)演練。我下載了Jcertify5.0,它需在JDK1.3環(huán)境下安裝運(yùn)行,該軟件可以設(shè)置考查的范圍進(jìn)行practice或者test,是一個(gè)很不錯(cuò)的模擬軟件,里面的題目與實(shí)際考試題目難易程度相當(dāng)。
  
  通過該軟件可以檢驗(yàn)?zāi)阍谝?guī)定的時(shí)間內(nèi)(120分鐘)完成題目的情況及各個(gè)知識(shí)點(diǎn)掌握的程度,這樣就可以根據(jù)測(cè)驗(yàn)結(jié)果有針對(duì)性的復(fù)習(xí)掌握的不太好的知識(shí)點(diǎn),反復(fù)測(cè)驗(yàn)、練習(xí),直到自己滿意為止。
  
  5.考前給自己足夠的信心。通過做練習(xí)和模擬軟件測(cè)驗(yàn),對(duì)各個(gè)知識(shí)點(diǎn)都比較有把握以后,其實(shí)這時(shí)參加考試已經(jīng)沒有問題了,要相信自己的實(shí)力,給自己加油,此時(shí)切不可再做較難的題目,我當(dāng)時(shí)就犯了這樣的大忌,聽說310-035的考試比較難,目前通過的人數(shù)不多,我怕考試時(shí)的題目比我做的練習(xí)題難,在考試的前一天,又做了一套題目,共43題,通過率為僅為42%,這個(gè)結(jié)果令我很失望,也喪失了自信心,晚上做夢(mèng)一直考試且考試結(jié)果很糟糕,第二天醒來頭昏腦脹,本來預(yù)約好的考試不得不往后推遲,這是沉痛的教訓(xùn)。
  
  6.預(yù)約、考試。知名的IT認(rèn)證廠商在一些大城市都有指定的考試中心,Sun認(rèn)證也不例外,考試前三天到Sun指定的考試中心報(bào)名,由他們幫你注冊(cè),除了考試做題,其他的事情你盡管交給考試中心去做就行了。
  
  7.考試注意事項(xiàng)。按預(yù)約的時(shí)間提前到達(dá)考試中心,熟悉考試環(huán)境,穩(wěn)定情緒,考試中心在考場(chǎng)里設(shè)置有監(jiān)視器,你的一舉一動(dòng)都會(huì)被錄制下來,所以考試來不得半點(diǎn)虛假,盡早打消“打小抄”的念頭。考務(wù)人員幫你進(jìn)入考試系統(tǒng)后,開始進(jìn)行考試,考試分三個(gè)階段:
  
  (1)正式考試前的問卷調(diào)查,給定時(shí)間為15分,有10來個(gè)問題,給出四個(gè)選項(xiàng)供你選擇,主要調(diào)查你對(duì)SCJP要考查的知識(shí)點(diǎn)的掌握情況,如對(duì)數(shù)組、類的定義是精通、是了解但需要?jiǎng)e人幫助還是不懂等,這部分內(nèi)容的回答不影響你的考試成績(jī),如果你讀懂問題的意思了,你可以根據(jù)自己的實(shí)際情況進(jìn)行選擇,否則你盡管隨意選一個(gè),一路Next下去,直到所有的問題做完;
  
  (2)正式考試,共120分鐘,選擇題在題面中注明了正確答案的數(shù)目(如:choose two),按Next做下一題,單擊exhibit按鈕查看題目提供的代碼;
  
  (3)題目做完后的問卷調(diào)查,同樣也不影響你的考試結(jié)果。問卷結(jié)束后緊張、激動(dòng)的時(shí)刻就要到了,此時(shí)你可以查看考試結(jié)果,考試系統(tǒng)列出你對(duì)各個(gè)知識(shí)點(diǎn)答題情況的一覽表,如果你的成績(jī)超過指定的分?jǐn)?shù)底線,則Result即為Pass。
  
  拿到考試結(jié)果通知單的一刻是令人興奮的,考后的總體感覺是SCJP認(rèn)證并不像自己想象的那么難,只要你做好了充分的準(zhǔn)備,拿到SCJP認(rèn)證證書不成問題。

]]>
JAVA程序員面試32問http://www.aygfsteel.com/xixidabao/archive/2006/05/04/44487.htmlJAVA之路JAVA之路Wed, 03 May 2006 16:10:00 GMThttp://www.aygfsteel.com/xixidabao/archive/2006/05/04/44487.html第一,談?wù)刦inal, finally, finalize的區(qū)別。
final 修飾符(關(guān)鍵字)如果一個(gè)類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個(gè)類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們?cè)谑褂弥胁槐桓淖儭1宦暶鳛閒inal的變量必須在聲明時(shí)給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載
finally 再異常處理時(shí)提供 finally 塊來執(zhí)行任何清除操作。如果拋出一個(gè)異常,那么相匹配的 catch 子句就會(huì)執(zhí)行,然后控制就會(huì)進(jìn)入 finally 塊(如果有的話)。
finalize?方法名。Java 技術(shù)允許使用 finalize() 方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。這個(gè)方法是由垃圾收集器在確定這個(gè)對(duì)象沒有被引用時(shí)對(duì)這個(gè)對(duì)象調(diào)用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作。finalize() 方法是在垃圾收集器刪除對(duì)象之前對(duì)這個(gè)對(duì)象調(diào)用的。

第二,Anonymous Inner Class (匿名內(nèi)部類) 是否可以extends(繼承)其它類,是否可以implements(實(shí)現(xiàn))interface(接口)?
匿名的內(nèi)部類是沒有名字的內(nèi)部類。不能extends(繼承) 其它類,但一個(gè)內(nèi)部類可以作為一個(gè)接口,由另一個(gè)內(nèi)部類實(shí)現(xiàn)。

第三,Static Nested Class 和 Inner Class的不同,說得越多越好(面試題有的很籠統(tǒng))。
Nested Class (一般是C++的說法),Inner Class (一般是JAVA的說法)。Java內(nèi)部類與C++嵌套類最大的不同就在于是否有指向外部的引用上。具體可見http: //www.frontfree.net/articles/services/view.asp?id=704&page=1
注: 靜態(tài)內(nèi)部類(Inner Class)意味著1創(chuàng)建一個(gè)static內(nèi)部類的對(duì)象,不需要一個(gè)外部類對(duì)象,2不能從一個(gè)static內(nèi)部類的一個(gè)對(duì)象訪問一個(gè)外部類對(duì)象

第四,&和&&的區(qū)別。
&是位運(yùn)算符。&&是布爾邏輯運(yùn)算符。

第五,HashMap和Hashtable的區(qū)別。
都屬于Map接口的類,實(shí)現(xiàn)了將惟一鍵映射到特定的值上。
HashMap 類沒有分類或者排序。它允許一個(gè) null 鍵和多個(gè) null 值。
Hashtable 類似于 HashMap,但是不允許 null 鍵和 null 值。它也比 HashMap 慢,因?yàn)樗峭降摹?

第六,Collection 和 Collections的區(qū)別。
Collections是個(gè)java.util下的類,它包含有各種有關(guān)集合操作的靜態(tài)方法。
Collection是個(gè)java.util下的接口,它是各種集合結(jié)構(gòu)的父接口。

第七,什么時(shí)候用assert。
斷言是一個(gè)包含布爾表達(dá)式的語句,在執(zhí)行這個(gè)語句時(shí)假定該表達(dá)式為 true。如果表達(dá)式計(jì)算為 false,那么系統(tǒng)會(huì)報(bào)告一個(gè) AssertionError。它用于調(diào)試目的:
assert(a > 0); // throws an AssertionError if a <= 0
斷言可以有兩種形式:
assert Expression1 ;
assert Expression1 : Expression2 ;
Expression1 應(yīng)該總是產(chǎn)生一個(gè)布爾值。
Expression2 可以是得出一個(gè)值的任意表達(dá)式。這個(gè)值用于生成顯示更多調(diào)試信息的 String 消息。
斷言在默認(rèn)情況下是禁用的。要在編譯時(shí)啟用斷言,需要使用 source 1.4 標(biāo)記:
javac -source 1.4 Test.java
要在運(yùn)行時(shí)啟用斷言,可使用 -enableassertions 或者 -ea 標(biāo)記。
要在運(yùn)行時(shí)選擇禁用斷言,可使用 -da 或者 -disableassertions 標(biāo)記。
要系統(tǒng)類中啟用斷言,可使用 -esa 或者 -dsa 標(biāo)記。還可以在包的基礎(chǔ)上啟用或者禁用斷言。
可以在預(yù)計(jì)正常情況下不會(huì)到達(dá)的任何位置上放置斷言。斷言可以用于驗(yàn)證傳遞給私有方法的參數(shù)。不過,斷言不應(yīng)該用于驗(yàn)證傳遞給公有方法的參數(shù),因?yàn)椴还苁欠駟⒂昧藬嘌裕蟹椒ǘ急仨殭z查其參數(shù)。不過,既可以在公有方法中,也可以在非公有方法中利用斷言測(cè)試后置條件。另外,斷言不應(yīng)該以任何方式改變程序的狀態(tài)。

第八,GC是什么? 為什么要有GC? (基礎(chǔ))。
GC是垃圾收集器。Java 程序員不用擔(dān)心內(nèi)存管理,因?yàn)槔占鲿?huì)自動(dòng)進(jìn)行管理。要請(qǐng)求垃圾收集,可以調(diào)用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()

第九,String s = new String("xyz");創(chuàng)建了幾個(gè)String Object?
兩個(gè)對(duì)象,一個(gè)是“xyx”,一個(gè)是指向“xyx”的引用對(duì)象s。

第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11;
第二十一,abstract的method是否可同時(shí)是static,是否可同時(shí)是native,是否可同時(shí)是synchronized?
都不能

第二十二,接口是否可繼承接口? 抽象類是否可實(shí)現(xiàn)(implements)接口? 抽象類是否可繼承實(shí)體類(concrete class)?
接口可以繼承接口。抽象類可以實(shí)現(xiàn)(implements)接口,抽象類是否可繼承實(shí)體類,但前提是實(shí)體類必須有明確的構(gòu)造函數(shù)。

第二十三,啟動(dòng)一個(gè)線程是用run()還是start()?
啟動(dòng)一個(gè)線程是調(diào)用start()方法,使線程所代表的虛擬處理機(jī)處于可運(yùn)行狀態(tài),這意味著它可以由JVM調(diào)度并執(zhí)行。這并不意味著線程就會(huì)立即運(yùn)行。run()方法可以產(chǎn)生必須退出的標(biāo)志來停止一個(gè)線程。

第二十四,構(gòu)造器Constructor是否可被override?
構(gòu)造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載Overloading。

第二十五,是否可以繼承String類?
String類是final類故不可以繼承。

第二十六,當(dāng)一個(gè)線程進(jìn)入一個(gè)對(duì)象的一個(gè)synchronized方法后,其它線程是否可進(jìn)入此對(duì)象的其它方法?
不能,一個(gè)對(duì)象的一個(gè)synchronized方法只能由一個(gè)線程訪問。

第二十七,try {}里有一個(gè)return語句,那么緊跟在這個(gè)try后的finally {}里的code會(huì)不會(huì)被執(zhí)行,什么時(shí)候被執(zhí)行,在return前還是后?
會(huì)執(zhí)行,在return前執(zhí)行。

第二十八,編程題: 用最有效率的方法算出2乘以8等於幾?
有C背景的程序員特別喜歡問這種問題。

2 << 3

第二十九,兩個(gè)對(duì)象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對(duì)不對(duì)?
不對(duì),有相同的hash code。

第三十,當(dāng)一個(gè)對(duì)象被當(dāng)作參數(shù)傳遞到一個(gè)方法后,此方法可改變這個(gè)對(duì)象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?
是值傳遞。Java 編程語言只由值傳遞參數(shù)。當(dāng)一個(gè)對(duì)象實(shí)例作為一個(gè)參數(shù)被傳遞到方法中時(shí),參數(shù)的值就是對(duì)該對(duì)象的引用。對(duì)象的內(nèi)容可以在被調(diào)用的方法中改變,但對(duì)象的引用是永遠(yuǎn)不會(huì)改變的。

第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
switch(expr1)中,expr1是一個(gè)整數(shù)表達(dá)式。因此傳遞給 switch 和 case 語句的參數(shù)應(yīng)該是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。

第三十二,編程題: 寫一個(gè)Singleton出來。
Singleton模式主要作用是保證在Java應(yīng)用程序中,一個(gè)類Class只有一個(gè)實(shí)例存在。
一般Singleton模式通常有幾種種形式:
第一種形式: 定義一個(gè)類,它的構(gòu)造函數(shù)為private的,它有一個(gè)static的private的該類變量,在類初始化時(shí)實(shí)例話,通過一個(gè)public的getInstance方法獲取對(duì)它的引用,繼而調(diào)用其中的方法。
public class Singleton {
  private Singleton(){}
  //在自己內(nèi)部定義自己一個(gè)實(shí)例,是不是很奇怪?
  //注意這是private 只供內(nèi)部調(diào)用
  private static Singleton instance = new Singleton();
  //這里提供了一個(gè)供外部訪問本class的靜態(tài)方法,可以直接訪問  
  public static Singleton getInstance() {
    return instance;   
   }
}
第二種形式:
public class Singleton {
  private static Singleton instance = null;
  public static synchronized Singleton getInstance() {
  //這個(gè)方法比上面有所改進(jìn),不用每次都進(jìn)行生成對(duì)象,只是第一次     
  //使用時(shí)生成實(shí)例,提高了效率!
  if (instance==null)
    instance=new Singleton();
return instance;   }
}




]]>
主站蜘蛛池模板: 九寨沟县| 鹤庆县| 四平市| 五家渠市| 玉溪市| 岗巴县| 莱州市| 武宣县| 香港 | 通化市| 呼图壁县| 深州市| 宿州市| 五大连池市| 定陶县| 平阴县| 嵊州市| 山阴县| 星座| 吴旗县| 南阳市| 常山县| 鄂托克前旗| 永和县| 六枝特区| 苍南县| 色达县| 杭锦旗| 岫岩| 年辖:市辖区| 上思县| 奇台县| 永城市| 四川省| 鸡泽县| 翁牛特旗| 会昌县| 抚州市| 红安县| 邵东县| 丰顺县|