﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-study-随笔分类-JAVA语言</title><link>http://www.blogjava.net/xixidabao/category/15384.html</link><description>GROW WITH JAVA</description><language>zh-cn</language><lastBuildDate>Sun, 27 Jan 2008 03:09:44 GMT</lastBuildDate><pubDate>Sun, 27 Jan 2008 03:09:44 GMT</pubDate><ttl>60</ttl><item><title>MessageDigest对密码进行加密</title><link>http://www.blogjava.net/xixidabao/archive/2008/01/26/177892.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 26 Jan 2008 04:16:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2008/01/26/177892.html</guid><description><![CDATA[时候，我们必须把用户密码存放到数据库，为了安全起见，我们需要对这些密码进行单向的加密处理，<br />
比如，有明文密码如下：<br />
String originalPwd = "mypassword";
<p>应用报文摘要方法，得到单向的加密字符串 </p>
<p>//MD5是16位,SHA是20位（这是两种报文摘要的算法）<br />
//MessageDigest md= MessageDigest.getInstance("MD5");<br />
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");<br />
messageDigest.update(originalPwd.getBytes());<br />
//String digestedPwdString = new String(messageDigest.digest());<br />
String digestedPwdString = new String(Base64.encode(messageDigest.digest()));<br />
System.out.println("pwd:" + digestedPwdString);<br />
这样，就得到密码的报文摘要，把此摘要保存到数据库，<br />
以后用户登陆时，用相同的算法算出摘要，和数据库中的比较，如果一致，则密码正确。 </p>
<p>注意：<br />
byte[] digest = messageDigest.digest();<br />
得到的是个二进制byte数组，有可能某些byte是不可打印的字符。<br />
所以用Base64.encode把它转化成可打印字符。 </p>
<p>也可以把digest的每个byte转化成hex（16进制）保存。<br />
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");<br />
messageDigest.update(originalPwd.getBytes());<br />
byte[] bin = messageDigest.digest()；<br />
再调用下面的方法生产hex（16进制）保存。 </p>
<p><br />
二行制转hex字符串的方法如下：<br />
private static String byte2hex(byte[] b){<br />
&nbsp;&nbsp;&nbsp; String hs="";<br />
&nbsp;&nbsp;&nbsp; String stmp="";<br />
&nbsp;&nbsp;&nbsp; for (int n=0; n&lt;b.length; n++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stmp=(java.lang.Integer.toHexString(b[n] &amp; 0xFF));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (stmp.length()==1) hs=hs+"0"+stmp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else hs=hs+stmp;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; return hs;<br />
} </p>
<p>或者：<br />
private static String byto2hex2(byte[] bin){<br />
&nbsp;&nbsp;&nbsp; StringBuffer buf = new StringBuffer();<br />
&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; bin.length; ++i) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int x = bin[i] &amp; 0xFF, h = x &gt;&gt;&gt; 4, l = x &amp; 0x0F;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append((char) (h + ((h &lt; 10) ? '0' : 'a' - 10)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append((char) (l + ((l &lt; 10) ? '0' : 'a' - 10)));<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; return buf.toString();<br />
} </p>
<p>或者:<br />
干脆直接用下面的方法生成，用到第三方包：<br />
public static String encryptPwd(String pwd, String algorithm){<br />
&nbsp;&nbsp;&nbsp; //String a = org.apache.catalina.realm.RealmBase.Digest(pwd,"SHA-1");<br />
&nbsp;&nbsp;&nbsp; return org.apache.catalina.realm.RealmBase.Digest(pwd, algorithm);<br />
}</p>
&nbsp; <br />
<br />
http://www.ibm.com/developerworks/cn/java/l-security/index.html
<img src ="http://www.blogjava.net/xixidabao/aggbug/177892.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2008-01-26 12:16 <a href="http://www.blogjava.net/xixidabao/archive/2008/01/26/177892.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java NIO API详解</title><link>http://www.blogjava.net/xixidabao/archive/2008/01/18/176208.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Fri, 18 Jan 2008 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2008/01/18/176208.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Java NIO API详解&nbsp;&nbsp;&nbsp;&nbsp; 选择自 DaiJiaLin 的 Blog &nbsp;在JDK 1.4以前，Java的IO操作集中在java.io这个包中，是基于流的同步（blocking）API。对于大多数应用来说，这样的API使用很方便，然而，一些对性能要求较高的应用，尤其是服务端应用，往往需要一个更为有效的方式来处理IO。从JDK 1.4...&nbsp;&nbsp;<a href='http://www.blogjava.net/xixidabao/archive/2008/01/18/176208.html'>阅读全文</a><img src ="http://www.blogjava.net/xixidabao/aggbug/176208.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2008-01-18 14:47 <a href="http://www.blogjava.net/xixidabao/archive/2008/01/18/176208.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>写得蛮好的linux学习笔记</title><link>http://www.blogjava.net/xixidabao/archive/2007/08/14/136537.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Mon, 13 Aug 2007 16:21:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/08/14/136537.html</guid><description><![CDATA[<div>
<div>
<div>linux<font face=宋体>目录架构</font><br>/ &nbsp; <font face=宋体>根目录</font><br>/bin&nbsp;&nbsp;&nbsp; <font face=宋体>常用的命令</font> binary file <font face=宋体>的目錄</font><br>/boot&nbsp;&nbsp; <font face=宋体>存放系统启动时必须读取的档案，包括核心</font> (kernel) <font face=宋体>在内</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /boot/grub/menu.lst&nbsp;&nbsp; GRUB<font face=宋体>设置</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /boot/vmlinuz&nbsp;&nbsp; <font face=宋体>内核</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /boot/initrd&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>核心解壓縮所需</font> RAM Disk<br>/dev&nbsp;&nbsp;&nbsp; <font face=宋体>系统周边设备</font>&nbsp;&nbsp;&nbsp;&nbsp; <br>/etc&nbsp;&nbsp;&nbsp; <font face=宋体>系统相关设定文件</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/DIR_COLORS&nbsp;&nbsp; <font face=宋体>设定颜色</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/HOSTNAME&nbsp;&nbsp; <font face=宋体>设定用户的节点名</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/NETWORKING&nbsp;&nbsp; <font face=宋体>只有</font>YES<font face=宋体>标明网络存在</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/host.conf <font face=宋体>文件说明用户的系统如何查询节点名</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/hosts <font face=宋体>设定用户自已的</font>IP<font face=宋体>与名字的对应表</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/hosts.allow <font face=宋体>设置允许使用</font>inetd<font face=宋体>的机器使用</font> <br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/hosts.deny <font face=宋体>设置不允许使用</font>inetd<font face=宋体>的机器使用</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/hosts.equiv <font face=宋体>设置远端机不用密码</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/inetd.conf <font face=宋体>设定系统网络守护进程</font>inetd<font face=宋体>的配置</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/gateways <font face=宋体>设定路由器</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/protocols <font face=宋体>设定系统支持的协议</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/named.boot <font face=宋体>设定本机为名字服务器的配置文件</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/sysconfig/network-scripts/ifcfg-eth0&nbsp;&nbsp; <font face=宋体>设置</font>IP<br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/resolv.conf&nbsp;&nbsp;&nbsp; <font face=宋体>设置</font>DNS&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/X11&nbsp; X Window<font face=宋体>的配置文件</font>,xorg.conf <font face=宋体>或</font> XF86Config <font face=宋体>這兩個</font> X Server <font face=宋体>的設定檔</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/fstab&nbsp;&nbsp;&nbsp; <font face=宋体>记录开机要</font>mount<font face=宋体>的文件系统</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/inittab <font face=宋体>设定系统启动时</font>init<font face=宋体>进程将把系统设置成什么样的</font>runlevel<br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/issue <font face=宋体>记录用户登录前显示的信息</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/group <font face=宋体>设定用户的组名与相关信息</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/passwd <font face=宋体>帐号信息</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/shadow <font face=宋体>密码信息</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/sudoers <font face=宋体>可以</font>sudo<font face=宋体>命令的配置文件</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/securetty <font face=宋体>设定哪些终端可以让</font>root<font face=宋体>登录</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/login.defs <font face=宋体>所有用户登录时的缺省配置</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/exports <font face=宋体>设定</font>NFS<font face=宋体>系统用的</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/init.d/&nbsp;&nbsp; <font face=宋体>所有服務的預設啟動</font> script <font face=宋体>都是放在這裡的，例如要啟動或者關閉</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/xinetd.d/&nbsp; <font face=宋体>這就是所謂的</font> super daemon <font face=宋体>管理的各項服務的設定檔目錄</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/modprobe.conf&nbsp;&nbsp; <font face=宋体>内核模块额外参数设定</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /etc/syslog.conf&nbsp;&nbsp; <font face=宋体>日志设置文件</font><br>/home&nbsp;&nbsp; <font face=宋体>使用者家目录</font><br>/lib&nbsp;&nbsp;&nbsp; <font face=宋体>系统会使用到的函数库</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /lib/modules&nbsp;&nbsp; kernel <font face=宋体>的相关模块</font><br>&nbsp;&nbsp;&nbsp;&nbsp; /var/lib/rpm&nbsp;&nbsp; rpm<font face=宋体>套件安装处</font> <br>/lost+found&nbsp;&nbsp;&nbsp; <font face=宋体>系統不正常產生錯誤時，會將一些遺失的片段放置於此目錄下</font><br>/mnt&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>外设的挂载点</font><br>/media&nbsp;&nbsp; <font face=宋体>与</font>/mnt<font face=宋体>类似</font><br>/opt&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>主机额外安装的软件</font><br>/proc&nbsp;&nbsp;&nbsp; <font face=宋体>虚拟目录，是内存的映射</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /proc/version&nbsp;&nbsp; <font face=宋体>内核版本</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /proc/sys/kernel&nbsp;&nbsp; <font face=宋体>系统内核功能</font><br>/root&nbsp;&nbsp;&nbsp; <font face=宋体>系统管理员的家目录</font><br>/sbin&nbsp;&nbsp;&nbsp; <font face=宋体>系统管理员才能执行的指令</font><br>/srv&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>一些服務啟動之後，這些服務所需要取用的資料目錄</font><br>/tmp&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>一般使用者或者是正在執行的程序暫時放置檔案的地方</font><br>/usr&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>最大的目录，存许应用程序和文件</font><br>&nbsp;&nbsp;&nbsp; /usr/X11R6<font face=宋体>：</font>&nbsp;&nbsp; X-Window<font face=宋体>目录</font> <br>&nbsp;&nbsp;&nbsp; /usr/src<font face=宋体>：</font>&nbsp;&nbsp;&nbsp; Linux<font face=宋体>源代码</font><br>&nbsp;&nbsp;&nbsp; /usr/include<font face=宋体>：系统头文件</font><br>&nbsp;&nbsp;&nbsp; /usr/openwin <font face=宋体>存放</font>SUN<font face=宋体>的</font>OpenWin <br>&nbsp;&nbsp;&nbsp; /usr/man <font face=宋体>在线使用手册</font><br>&nbsp;&nbsp;&nbsp; /usr/bin&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>使用者可執行的</font> binary file <font face=宋体>的目錄</font><br>&nbsp;&nbsp;&nbsp; /usr/local/bin&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>使用者可執行的</font> binary file <font face=宋体>的目錄</font><br>&nbsp;&nbsp;&nbsp; /usr/lib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>系统会使用到的函数库</font><br>&nbsp;&nbsp;&nbsp; /usr/local/lib&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>系统会使用到的函数库</font><br>&nbsp;&nbsp;&nbsp; /usr/sbin&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>系统管理员才能执行的指令</font><br>&nbsp;&nbsp;&nbsp; /usr/local/sbin&nbsp;&nbsp;&nbsp; <font face=宋体>系统管理员才能执行的指令</font><br>/var&nbsp;&nbsp; <font face=宋体>日志文件</font><br>&nbsp;&nbsp;&nbsp; /var/log/secure&nbsp;&nbsp;&nbsp; <font face=宋体>記錄登入系統存取資料的檔案，例如</font> pop3, ssh, telnet, ftp <font face=宋体>等都會記錄在此檔案中</font><br>&nbsp;&nbsp;&nbsp; /var/log/wtmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>記錄登入者的訊息資料</font>, last<br>&nbsp;&nbsp;&nbsp; /var/log/messages&nbsp; <font face=宋体>幾乎系統發生的錯誤訊息</font><br>&nbsp;&nbsp;&nbsp; /var/log/boot.log&nbsp; <font face=宋体>記錄開機或者是一些服務啟動的時候，所顯示的啟動或關閉訊息</font><br>&nbsp;&nbsp;&nbsp; /var/log/maillog&nbsp;&nbsp; <font face=宋体>紀錄郵件存取或往來</font>( sendmail <font face=宋体>與</font> pop3 )<font face=宋体>的使用者記錄</font><br>&nbsp;&nbsp;&nbsp; /var/log/cron&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>記錄</font> crontab <font face=宋体>這個例行性服務的內容</font><br>&nbsp;&nbsp;&nbsp; /var/log/httpd, /var/log/news, /var/log/mysqld.log, /var/log/samba, /var/log/procmail.log<font face=宋体>：</font><br>&nbsp;&nbsp;&nbsp; <font face=宋体>分別是幾個不同的網路服務的記錄檔</font></div>
<div>&nbsp;</div>
<div><font face=宋体>一些常用的基本命令</font>:<br>uname -a&nbsp;&nbsp;&nbsp; <font face=宋体>查看内核版本</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>ls -al&nbsp;&nbsp;&nbsp; <font face=宋体>显示所有文件的属性</font><br>pwd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示当前路径</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>cd -&nbsp;&nbsp;&nbsp; <font face=宋体>返回上一次目录</font>&nbsp;&nbsp;&nbsp;&nbsp; cd ~&nbsp;&nbsp;&nbsp; <font face=宋体>返回主目录</font><br>date s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设置时间、日期</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>cal&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示日历</font>&nbsp;&nbsp;&nbsp;&nbsp; cal 2006<br>bc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>计算器具</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>man&nbsp; &amp; info&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>帮助手册</font><br>locale&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示当前字体</font>&nbsp;&nbsp;&nbsp;&nbsp; locale -a&nbsp;&nbsp;&nbsp; <font face=宋体>所有可用字体</font>&nbsp;&nbsp;&nbsp;&nbsp; /etc/sysconfig/i18n<font face=宋体>设置文件</font><br>LANG=en&nbsp;&nbsp;&nbsp; <font face=宋体>使用英文字体</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>sync&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>将数据同步写入硬盘</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>shutdonw -h now &amp; half &amp; poweroff&nbsp; <font face=宋体>关机</font><br>reboot&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>重启</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>startx&nbsp; &amp;&nbsp; init 5&nbsp;&nbsp; <font face=宋体>进入图形介面</font><br>/work&nbsp; &amp; ?work&nbsp;&nbsp;&nbsp; <font face=宋体>向上、下查找文档内容</font><br>chgrp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>改变档案群组</font>&nbsp; chgrp testing install.log&nbsp;&nbsp;&nbsp; <br>chown&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>改变所属人</font>&nbsp;&nbsp; chown root:root install.log<br>chmod&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>改变属性</font>&nbsp;&nbsp;&nbsp;&nbsp; chmod 777 install.log&nbsp;&nbsp;&nbsp;&nbsp; read=4&nbsp; write=2&nbsp; execute=1<br>cp&nbsp;&nbsp; <font face=宋体>复制</font>&nbsp;&nbsp; cp filename<br>rm&nbsp;&nbsp; <font face=宋体>删除文件</font>&nbsp; rm -rf filename&nbsp;&nbsp; <font face=宋体>强制删除文件</font><br>rmdir&nbsp;&nbsp; <font face=宋体>删除文件夹</font><br>mv&nbsp; <font face=宋体>移动</font>&nbsp;&nbsp;&nbsp; mv 123.txt 222.txt&nbsp; <font face=宋体>重命名</font><br>mkdir&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>创建文件夹</font><br>touch&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>创建文件</font>&nbsp; <font face=宋体>更新当前时间</font><br>cat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>由第一行开始显示</font>&nbsp;&nbsp;&nbsp;&nbsp; cat |more&nbsp; <font face=宋体>分页</font><br>nl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>在内容前加行号</font><br>more&nbsp; &amp;&nbsp; less&nbsp;&nbsp; <font face=宋体>一面一面翻动</font><br>head -n filename&nbsp;&nbsp; <font face=宋体>显示第</font>N<font face=宋体>行内容</font><br>tail -n filename&nbsp; <font face=宋体>显示后</font>N<font face=宋体>行内容</font><br>od&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示非纯文档</font><br>df -h <font face=宋体>显示分区空间</font><br>du&nbsp; <font face=宋体>显示目录或文件的大小</font><br>fdisk&nbsp;&nbsp; <font face=宋体>分区设置</font>&nbsp;&nbsp;&nbsp; fdisk -l /dev/hda&nbsp; <font face=宋体>显示硬盘分区状态</font><br>mkfs&nbsp;&nbsp;&nbsp; <font face=宋体>建立各种文件系统</font>&nbsp; mkfs -t ext3&nbsp; /dev/ram15&nbsp;&nbsp; <br>fsck&nbsp;&nbsp;&nbsp; <font face=宋体>检查和修复</font>LINUX<font face=宋体>档案</font><br>ln&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>硬链接</font>&nbsp;&nbsp; ln -s&nbsp; <font face=宋体>软件链接</font><br>whereis&nbsp;&nbsp; <font face=宋体>查找命令</font><br>locate&nbsp;&nbsp;&nbsp; <font face=宋体>查找</font><br>find&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>查找</font>&nbsp;&nbsp; find / -name "***.***"<br>which&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>查看工具</font><br>whoami&nbsp;&nbsp;&nbsp; <font face=宋体>显示当前用户</font><br>gcc -v&nbsp;&nbsp;&nbsp; <font face=宋体>查看</font>GCC<font face=宋体>版本</font><br>chattr +i filename&nbsp; <font face=宋体>禁止删除</font>&nbsp;&nbsp; chattr -i filename&nbsp; <font face=宋体>取消禁止</font><br>lsattr&nbsp;&nbsp;&nbsp; <font face=宋体>显示隐藏档属性</font><br>updatedb&nbsp; <font face=宋体>更新资料库</font><br>mke2fs&nbsp;&nbsp;&nbsp; <font face=宋体>格式化</font>&nbsp;&nbsp; mkfs -t ext3 <br>dd if=/etc/passwd of=/tmp/passwd.bak&nbsp;&nbsp;&nbsp; <font face=宋体>备份</font><br>mount&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>列出系统所有的分区</font><br>mount -t iso9660 /dev/cdrom /mnt/cdrom&nbsp;&nbsp; <font face=宋体>挂载光盘</font><br>mount -t vfat /dev/fd0 /mnt/floppy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>挂载软盘</font><br>mount -t vfat -o iocharset=utf8,umask=000 /dev/hda2 /mnt/hda2&nbsp;&nbsp; <font face=宋体>挂载</font>fat32<font face=宋体>分区</font><br>mount -t ntfs -o nls=utf8,umask=000 /dev/hda3 /mnt/hda3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>挂载</font>ntfs<font face=宋体>分区</font><br>Linux-NTFS Project: <a href="http://linux-ntfs.sourceforge.net/"><font color=#223355><u>http://linux-ntfs.sourceforge.net/</u></font></a><br>umount /mnt/hda3&nbsp; <font face=宋体>缷载</font><br>ifconfig&nbsp;&nbsp; <font face=宋体>显示或设置网络设备</font><br>service network restart&nbsp;&nbsp; <font face=宋体>重启网卡</font>&nbsp; <br>ifdown eth0&nbsp; <font face=宋体>关闭网卡</font><br>ifup eth0&nbsp;&nbsp;&nbsp; <font face=宋体>开启网卡</font><br>clear&nbsp;&nbsp;&nbsp; <font face=宋体>清屏</font><br>history&nbsp;&nbsp;&nbsp; <font face=宋体>历史记录</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; !55&nbsp; <font face=宋体>执行第</font>55<font face=宋体>个指令</font><br>stty&nbsp;&nbsp; <font face=宋体>设置终端</font>&nbsp;&nbsp;&nbsp; stty -a<br>fdisk /mbr&nbsp;&nbsp; <font face=宋体>删除</font>GRUB<br>at&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>僅進行一次的工作排程</font><br>crontab&nbsp;&nbsp; <font face=宋体>循環執行的例行性命令</font>&nbsp;&nbsp;&nbsp; [e]<font face=宋体>编辑</font>,[l]<font face=宋体>显示</font>,[r]<font face=宋体>删除任务</font><br>&amp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>后台运行程序</font>&nbsp;&nbsp;&nbsp; tar -zxvf 123.tar.gz &amp; ---------&gt;<font face=宋体>后台运行</font><br>jobs&nbsp;&nbsp;&nbsp; <font face=宋体>观看后台暂停的程序</font>&nbsp;&nbsp; jobs -l<br>fg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>将后台程序调到前台</font>&nbsp;&nbsp; fg n ------&gt;n<font face=宋体>是数字</font>,<font face=宋体>可以指定进行那个程序</font><br>bg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>让工作在后台运行</font><br>kill&nbsp;&nbsp;&nbsp; <font face=宋体>结束进程</font>&nbsp;&nbsp;&nbsp; kill -9 PID&nbsp;&nbsp;&nbsp;&nbsp; [9]<font face=宋体>强制结束</font>,[15]<font face=宋体>正常结束</font>,[l]<font face=宋体>列出可用的</font>kill<font face=宋体>信号</font><br>ps aux&nbsp; <font face=宋体>查看后台程序</font>&nbsp;&nbsp; <br>top&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>查看后台程序</font>&nbsp;&nbsp; top -d 2&nbsp;&nbsp;&nbsp; <font face=宋体>每两秒更新一次</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; top -d 2 -p10604&nbsp;&nbsp; <font face=宋体>观看某个</font>PID<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; top -b -n 2 &gt; /tmp/top.txt -----&gt;<font face=宋体>將</font> top <font face=宋体>的資訊進行</font> 2 <font face=宋体>次，然後將結果輸出到</font> /tmp/top.txt&nbsp;&nbsp;&nbsp; <br>pstree&nbsp;&nbsp; <font face=宋体>以树状图显示程序</font>&nbsp;&nbsp;&nbsp; [A]<font face=宋体>以</font> ASCII <font face=宋体>來連接</font>, [u]<font face=宋体>列出</font>PID, [p]<font face=宋体>列出帐号</font><br>killall&nbsp;&nbsp; <font face=宋体>要刪除某個服務</font>&nbsp;&nbsp;&nbsp; killall -9 httpd<br>free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示内存状态</font>&nbsp;&nbsp;&nbsp;&nbsp; free -m&nbsp; --------&gt;<font face=宋体>以</font>M<font face=宋体>为单位显示</font><br>uptime&nbsp;&nbsp;&nbsp; <font face=宋体>显示目前系统开机时间</font><br>netstat&nbsp;&nbsp; <font face=宋体>显示网络状态</font>&nbsp;&nbsp;&nbsp; netstat -tulnp------&gt;<font face=宋体>找出目前系統上已在監聽的網路連線及其</font> PID<br>dmesg&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示开机信息</font>&nbsp;&nbsp;&nbsp; demsg | more<br>nice&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设置优先权</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nice -n -5 vi &amp; -----&gt;<font face=宋体>用</font> root <font face=宋体>給一個</font> nice <font face=宋体>植為</font> -5 <font face=宋体>，用於執行</font> vi <br>renice&nbsp;&nbsp;&nbsp; <font face=宋体>调整已存在优先权</font><br>runlevel&nbsp; <font face=宋体>显示目前的</font>runlevel<br>depmod&nbsp;&nbsp;&nbsp; <font face=宋体>分析可载入模块的相依性</font><br>lsmod&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示已载入系统的模块</font><br>modinfo&nbsp;&nbsp; <font face=宋体>显示</font>kernel<font face=宋体>模块的信息</font><br>insmod&nbsp;&nbsp;&nbsp; <font face=宋体>载入模块</font><br>modprobe&nbsp;&nbsp; <font face=宋体>自动处理可载入模块</font><br>rmmod&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>删除模块</font><br>chkconfig&nbsp;&nbsp; <font face=宋体>检查，设置系统的各种服务</font>&nbsp;&nbsp;&nbsp;&nbsp; chkconfig --list -----&gt;<font face=宋体>列出各项服务状态</font><br>ntsysv&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设置系统的各种服务</font><br>cpio&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>备份文件</font><br>&nbsp;</div>
<div><br><font face=宋体>压缩命令：</font><br>&nbsp;*.Z&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; compress <font face=宋体>程式壓縮的檔案；</font> <br>&nbsp;*.bz2&nbsp;&nbsp;&nbsp; bzip2 <font face=宋体>程式壓縮的檔案；</font> <br>&nbsp;*.gz&nbsp;&nbsp;&nbsp;&nbsp; gzip <font face=宋体>程式壓縮的檔案；</font> <br>&nbsp;*.tar&nbsp;&nbsp;&nbsp; tar <font face=宋体>程式打包的資料，並沒有壓縮過；</font> <br>&nbsp;*.tar.gz tar <font face=宋体>程式打包的檔案，其中並且經過</font> gzip <font face=宋体>的壓縮</font><br>compress filename&nbsp; <font face=宋体>压缩文件</font>&nbsp; <font face=宋体>加</font>[-d]<font face=宋体>解压</font>&nbsp; uncompress<br>gzip filename&nbsp;&nbsp; <font face=宋体>压缩</font>&nbsp; <font face=宋体>加</font>[-d]<font face=宋体>解压</font>&nbsp; zcat 123.gz <font face=宋体>查看压缩文件内容</font><br>bzip2 -z filename&nbsp; <font face=宋体>压缩</font>&nbsp; <font face=宋体>加</font>[-d]<font face=宋体>解压</font>&nbsp;&nbsp; bzcat filename.bz2&nbsp; <font face=宋体>查看压缩文件内容</font><br>tar -cvf /home/123.tar /etc&nbsp; <font face=宋体>打包，不压缩</font><br>tar -xvf 123.tar&nbsp;&nbsp; <font face=宋体>解开包</font><br>tar -zxvf /home/123.tar.gz&nbsp; <font face=宋体>以</font>gzip<font face=宋体>解压</font><br>tar -jxvf /home/123.tar.bz2&nbsp; <font face=宋体>以</font>bzip2<font face=宋体>解压</font><br>tar -ztvf /tmp/etc.tar.gz&nbsp;&nbsp; <font face=宋体>查看</font>tar<font face=宋体>内容</font><br>cpio -covB&nbsp; &gt; [file|device]&nbsp;&nbsp; <font face=宋体>份份</font><br>cpio -icduv &lt; [file|device]&nbsp;&nbsp; <font face=宋体>还原</font></div>
<div>&nbsp;</div>
<div>vi<font face=宋体>一般用法</font><br><font face=宋体>一般模式</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>编辑模式</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>指令模式</font><br>h <font face=宋体>左</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a,i,r,o,A,I,R,O&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :w <font face=宋体>保存</font><br>j <font face=宋体>下</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>进入编辑模式</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :w! <font face=宋体>强制保存</font><br>k <font face=宋体>上</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dd <font face=宋体>删除光标当前行</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :q! <font face=宋体>不保存离开</font><br>l <font face=宋体>右</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ndd <font face=宋体>删除</font>n<font face=宋体>行</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :wq! <font face=宋体>保存后离开</font><br>0 <font face=宋体>移动到行首</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yy <font face=宋体>复制当前行</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :e! <font face=宋体>还原原始档</font><br>$ <font face=宋体>移动到行尾</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nyy <font face=宋体>复制</font>n<font face=宋体>行</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :w filename <font face=宋体>另存为</font><br>H <font face=宋体>屏幕最上</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p,P <font face=宋体>粘贴</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :set nu <font face=宋体>设置行号</font><br>M <font face=宋体>屏幕中央</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u&nbsp; <font face=宋体>撤消</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :set nonu <font face=宋体>取消行号</font><br>L <font face=宋体>屏幕最下</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Ctrl]+r <font face=宋体>重做上一个动作</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ZZ <font face=宋体>保存离开</font><br>G <font face=宋体>档案最后一行</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ctrl]+z <font face=宋体>暂停退出</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :set nohlsearch&nbsp;&nbsp; <font face=宋体>永久地关闭高亮显示</font><br>/work <font face=宋体>向下搜索</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :sp <font face=宋体>同时打开两个文档</font> <br>?work <font face=宋体>向上搜索</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Ctrl]+w <font face=宋体>两个文档设换</font><br>gg <font face=宋体>移动到档案第一行</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :nohlsearch&nbsp;&nbsp;&nbsp; <font face=宋体>暂时关闭高亮显示</font></div>
<div>&nbsp;</div>
<div><font face=宋体>认识</font>SHELL<br>alias&nbsp;&nbsp;&nbsp; <font face=宋体>显示当前所有的命令别名</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alias lm="ls -al"&nbsp;&nbsp; <font face=宋体>命令别名</font>&nbsp;&nbsp;&nbsp; unalias lm <font face=宋体>取消命令别名</font><br>type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>类似</font>which<br>exprot&nbsp;&nbsp;&nbsp; <font face=宋体>设置或显示环境变量</font><br>exprot PATH="$PATH":/sbin&nbsp; <font face=宋体>添加</font>/sbin<font face=宋体>入</font>PATH<font face=宋体>路径</font><br>echo $PATH&nbsp;&nbsp;&nbsp; <font face=宋体>显示</font>PATH<font face=宋体>路径</font><br>bash&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>进入子程序</font><br>name=yang&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设定变量</font><br>unset name&nbsp;&nbsp;&nbsp; <font face=宋体>取消变量</font><br>echo $name&nbsp;&nbsp;&nbsp; <font face=宋体>显示变量的内容</font><br>myname="$name its me"&nbsp;&nbsp; &amp;&nbsp;&nbsp; myname='$name its me'&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>单引号时</font>$name<font face=宋体>失去变量内容</font><br>ciw=/etc/sysconfig/network-scripts/&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设置路径</font><br>env&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>列出所有环境变量</font><br>echo $RANDOM&nbsp;&nbsp;&nbsp; <font face=宋体>显示随意产生的数</font><br>set&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设置</font>SHELL<br>PS1='[\u@\h \w \A #\#]\$ '&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>提示字元的設定</font><br>&nbsp;&nbsp; [root@linux ~]# read [-pt] variable&nbsp;&nbsp;&nbsp;&nbsp; -----------<font face=宋体>读取键盘输入的变量</font><br>&nbsp;&nbsp; <font face=宋体>參數：</font><br>&nbsp;&nbsp; -p&nbsp; <font face=宋体>：後面可以接提示字元！</font><br>&nbsp;&nbsp; -t&nbsp; <font face=宋体>：後面可以接等待的『秒數！』</font><br>declare&nbsp;&nbsp;&nbsp; <font face=宋体>声明</font> shell <font face=宋体>变量</font><br>ulimit -a&nbsp;&nbsp; <font face=宋体>显示所有限制资料</font><br>&nbsp;ls /tmp/yang &amp;&amp; echo "exist" || echo "not exist"<br>&nbsp;<font face=宋体>意思是說，當</font> ls /tmp/yang <font face=宋体>執行後，若正確，就執行</font>echo "exist" ,<font face=宋体>若有問題，就執行</font>echo "not exist" <br>&nbsp;echo $PATH | cut -d ':' -f 5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>以</font>:<font face=宋体>为分隔符</font>,<font face=宋体>读取第</font>5<font face=宋体>段内容</font><br>&nbsp;export | cut -c 10-20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>读取第</font>10<font face=宋体>到</font>20<font face=宋体>个字节的内容</font><br>&nbsp;last | grep 'root'&nbsp;&nbsp;&nbsp; <font face=宋体>搜索有</font>root<font face=宋体>的一行</font>,<font face=宋体>加</font>[-v]<font face=宋体>反向搜索</font><br>&nbsp;cat /etc/passwd | sort&nbsp;&nbsp;&nbsp; <font face=宋体>排序显示</font><br>&nbsp;cat /etc/passwd | wc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示『行、字数、字节数』</font></div>
<div><font face=宋体>正规表示法</font><br>[root@test root]# grep [-acinv] '<font face=宋体>搜尋字串</font>' filename<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>參數說明：</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -a <font face=宋体>：將</font> binary <font face=宋体>檔案以</font> text <font face=宋体>檔案的方式搜尋資料</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -c <font face=宋体>：計算找到</font> '<font face=宋体>搜尋字串</font>' <font face=宋体>的次數</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -i <font face=宋体>：忽略大小寫的不同，所以大小寫視為相同</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -n <font face=宋体>：順便輸出行號</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -v <font face=宋体>：反向選擇，亦即顯示出沒有</font> '<font face=宋体>搜尋字串</font>' <font face=宋体>內容的那一行！</font><br>&nbsp;grep -n 'the' 123.txt&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>搜索</font>the<font face=宋体>字符</font> -----------<font face=宋体>搜尋特定字串</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;grep -n 't[ea]st' 123.txt&nbsp;&nbsp;&nbsp; <font face=宋体>搜索</font>test<font face=宋体>或</font>taste<font face=宋体>两个字符</font>---------<font face=宋体>利用</font> [] <font face=宋体>來搜尋集合字元</font><br>&nbsp;grep -n '[^g]oo' 123.txt&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>搜索前面不为</font>g<font face=宋体>的</font>oo-----------<font face=宋体>向選擇</font> [^] <br>&nbsp;grep -n '[0-9]' 123.txt&nbsp; <font face=宋体>搜索有</font>0-9<font face=宋体>的数字</font><br>&nbsp;grep -n '^the' 123.txt <font face=宋体>搜索以</font>the<font face=宋体>为行首</font>-----------<font face=宋体>行首搜索</font>^<br>&nbsp;grep -n '^[^a-zA-Z]' 123.txt&nbsp; <font face=宋体>搜索不以英文字母开头</font><br>&nbsp;grep -n '[a-z]$' 123.txt&nbsp;&nbsp;&nbsp; <font face=宋体>搜索以</font>a-z<font face=宋体>结尾的行</font>---------- <font face=宋体>行尾搜索</font>$<br>&nbsp;grep -n 'g..d' 123.txt&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>搜索开头</font>g<font face=宋体>结尾</font>d<font face=宋体>字符</font>----------<font face=宋体>任意一個字元</font> . <br>&nbsp;grep -n 'ooo*' 123.txt&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>搜索至少有两个</font>oo<font face=宋体>的字符</font>---------<font face=宋体>重複字元</font> *<br>sed&nbsp;&nbsp;&nbsp; <font face=宋体>文本流编辑器</font>&nbsp;&nbsp;&nbsp; <font face=宋体>利用脚本命令来处理文本文件</font><br>awd&nbsp;&nbsp;&nbsp; <font face=宋体>模式扫描和处理语言</font><br>&nbsp;nl 123.txt | sed '2,5d'&nbsp;&nbsp; <font face=宋体>删除第二到第五行的内容</font><br>diff&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>比较文件的差异</font><br>cmp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>比较两个文件是否有差异</font><br>patch&nbsp;&nbsp;&nbsp; <font face=宋体>修补文件</font><br>pr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>要打印的文件格式化</font><br>&nbsp;</div>
<div><br><font face=宋体>帐号管理</font><br>/etc/passwd&nbsp;&nbsp;&nbsp; <font face=宋体>系统帐号信息</font><br>/etc/shadow&nbsp;&nbsp;&nbsp; <font face=宋体>帐号密码信息</font>&nbsp;&nbsp;&nbsp; <font face=宋体>经</font>MD5 32<font face=宋体>位加密</font><br>&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>在密码栏前面加『</font> * <font face=宋体>』『</font> ! <font face=宋体>』禁止使用某帐号</font><br>/etc/group&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>系统群组信息</font><br>/etc/gshadow<br>newgrp&nbsp;&nbsp;&nbsp; <font face=宋体>改变登陆组</font><br>useradd&nbsp; &amp;&nbsp; adduser&nbsp;&nbsp;&nbsp; <font face=宋体>建立新用户</font>&nbsp; ---------&gt; useradd -m test&nbsp; <font face=宋体>自动建立用户的登入目录</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; useradd -m -g pgroup test ---------&gt;<font face=宋体>指定所属级</font><br>/etc/default/useradd&nbsp;&nbsp; <font face=宋体>相关设定</font><br>/etc/login.defs&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UID/GID <font face=宋体>有關的設定</font><br>passwd&nbsp;&nbsp;&nbsp; <font face=宋体>更改密码</font> -----------&gt; passwd test<br>usermod&nbsp;&nbsp; <font face=宋体>修改用户帐号</font><br>userdel&nbsp;&nbsp; <font face=宋体>删除帐号</font> -----------&gt;userdel -r test<br>chsh&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>更换登陆系统时使用的</font>SHELL&nbsp;&nbsp; [-l]<font face=宋体>显示可用的</font>SHELL;[-s]<font face=宋体>修改自己的</font>SHELL<br>chfn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>改变</font>finger<font face=宋体>指令显示的信息</font><br>finger&nbsp;&nbsp;&nbsp; <font face=宋体>查找并显示用户信息</font><br>id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示用户的</font>ID -----------&gt;&nbsp; id test<br>groupadd&nbsp;&nbsp; <font face=宋体>添加组</font><br>groupmod&nbsp;&nbsp; <font face=宋体>与</font>usermod<font face=宋体>类似</font><br>groupdel&nbsp;&nbsp; <font face=宋体>删除组</font><br>su test&nbsp;&nbsp;&nbsp; <font face=宋体>更改用户</font>&nbsp;&nbsp; su -&nbsp;&nbsp;&nbsp; <font face=宋体>进入</font>root,<font face=宋体>且使用</font>root<font face=宋体>的环境变量</font><br>sudo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>以其他身份来执行指令</font><br>visudo&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>编辑</font>/etc/sudoers&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>加入一行『</font> test ALL=(ALL) ALL <font face=宋体>』</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %wheel ALL = (ALL) ALL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>系统里所有</font>wheel<font face=宋体>群组的用户都可用</font>sudo<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %wheel ALL = (ALL) NOPASSWD: ALL&nbsp;&nbsp;&nbsp;&nbsp; wheel<font face=宋体>群组所有用户都不用密码</font>NOPASSWD<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; User_Alias ADMPW = vbird, dmtsai, vbird1, vbird3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>加入</font>ADMPW<font face=宋体>组</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ADMPW ALL = NOPASSWD: !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, \<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; !/usr/bin/passwd root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>可以更改使用者密码</font>,<font face=宋体>但不能更改</font>root<font face=宋体>密码</font> (<font face=宋体>在指令前面加入</font> ! <font face=宋体>代表不可</font>)<br>PAM (Pluggable Authentication Modules, <font face=宋体>嵌入式模組</font>)<br>who &amp; w&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>看谁在线</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>last&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>最近登陆主机的信息</font><br>lastlog&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>最近登入的時間</font>&nbsp;&nbsp;&nbsp; <font face=宋体>读取</font> /var/log/lastlog <br>talk&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>与其他用户交谈</font><br>write&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>发送信息</font>&nbsp;&nbsp;&nbsp; write test&nbsp;&nbsp; [ctrl]+d <font face=宋体>发送</font><br>mesg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设置终端机的写入权限</font>&nbsp;&nbsp;&nbsp; mesg n <font face=宋体>禁止接收</font>&nbsp;&nbsp;&nbsp;&nbsp; mesg y <br>wall&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>向所有用户发送信息</font>&nbsp;&nbsp;&nbsp; wall this is q test<br>mail&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>写</font>mail&nbsp;&nbsp; <br>/etc/default/useradd&nbsp;&nbsp;&nbsp; <font face=宋体>家目录默认设置</font></div>
<div>quota&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>显示磁盘已使用的空间与限制</font>&nbsp;&nbsp;&nbsp;&nbsp; quota -guvs -----&gt;<font face=宋体>秀出目前</font> root <font face=宋体>自己的</font> quota <font face=宋体>限制值</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; quota -vu&nbsp;&nbsp; <font face=宋体>查询</font><br>quotacheck&nbsp;&nbsp; <font face=宋体>检查磁盘的使用空间与限制</font>&nbsp;&nbsp;&nbsp;&nbsp; quotacheck -avug&nbsp; -----&gt;<font face=宋体>將所有的在</font> /etc/mtab <font face=宋体>內，含有</font> quota <font face=宋体>支援的</font> partition <font face=宋体>進行掃瞄</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [-m] <font face=宋体>强制扫描</font>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp; quota<font face=宋体>一定要是独立的分区</font>,<font face=宋体>要有</font>quota.user<font face=宋体>和</font>quota.group<font face=宋体>两件文件</font>,<font face=宋体>在</font>/etc/fstab<font face=宋体>添加一句</font>:<br>&nbsp;&nbsp;&nbsp;&nbsp; /dev/hda3 /home ext3 defaults,usrquota,grpquota 1 2<br>&nbsp;&nbsp;&nbsp;&nbsp; chmod 600 quota*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>设置完成</font>,<font face=宋体>重启生效</font><br>edquota&nbsp;&nbsp;&nbsp; <font face=宋体>编辑用户或群组的</font>quota&nbsp; [u]<font face=宋体>用户</font>,[g]<font face=宋体>群组</font>,[p]<font face=宋体>复制</font>,[t]<font face=宋体>设置宽限期限</font> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edquota -a yang&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edquota -p yang -u young -----&gt;<font face=宋体>复制</font>&nbsp;&nbsp;&nbsp; <br>quotaon&nbsp;&nbsp;&nbsp; <font face=宋体>开启磁盘空间限制</font>&nbsp;&nbsp;&nbsp;&nbsp; quotaon -auvg --------&gt;<font face=宋体>啟動所有的具有</font> quota <font face=宋体>的</font> filesystem<br>quotaoff&nbsp;&nbsp; <font face=宋体>关闭磁盘空间限制</font>&nbsp;&nbsp;&nbsp;&nbsp; quotaoff -a&nbsp; --------&gt;<font face=宋体>關閉了</font> quota <font face=宋体>的限制</font><br>repquota -av&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>查閱系統內所有的具有</font> quota <font face=宋体>的</font> filesystem <font face=宋体>的限值狀態</font></div>
<div>Quota <font face=宋体>從開始準備</font> filesystem <font face=宋体>的支援到整個設定結束的主要的步驟大概是：</font><br>1<font face=宋体>、設定</font> partition <font face=宋体>的</font> filesystem <font face=宋体>支援</font> quota <font face=宋体>參數：</font><br><font face=宋体>由於</font> quota <font face=宋体>必須要讓</font> partition <font face=宋体>上面的</font> filesystem <font face=宋体>支援才行，一般來說，</font> <font face=宋体>支援度最好的是</font> ext2/ext3 <font face=宋体>，</font><br><font face=宋体>其他的</font> filesystem <font face=宋体>類型鳥哥我是沒有試過啦！</font> <font face=宋体>啟動</font> filesystem <font face=宋体>支援</font> quota <font face=宋体>最簡單就是編輯</font> /etc/fstab <font face=宋体>，</font><br><font face=宋体>使得準備要開放的</font> quota <font face=宋体>磁碟可以支援</font> quota <font face=宋体>囉；</font><br>2<font face=宋体>、建立</font> quota <font face=宋体>記錄檔：</font><br><font face=宋体>剛剛前面講過，整個</font> quota <font face=宋体>進行磁碟限制值記錄的檔案是</font> aquota.user/aquota.group<font face=宋体>，</font> <br><font face=宋体>要建立這兩個檔案就必須要先利用</font> quotacheck <font face=宋体>掃瞄才行喔！</font><br>3<font face=宋体>、編輯</font> quota <font face=宋体>限制值資料：</font><br><font face=宋体>再來就是使用</font> edquota <font face=宋体>來編輯每個使用者或群組的可使用空間囉；</font><br>4<font face=宋体>、重新掃瞄與啟動</font> quota <font face=宋体>：</font><br><font face=宋体>設定好</font> quota <font face=宋体>之後，建議可以再進行一次</font> quotacheck <font face=宋体>，然後再以</font> quotaon <font face=宋体>來啟動吧！</font></div>
<div><br><font face=宋体>开机流程简介</font><br>1<font face=宋体>、載入</font> BIOS <font face=宋体>的硬體資訊，並取得第一個開機裝置的代號；</font> <br>2<font face=宋体>、讀取第一個開機裝置的</font> MBR <font face=宋体>的</font> boot Loader (<font face=宋体>亦即是</font> lilo, grub, spfdisk <font face=宋体>等等</font>) <font face=宋体>的開機資訊；</font> <br>3<font face=宋体>、載入</font> Kernel <font face=宋体>作業系統核心資訊，</font> Kernel <font face=宋体>開始解壓縮，並且嘗試驅動所有硬體裝置；</font> <br>4<font face=宋体>、</font>Kernel <font face=宋体>執行</font> init <font face=宋体>程式並取得</font> run-level <font face=宋体>資訊；</font> <br>5<font face=宋体>、</font>init <font face=宋体>執行</font> /etc/rc.d/rc.sysinit <font face=宋体>檔案；</font> <br>6<font face=宋体>、啟動核心的外掛模組</font> (/etc/modprobe.conf)<font face=宋体>；</font> <br>7<font face=宋体>、</font>init <font face=宋体>執行</font> run-level <font face=宋体>的各個批次檔</font>( Scripts )<font face=宋体>；</font> <br>8<font face=宋体>、</font>init <font face=宋体>執行</font> /etc/rc.d/rc.local <font face=宋体>檔案；</font> <br>9<font face=宋体>、執行</font> /bin/login <font face=宋体>程式，並等待使用者登入；</font> <br>10<font face=宋体>、登入之後開始以</font> Shell <font face=宋体>控管主機。</font> </div>
<div><font face=宋体>在</font>/etc/rc.d/rc3.d<font face=宋体>內</font>,<font face=宋体>以</font>S<font face=宋体>开头的为开机启动</font>,<font face=宋体>以</font>K<font face=宋体>开头的为关闭</font>,<font face=宋体>接着的数字代表执行顺序</font></div>
<div>GRUB vga<font face=宋体>设定</font><br><font face=宋体>彩度</font>\<font face=宋体>解析度</font>&nbsp; 640x480&nbsp; 800x600&nbsp; 1024x768&nbsp; 1280x1024&nbsp;&nbsp; bit <br>&nbsp;&nbsp;&nbsp; 256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 769&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 771&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 773&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 775&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8 bit <br>&nbsp;&nbsp; 32768&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 784&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 787&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 790&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 793&nbsp;&nbsp;&nbsp;&nbsp; 15 bit <br>&nbsp;&nbsp; 65536&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 785&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 788&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 791&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 794&nbsp;&nbsp;&nbsp;&nbsp; 16 bit <br>&nbsp;&nbsp; 16.8M&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 786&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 789&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 792&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 795&nbsp;&nbsp;&nbsp;&nbsp; 32 bit </div>
<div><br>./configure&nbsp;&nbsp;&nbsp; <font face=宋体>检查系统信息</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ./configure --help | more&nbsp; <font face=宋体>帮助信息</font><br>make clean&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>清除之前留下的文件</font><br>make&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face=宋体>编译</font><br>make install&nbsp;&nbsp; <font face=宋体>安装</font></div>
<div>rpm -q&nbsp; -----&gt;<font face=宋体>查询是否安装</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpm -ql ------&gt;<font face=宋体>查询该套件所有的目录</font><br>rpm -qi -----&gt;<font face=宋体>查询套件的说明资料</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpm -qc[d] -----&gt;<font face=宋体>设定档与说明档</font><br>rpm -ivh&nbsp; ----&gt;<font face=宋体>安装</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpm -V&nbsp; --------&gt;<font face=宋体>查看套件有否更动过</font><br>rpm -e&nbsp; ------&gt;<font face=宋体>删除</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpm -Uvh -------&gt;<font face=宋体>升级安装</font>&nbsp; <br>--nodeps -----&gt;<font face=宋体>强行安装</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --test -----&gt;<font face=宋体>测试安装</font></div>
</div>
<div><font size=3><font face="Times New Roman"></font></font>转&nbsp;<a href="http://blog.chinaunix.net/u/30619/showart.php?id=249558">http://blog.chinaunix.net/u/30619/showart.php?id=249558</a></div>
<div></div>
</div>
<img src ="http://www.blogjava.net/xixidabao/aggbug/136537.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-08-14 00:21 <a href="http://www.blogjava.net/xixidabao/archive/2007/08/14/136537.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java面试笔试题大汇总 ~很全面</title><link>http://www.blogjava.net/xixidabao/archive/2007/06/01/121279.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Thu, 31 May 2007 18:56:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/06/01/121279.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: java面试笔试题大汇总&nbsp;java面试笔试题大汇总&nbsp;&nbsp;第一，谈谈final, finally, finalize的区别。 　　最常被问到。　　 　　第二，Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类，是否可以implements(实现)interface(接口)?　　 　　第三，Static Nest...&nbsp;&nbsp;<a href='http://www.blogjava.net/xixidabao/archive/2007/06/01/121279.html'>阅读全文</a><img src ="http://www.blogjava.net/xixidabao/aggbug/121279.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-06-01 02:56 <a href="http://www.blogjava.net/xixidabao/archive/2007/06/01/121279.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网上搜集的java问题</title><link>http://www.blogjava.net/xixidabao/archive/2007/04/14/110597.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 14 Apr 2007 02:13:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/14/110597.html</guid><description><![CDATA[<p>1，看看API，在Integer类里面有转成 16进制，8进制和2进制的函数 16进制 Integer.toHexString(int i) 8进制 Integer.toOctalString(int i) 2进制 Integer.toBinaryString(int i) </p>
<p>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); </p>
<p>2，java 如何实现程序的自动更新，有例子最好了</p>
<p>做一个线程 过一段时间 就连接指定的远程服务器 看最新版本号 与本地当前版本号是不是一致 是的话 就弹出窗口 提示用户 用户确认就 自动下载下来 然后更新原来的class 再启动 过程就是这样 自己写一个小小的代码测试一下就可以 </p>
<img src ="http://www.blogjava.net/xixidabao/aggbug/110597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-14 10:13 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/14/110597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内存管理内幕</title><link>http://www.blogjava.net/xixidabao/archive/2007/04/03/108120.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Tue, 03 Apr 2007 01:33:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/03/108120.html</guid><description><![CDATA[<p><a href="http://www.ibm.com/developerworks/cn/linux/l-memory/#author">Jonathan Bartlett</a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#106;&#111;&#104;&#110;&#110;&#121;&#98;&#64;&#101;&#115;&#107;&#105;&#109;&#111;&#46;&#99;&#111;&#109;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#20869;&#23384;&#31649;&#29702;&#20869;&#24149;">johnnyb@eskimo.com</a>), 技术总监, New Media Worx<br></p>
<p>2004 年 11 月 29 日</p>
<blockquote>本文将对 Linux&#8482; 程序员可以使用的内存管理技术进行概述，虽然关注的重点是 C 语言，但同样也适用于其他语言。文中将为您提供如何管理内存的细节，然后将进一步展示如何手工管理内存，如何使用引用计数或者内存池来半手工地管理内存，以及如何使用垃圾收集自动管理内存。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name=N10046><span class=atitle>为什么必须管理内存</span></a></p>
<p>内存管理是计算机编程最为基本的领域之一。在很多脚本语言中，您不必担心内存是如何管理的，这并不能使得内存管理的重要性有一点点降低。对实际编程来说，理解您的内存管理器的能力与局限性至关重要。在大部分系统语言中，比如 C 和 C++，您必须进行内存管理。本文将介绍手工的、半手工的以及自动的内存管理实践的基本概念。 </p>
<p>追溯到在 Apple II 上进行汇编语言编程的时代，那时内存管理还不是个大问题。您实际上在运行整个系统。系统有多少内存，您就有多少内存。您甚至不必费心思去弄明白它有多少内存，因为每一台机器的内存数量都相同。所以，如果内存需要非常固定，那么您只需要选择一个内存范围并使用它即可。 </p>
<p>不过，即使是在这样一个简单的计算机中，您也会有问题，尤其是当您不知道程序的每个部分将需要多少内存时。如果您的空间有限，而内存需求是变化的，那么您需要一些方法来满足这些需求：
<ul>
    <li>确定您是否有足够的内存来处理数据。
    <li>从可用的内存中获取一部分内存。
    <li>向可用内存池（pool）中返回部分内存，以使其可以由程序的其他部分或者其他程序使用。 </li>
</ul>
<p>&nbsp;</p>
<p>实现这些需求的程序库称为 <em>分配程序（allocators）</em>，因为它们负责分配和回收内存。程序的动态性越强，内存管理就越重要，您的内存分配程序的选择也就更重要。让我们来了解可用于内存管理的不同方法，它们的好处与不足，以及它们最适用的情形。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-memory/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10068><span class=atitle>C 风格的内存分配程序</span></a></p>
<p>C 编程语言提供了两个函数来满足我们的三个需求：
<ul>
    <li><strong>malloc：</strong>该函数分配给定的字节数，并返回一个指向它们的指针。如果没有足够的可用内存，那么它返回一个空指针。
    <li><strong>free：</strong>该函数获得指向由 <code>malloc</code> 分配的内存片段的指针，并将其释放，以便以后的程序或操作系统使用（实际上，一些 <code>malloc</code> 实现只能将内存归还给程序，而无法将内存归还给操作系统）。 </li>
</ul>
<p>&nbsp;</p>
<p><a name=N10089><span class=smalltitle>物理内存和虚拟内存</span></a></p>
<p>要理解内存在程序中是如何分配的，首先需要理解如何将内存从操作系统分配给程序。计算机上的每一个进程都认为自己可以访问所有的物理内存。显然，由于同时在运行多个程序，所以每个进程不可能拥有全部内存。实际上，这些进程使用的是 <em>虚拟内存</em>。 </p>
<p>只是作为一个例子，让我们假定您的程序正在访问地址为 629 的内存。不过，虚拟内存系统不需要将其存储在位置为 629 的 RAM 中。实际上，它甚至可以不在 RAM 中 —— 如果物理 RAM 已经满了，它甚至可能已经被转移到硬盘上！由于这类地址不必反映内存所在的物理位置，所以它们被称为虚拟内存。操作系统维持着一个虚拟地址到物理地址的转换的表，以便计算机硬件可以正确地响应地址请求。并且，如果地址在硬盘上而不是在 RAM 中，那么操作系统将暂时停止您的进程，将其他内存转存到硬盘中，从硬盘上加载被请求的内存，然后再重新启动您的进程。这样，每个进程都获得了自己可以使用的地址空间，可以访问比您物理上安装的内存更多的内存。 </p>
<p>在 32-位 x86 系统上，每一个进程可以访问 4 GB 内存。现在，大部分人的系统上并没有 4 GB 内存，即使您将 swap 也算上， <em>每个进程</em>所使用的内存也肯定少于 4 GB。因此，当加载一个进程时，它会得到一个取决于某个称为 <em>系统中断点（system break）</em>的特定地址的初始内存分配。该地址之后是未被映射的内存 —— 用于在 RAM 或者硬盘中没有分配相应物理位置的内存。因此，如果一个进程运行超出了它初始分配的内存，那么它必须请求操作系统&#8220;映射进来（map in）&#8221;更多的内存。（映射是一个表示一一对应关系的数学术语 —— 当内存的虚拟地址有一个对应的物理地址来存储内存内容时，该内存将被映射。） </p>
<p>基于 UNIX 的系统有两个可映射到附加内存中的基本系统调用：
<ul>
    <li><strong>brk：</strong> <code>brk()</code> 是一个非常简单的系统调用。还记得系统中断点吗？该位置是进程映射的内存边界。 <code>brk()</code> 只是简单地将这个位置向前或者向后移动，就可以向进程添加内存或者从进程取走内存。
    <li><strong>mmap：</strong> <code>mmap()</code>，或者说是&#8220;内存映像&#8221;，类似于 <code>brk()</code>，但是更为灵活。首先，它可以映射任何位置的内存，而不单单只局限于进程。其次，它不仅可以将虚拟地址映射到物理的 RAM 或者 swap，它还可以将它们映射到文件和文件位置，这样，读写内存将对文件中的数据进行读写。不过，在这里，我们只关心 <code>mmap</code> 向进程添加被映射的内存的能力。 <code>munmap()</code> 所做的事情与 <code>mmap()</code> 相反。 </li>
</ul>
<p>&nbsp;</p>
<p>如您所见， <code>brk()</code> 或者 <code>mmap()</code> 都可以用来向我们的进程添加额外的虚拟内存。在我们的例子中将使用 <code>brk()</code>，因为它更简单，更通用。 </p>
<p><a name=N100DE><span class=smalltitle>实现一个简单的分配程序</span></a></p>
<p>如果您曾经编写过很多 C 程序，那么您可能曾多次使用过 <code>malloc()</code> 和 <code>free()</code>。不过，您可能没有用一些时间去思考它们在您的操作系统中是如何实现的。本节将向您展示 <code>malloc</code> 和 <code>free</code> 的一个最简化实现的代码，来帮助说明管理内存时都涉及到了哪些事情。 </p>
<p>要试着运行这些示例，需要先 <a href="http://www.ibm.com/developerworks/cn/linux/l-memory/sidefile.html">复制本代码清单</a>，并将其粘贴到一个名为 malloc.c 的文件中。接下来，我将一次一个部分地对该清单进行解释。 </p>
<p>在大部分操作系统中，内存分配由以下两个简单的函数来处理： </p>
<ul>
    <li><code>void *malloc(long numbytes)</code>：该函数负责分配 <code>numbytes</code> 大小的内存，并返回指向第一个字节的指针。
    <li><code>void free(void *firstbyte)</code>：如果给定一个由先前的 <code>malloc</code> 返回的指针，那么该函数会将分配的空间归还给进程的&#8220;空闲空间&#8221;。 </li>
</ul>
<p><code>malloc_init</code> 将是初始化内存分配程序的函数。它要完成以下三件事：将分配程序标识为已经初始化，找到系统中最后一个有效内存地址，然后建立起指向我们管理的内存的指针。这三个变量都是全局变量： </p>
<br><br><a name=N10124><strong>清单 1. 我们的简单分配程序的全局变量</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            int has_initialized = 0;
            void *managed_memory_start;
            void *last_valid_address;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>如前所述，被映射的内存的边界（最后一个有效地址）常被称为系统中断点或者 <em>当前中断点</em>。在很多 UNIX&#174; 系统中，为了指出当前系统中断点，必须使用 <code>sbrk(0)</code> 函数。 <code>sbrk</code> 根据参数中给出的字节数移动当前系统中断点，然后返回新的系统中断点。使用参数 <code>0</code> 只是返回当前中断点。这里是我们的 <code>malloc</code> 初始化代码，它将找到当前中断点并初始化我们的变量： </p>
<br><br><a name=N10144><strong>清单 2. 分配程序初始化函数</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            /* Include the sbrk function */
            #include &lt;unistd.h&gt;
            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;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>现在，为了完全地管理内存，我们需要能够追踪要分配和回收哪些内存。在对内存块进行了 <code>free</code> 调用之后，我们需要做的是诸如将它们标记为未被使用的等事情，并且，在调用 <code>malloc</code> 时，我们要能够定位未被使用的内存块。因此， <code>malloc</code> 返回的每块内存的起始处首先要有这个结构： </p>
<br><br><a name=N1015D><strong>清单 3. 内存控制块结构定义</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            struct mem_control_block {
            int is_available;
            int size;
            };
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>现在，您可能会认为当程序调用 <code>malloc</code> 时这会引发问题 —— 它们如何知道这个结构？答案是它们不必知道；在返回指针之前，我们会将其移动到这个结构之后，把它隐藏起来。这使得返回的指针指向没有用于任何其他用途的内存。那样，从调用程序的角度来看，它们所得到的全部是空闲的、开放的内存。然后，当通过 <code>free()</code> 将该指针传递回来时，我们只需要倒退几个内存字节就可以再次找到这个结构。 </p>
<p>在讨论分配内存之前，我们将先讨论释放，因为它更简单。为了释放内存，我们必须要做的惟一一件事情就是，获得我们给出的指针，回退 <code>sizeof(struct mem_control_block)</code> 个字节，并将其标记为可用的。这里是对应的代码： </p>
<br><br><a name=N10179><strong>清单 4. 解除分配函数</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            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-&gt;is_available = 1;
            /* That's It!  We're done. */
            return;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>如您所见，在这个分配程序中，内存的释放使用了一个非常简单的机制，在固定时间内完成内存释放。分配内存稍微困难一些。以下是该算法的略述： </p>
<br><br><a name=N10186><strong>清单 5. 主分配程序的伪代码</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            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
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>我们主要使用连接的指针遍历内存来寻找开放的内存块。这里是代码： </p>
<br><br><a name=N10193><strong>清单 6. 主分配程序</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            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-&gt;is_available)
            {
            if(current_location_mcb-&gt;size &gt;= numbytes)
            {
            /* Woohoo!  We've found an open,
            * appropriately-size location.
            */
            /* It is no longer available */
            current_location_mcb-&gt;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-&gt;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-&gt;is_available = 0;
            current_location_mcb-&gt;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;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>这就是我们的内存管理器。现在，我们只需要构建它，并在程序中使用它即可。 </p>
<p>运行下面的命令来构建 <code>malloc</code> 兼容的分配程序（实际上，我们忽略了 <code>realloc()</code> 等一些函数，不过， <code>malloc()</code> 和 <code>free()</code> 才是最主要的函数）： </p>
<br><br><a name=N101B3><strong>清单 7. 编译分配程序</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            gcc -shared -fpic malloc.c -o malloc.so
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>该程序将生成一个名为 <em>malloc.so</em> 的文件，它是一个包含有我们的代码的共享库。 </p>
<p>在 UNIX 系统中，现在您可以用您的分配程序来取代系统的 <code>malloc()</code>，做法如下： </p>
<br><br><a name=N101CA><strong>清单 8. 替换您的标准的 malloc</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            LD_PRELOAD=/path/to/malloc.so
            export LD_PRELOAD
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><code>LD_PRELOAD</code> 环境变量使动态链接器在加载任何可执行程序之前，先加载给定的共享库的符号。它还为特定库中的符号赋予优先权。因此，从现在起，该会话中的任何应用程序都将使用我们的 <code>malloc()</code>，而不是只有系统的应用程序能够使用。有一些应用程序不使用 <code>malloc()</code>，不过它们是例外。其他使用 <code>realloc()</code> 等其他内存管理函数的应用程序，或者错误地假定 <code>malloc()</code> 内部行为的那些应用程序，很可能会崩溃。ash shell 似乎可以使用我们的新 <code>malloc()</code> 很好地工作。 </p>
<p>如果您想确保 <code>malloc()</code> 正在被使用，那么您应该通过向函数的入口点添加 <code>write()</code> 调用来进行测试。 </p>
<p>我们的内存管理器在很多方面都还存在欠缺，但它可以有效地展示内存管理需要做什么事情。它的某些缺点包括：
<ul>
    <li>由于它对系统中断点（一个全局变量）进行操作，所以它不能与其他分配程序或者 <code>mmap</code> 一起使用。
    <li>当分配内存时，在最坏的情形下，它将不得不遍历 <em>全部</em>进程内存；其中可能包括位于硬盘上的很多内存，这意味着操作系统将不得不花时间去向硬盘移入数据和从硬盘中移出数据。
    <li>没有很好的内存不足处理方案（ <code>malloc</code> 只假定内存分配是成功的）。
    <li>它没有实现很多其他的内存函数，比如 <code>realloc()</code>。
    <li>由于 <code>sbrk()</code> 可能会交回比我们请求的更多的内存，所以在堆（heap）的末端会遗漏一些内存。
    <li>虽然 <code>is_available</code> 标记只包含一位信息，但它要使用完整的 4-字节 的字。
    <li>分配程序不是线程安全的。
    <li>分配程序不能将空闲空间拼合为更大的内存块。
    <li>分配程序的过于简单的匹配算法会导致产生很多潜在的内存碎片。
    <li>我确信还有很多其他问题。这就是为什么它只是一个例子! </li>
</ul>
<p>&nbsp;</p>
<p><a name=N10232><span class=smalltitle>其他 malloc 实现</span></a></p>
<p><code>malloc()</code> 的实现有很多，这些实现各有优点与缺点。在设计一个分配程序时，要面临许多需要折衷的选择，其中包括：
<ul>
    <li>分配的速度。
    <li>回收的速度。
    <li>有线程的环境的行为。
    <li>内存将要被用光时的行为。
    <li>局部缓存。
    <li>簿记（Bookkeeping）内存开销。
    <li>虚拟内存环境中的行为。
    <li>小的或者大的对象。
    <li>实时保证。 </li>
</ul>
<p>&nbsp;</p>
<p>每一个实现都有其自身的优缺点集合。在我们的简单的分配程序中，分配非常慢，而回收非常快。另外，由于它在使用虚拟内存系统方面较差，所以它最适于处理大的对象。 </p>
<p>还有其他许多分配程序可以使用。其中包括：
<ul>
    <li><strong>Doug Lea Malloc：</strong>Doug Lea Malloc 实际上是完整的一组分配程序，其中包括 Doug Lea 的原始分配程序，GNU libc 分配程序和 <code>ptmalloc</code>。 Doug Lea 的分配程序有着与我们的版本非常类似的基本结构，但是它加入了索引，这使得搜索速度更快，并且可以将多个没有被使用的块组合为一个大的块。它还支持缓存，以便更快地再次使用最近释放的内存。 <code>ptmalloc</code> 是 Doug Lea Malloc 的一个扩展版本，支持多线程。在本文后面的 <a href="http://www.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>部分中，有一篇描述 Doug Lea 的 Malloc 实现的文章。
    <li><strong>BSD Malloc：</strong>BSD Malloc 是随 4.2 BSD 发行的实现，包含在 FreeBSD 之中，这个分配程序可以从预先确实大小的对象构成的池中分配对象。它有一些用于对象大小的 size 类，这些对象的大小为 2 的若干次幂减去某一常数。所以，如果您请求给定大小的一个对象，它就简单地分配一个与之匹配的 size 类。这样就提供了一个快速的实现，但是可能会浪费内存。在 <a href="http://www.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>部分中，有一篇描述该实现的文章。
    <li><strong>Hoard：编写 </strong>Hoard 的目标是使内存分配在多线程环境中进行得非常快。因此，它的构造以锁的使用为中心，从而使所有进程不必等待分配内存。它可以显著地加快那些进行很多分配和回收的多线程进程的速度。在 <a href="http://www.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>部分中，有一篇描述该实现的文章。 </li>
</ul>
<p>&nbsp;</p>
<p>众多可用的分配程序中最有名的就是上述这些分配程序。如果您的程序有特别的分配需求，那么您可能更愿意编写一个定制的能匹配您的程序内存分配方式的分配程序。不过，如果不熟悉分配程序的设计，那么定制分配程序通常会带来比它们解决的问题更多的问题。要获得关于该主题的适当的介绍，请参阅 Donald Knuth 撰写的 <em>The Art of Computer Programming Volume 1: Fundamental Algorithms</em> 中的第 2.5 节&#8220;Dynamic Storage Allocation&#8221;（请参阅 <a href="http://www.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>中的链接）。它有点过时，因为它没有考虑虚拟内存环境，不过大部分算法都是基于前面给出的函数。 </p>
<p>在 C++ 中，通过重载 <code>operator new()</code>，您可以以每个类或者每个模板为单位实现自己的分配程序。在 Andrei Alexandrescu 撰写的 <em>Modern C++ Design</em> 的第 4 章（&#8220;Small Object Allocation&#8221;）中，描述了一个小对象分配程序（请参阅 <a href="http://www.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>中的链接）。 </p>
<p><a name=N102A4><span class=smalltitle>基于 malloc() 的内存管理的缺点</span></a></p>
<p>不只是我们的内存管理器有缺点，基于 <code>malloc()</code> 的内存管理器仍然也有很多缺点，不管您使用的是哪个分配程序。对于那些需要保持长期存储的程序使用 <code>malloc()</code> 来管理内存可能会非常令人失望。如果您有大量的不固定的内存引用，经常难以知道它们何时被释放。生存期局限于当前函数的内存非常容易管理，但是对于生存期超出该范围的内存来说，管理内存则困难得多。而且，关于内存管理是由进行调用的程序还是由被调用的函数来负责这一问题，很多 API 都不是很明确。 </p>
<p>因为管理内存的问题，很多程序倾向于使用它们自己的内存管理规则。C++ 的异常处理使得这项任务更成问题。有时好像致力于管理内存分配和清理的代码比实际完成计算任务的代码还要多！因此，我们将研究内存管理的其他选择。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-memory/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N102B8><span class=atitle>半自动内存管理策略</span></a></p>
<p><a name=N102BF><span class=smalltitle>引用计数</span></a></p>
<p>引用计数是一种 <em>半自动（semi-automated）</em>的内存管理技术，这表示它需要一些编程支持，但是它不需要您确切知道某一对象何时不再被使用。引用计数机制为您完成内存管理任务。 </p>
<p>在引用计数中，所有共享的数据结构都有一个域来包含当前活动&#8220;引用&#8221;结构的次数。当向一个程序传递一个指向某个数据结构指针时，该程序会将引用计数增加 1。实质上，您是在告诉数据结构，它正在被存储在多少个位置上。然后，当您的进程完成对它的使用后，该程序就会将引用计数减少 1。结束这个动作之后，它还会检查计数是否已经减到零。如果是，那么它将释放内存。 </p>
<p>这样做的好处是，您不必追踪程序中某个给定的数据结构可能会遵循的每一条路径。每次对其局部的引用，都将导致计数的适当增加或减少。这样可以防止在使用数据结构时释放该结构。不过，当您使用某个采用引用计数的数据结构时，您必须记得运行引用计数函数。另外，内置函数和第三方的库不会知道或者可以使用您的引用计数机制。引用计数也难以处理发生循环引用的数据结构。 </p>
<p>要实现引用计数，您只需要两个函数 —— 一个增加引用计数，一个减少引用计数并当计数减少到零时释放内存。 </p>
<p>一个示例引用计数函数集可能看起来如下所示： </p>
<br><br><a name=N102DA><strong>清单 9. 基本的引用计数函数</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            /* 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-&gt;refcount++;
            }
            /* Decrease reference count */
            void UNREF(void *data)
            {
            struct refcountedstruct *rstruct;
            rstruct = (struct refcountedstruct *) data;
            rstruct-&gt;refcount--;
            /* Free the structure if there are no more users */
            if(rstruct-&gt;refcount == 0)
            {
            free(rstruct);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><code>REF</code> 和 <code>UNREF</code> 可能会更复杂，这取决于您想要做的事情。例如，您可能想要为多线程程序增加锁，那么您可能想扩展 <code>refcountedstruct</code>，使它同样包含一个指向某个在释放内存之前要调用的函数的指针（类似于面向对象语言中的析构函数 —— 如果您的结构中包含这些指针，那么这是 <em>必需的</em>）。 </p>
<p>当使用 <code>REF</code> 和 <code>UNREF</code> 时，您需要遵守这些指针的分配规则：
<ul>
    <li><code>UNREF</code> 分配前左端指针（left-hand-side pointer）指向的值。
    <li><code>REF</code> 分配后左端指针（left-hand-side pointer）指向的值。 </li>
</ul>
<p>&nbsp;</p>
<p>在传递使用引用计数的结构的函数中，函数需要遵循以下这些规则：
<ul>
    <li>在函数的起始处 REF 每一个指针。
    <li>在函数的结束处 UNREF 第一个指针。 </li>
</ul>
<p>&nbsp;</p>
<p>以下是一个使用引用计数的生动的代码示例： </p>
<br><br><a name=N10322><strong>清单 10. 使用引用计数的示例</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            /* 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 */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>由于引用计数是如此简单，大部分程序员都自已去实现它，而不是使用库。不过，它们依赖于 <code>malloc</code> 和 <code>free</code> 等低层的分配程序来实际地分配和释放它们的内存。 </p>
<p>在 Perl 等高级语言中，进行内存管理时使用引用计数非常广泛。在这些语言中，引用计数由语言自动地处理，所以您根本不必担心它，除非要编写扩展模块。由于所有内容都必须进行引用计数，所以这会对速度产生一些影响，但它极大地提高了编程的安全性和方便性。以下是引用计数的益处：
<ul>
    <li>实现简单。
    <li>易于使用。
    <li>由于引用是数据结构的一部分，所以它有一个好的缓存位置。 </li>
</ul>
<p>&nbsp;</p>
<p>不过，它也有其不足之处：
<ul>
    <li>要求您永远不要忘记调用引用计数函数。
    <li>无法释放作为循环数据结构的一部分的结构。
    <li>减缓几乎每一个指针的分配。
    <li>尽管所使用的对象采用了引用计数，但是当使用异常处理（比如 <code>try</code> 或 <code>setjmp()</code>/ <code>longjmp()</code>）时，您必须采取其他方法。
    <li>需要额外的内存来处理引用。
    <li>引用计数占用了结构中的第一个位置，在大部分机器中最快可以访问到的就是这个位置。
    <li>在多线程环境中更慢也更难以使用。 </li>
</ul>
<p>&nbsp;</p>
<p>C++ 可以通过使用 <em>智能指针（smart pointers）</em>来容忍程序员所犯的一些错误，智能指针可以为您处理引用计数等指针处理细节。不过，如果不得不使用任何先前的不能处理智能指针的代码（比如对 C 库的联接），实际上，使用它们的后果通实比不使用它们更为困难和复杂。因此，它通常只是有益于纯 C++ 项目。如果您想使用智能指针，那么您实在应该去阅读 Alexandrescu 撰写的 <em>Modern C++ Design</em> 一书中的&#8220;Smart Pointers&#8221;那一章。 </p>
<p><a name=N10373><span class=smalltitle>内存池</span></a></p>
<p>内存池是另一种半自动内存管理方法。内存池帮助某些程序进行自动内存管理，这些程序会经历一些特定的阶段，而且每个阶段中都有分配给进程的特定阶段的内存。例如，很多网络服务器进程都会分配很多针对每个连接的内存 —— 内存的最大生存期限为当前连接的存在期。Apache 使用了池式内存（pooled memory），将其连接拆分为各个阶段，每个阶段都有自己的内存池。在结束每个阶段时，会一次释放所有内存。 </p>
<p>在池式内存管理中，每次内存分配都会指定内存池，从中分配内存。每个内存池都有不同的生存期限。在 Apache 中，有一个持续时间为服务器存在期的内存池，还有一个持续时间为连接的存在期的内存池，以及一个持续时间为请求的存在期的池，另外还有其他一些内存池。因此，如果我的一系列函数不会生成比连接持续时间更长的数据，那么我就可以完全从连接池中分配内存，并知道在连接结束时，这些内存会被自动释放。另外，有一些实现允许注册 <em>清除函数（cleanup functions）</em>，在清除内存池之前，恰好可以调用它，来完成在内存被清理前需要完成的其他所有任务（类似于面向对象中的析构函数）。 </p>
<p>要在自己的程序中使用池，您既可以使用 GNU libc 的 obstack 实现，也可以使用 Apache 的 Apache Portable Runtime。GNU obstack 的好处在于，基于 GNU 的 Linux 发行版本中默认会包括它们。Apache Portable Runtime 的好处在于它有很多其他工具，可以处理编写多平台服务器软件所有方面的事情。要深入了解 GNU obstack 和 Apache 的池式内存实现，请参阅 <a href="http://www.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>部分中指向这些实现的文档的链接。 </p>
<p>下面的假想代码列表展示了如何使用 obstack： </p>
<br><br><a name=N1038F><strong>清单 11. obstack 的示例代码</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>            #include &lt;obstack.h&gt;
            #include &lt;stdlib.h&gt;
            /* 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 = &amp;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;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>基本上，在操作的每一个主要阶段结束之后，这个阶段的 obstack 会被释放。不过，要注意的是，如果一个过程需要分配持续时间比当前阶段更长的内存，那么它也可以使用更长期限的 obstack，比如连接或者全局内存。传递给 <code>obstack_free()</code> 的 <code>NULL</code> 指出它应该释放 obstack 的全部内容。可以用其他的值，但是它们通常不怎么实用。 </p>
<p>使用池式内存分配的益处如下所示：
<ul>
    <li>应用程序可以简单地管理内存。
    <li>内存分配和回收更快，因为每次都是在一个池中完成的。分配可以在 O(1) 时间内完成，释放内存池所需时间也差不多（实际上是 O(n) 时间，不过在大部分情况下会除以一个大的因数，使其变成 O(1)）。
    <li>可以预先分配错误处理池（Error-handling pools），以便程序在常规内存被耗尽时仍可以恢复。
    <li>有非常易于使用的标准实现。 </li>
</ul>
<p>&nbsp;</p>
<p>池式内存的缺点是： </p>
<ul>
    <li>内存池只适用于操作可以分阶段的程序。
    <li>内存池通常不能与第三方库很好地合作。
    <li>如果程序的结构发生变化，则不得不修改内存池，这可能会导致内存管理系统的重新设计。
    <li>您必须记住需要从哪个池进行分配。另外，如果在这里出错，就很难捕获该内存池。 </li>
</ul>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-memory/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N103C5><span class=atitle>垃圾收集</span></a></p>
<p><em>垃圾收集（Garbage collection）</em>是全自动地检测并移除不再使用的数据对象。垃圾收集器通常会在当可用内存减少到少于一个具体的阈值时运行。通常，它们以程序所知的可用的一组&#8220;基本&#8221;数据 —— 栈数据、全局变量、寄存器 —— 作为出发点。然后它们尝试去追踪通过这些数据连接到每一块数据。收集器找到的都是有用的数据；它没有找到的就是垃圾，可以被销毁并重新使用这些无用的数据。为了有效地管理内存，很多类型的垃圾收集器都需要知道数据结构内部指针的规划，所以，为了正确运行垃圾收集器，它们必须是语言本身的一部分。 </p>
<p><a name=N103D2><span class=smalltitle>收集器的类型</span></a></p>
<p>
<ul>
    <li><strong>复制（copying）：</strong> 这些收集器将内存存储器分为两部分，只允许数据驻留在其中一部分上。它们定时地从&#8220;基本&#8221;的元素开始将数据从一部分复制到另一部分。内存新近被占用的部分现在成为活动的，另一部分上的所有内容都认为是垃圾。另外，当进行这项复制操作时，所有指针都必须被更新为指向每个内存条目的新位置。因此，为使用这种垃圾收集方法，垃圾收集器必须与编程语言集成在一起。
    <li><strong>标记并清理（Mark and sweep）：</strong>每一块数据都被加上一个标签。不定期的，所有标签都被设置为 0，收集器从&#8220;基本&#8221;的元素开始遍历数据。当它遇到内存时，就将标签标记为 1。最后没有被标记为 1 的所有内容都认为是垃圾，以后分配内存时会重新使用它们。
    <li><strong>增量的（Incremental）：</strong>增量垃圾收集器不需要遍历全部数据对象。因为在收集期间的突然等待，也因为与访问所有当前数据相关的缓存问题（所有内容都不得不被页入（page-in）），遍历所有内存会引发问题。增量收集器避免了这些问题。
    <li><strong>保守的（Conservative）：</strong>保守的垃圾收集器在管理内存时不需要知道与数据结构相关的任何信息。它们只查看所有数据类型，并假定它们 <em>可以</em>全部都是指针。所以，如果一个字节序列可以是一个指向一块被分配的内存的指针，那么收集器就将其标记为正在被引用。有时没有被引用的内存会被收集，这样会引发问题，例如，如果一个整数域中包含一个值，该值是已分配内存的地址。不过，这种情况极少发生，而且它只会浪费少量内存。保守的收集器的优势是，它们可以与任何编程语言相集成。 </li>
</ul>
<p>&nbsp;</p>
<p>Hans Boehm 的保守垃圾收集器是可用的最流行的垃圾收集器之一，因为它是免费的，而且既是保守的又是增量的，可以使用 <code>--enable-redirect-malloc</code> 选项来构建它，并且可以将它用作系统分配程序的简易替代者（drop-in replacement）（用 <code>malloc</code>/ <code>free</code> 代替它自己的 API）。实际上，如果这样做，您就可以使用与我们在示例分配程序中所使用的相同的 <code>LD_PRELOAD</code> 技巧，在系统上的几乎任何程序中启用垃圾收集。如果您怀疑某个程序正在泄漏内存，那么您可以使用这个垃圾收集器来控制进程。在早期，当 Mozilla 严重地泄漏内存时，很多人在其中使用了这项技术。这种垃圾收集器既可以在 Windows&#174; 下运行，也可以在 UNIX 下运行。 </p>
<p>垃圾收集的一些优点：
<ul>
    <li>您永远不必担心内存的双重释放或者对象的生命周期。
    <li>使用某些收集器，您可以使用与常规分配相同的 API。 </li>
</ul>
<p>&nbsp;</p>
<p>其缺点包括：
<ul>
    <li>使用大部分收集器时，您都无法干涉何时释放内存。
    <li>在多数情况下，垃圾收集比其他形式的内存管理更慢。
    <li>垃圾收集错误引发的缺陷难于调试。
    <li>如果您忘记将不再使用的指针设置为 null，那么仍然会有内存泄漏。 </li>
</ul>
<p>&nbsp;</p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-memory/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N1042A><span class=atitle>结束语</span></a></p>
<p>一切都需要折衷：性能、易用、易于实现、支持线程的能力等，这里只列出了其中的一些。为了满足项目的要求，有很多内存管理模式可以供您使用。每种模式都有大量的实现，各有其优缺点。对很多项目来说，使用编程环境默认的技术就足够了，不过，当您的项目有特殊的需要时，了解可用的选择将会有帮助。下表对比了本文中涉及的内存管理策略。 </p>
<p><a name=N10434><span class=smalltitle>表 1. 内存分配策略的对比</span></a></p>
<p>
<table cellSpacing=1 cellPadding=3 width="60%" border=1>
    <tbody>
        <tr vAlign=top>
            <td><strong>策略</strong> </td>
            <td><strong>分配速度</strong> </td>
            <td><strong>回收速度</strong> </td>
            <td><strong>局部缓存</strong> </td>
            <td><strong>易用性</strong> </td>
            <td><strong>通用性</strong> </td>
            <td><strong>实时可用</strong> </td>
            <td><strong>SMP 线程友好</strong> </td>
        </tr>
        <tr>
            <td>定制分配程序 </td>
            <td>取决于实现 </td>
            <td>取决于实现 </td>
            <td>取决于实现 </td>
            <td>很难 </td>
            <td>无 </td>
            <td>取决于实现 </td>
            <td>取决于实现 </td>
        </tr>
        <tr>
            <td>简单分配程序</td>
            <td>内存使用少时较快</td>
            <td>很快</td>
            <td>差 </td>
            <td>容易 </td>
            <td>高 </td>
            <td>否 </td>
            <td>否 </td>
        </tr>
        <tr>
            <td>GNU <code>malloc</code> </td>
            <td>中 </td>
            <td>快 </td>
            <td>中 </td>
            <td>容易 </td>
            <td>高 </td>
            <td>否 </td>
            <td>中</td>
        </tr>
        <tr>
            <td>Hoard </td>
            <td>中 </td>
            <td>中 </td>
            <td>中 </td>
            <td>容易 </td>
            <td>高 </td>
            <td>否 </td>
            <td>是 </td>
        </tr>
        <tr>
            <td>引用计数 </td>
            <td>N/A </td>
            <td>N/A </td>
            <td>非常好 </td>
            <td>中 </td>
            <td>中 </td>
            <td>是（取决于 <code>malloc</code> 实现） </td>
            <td>取决于实现 </td>
        </tr>
        <tr>
            <td>池 </td>
            <td>中 </td>
            <td>非常快 </td>
            <td>极好 </td>
            <td>中 </td>
            <td>中 </td>
            <td>是（取决于 <code>malloc</code> 实现） </td>
            <td>取决于实现 </td>
        </tr>
        <tr>
            <td>垃圾收集 </td>
            <td>中（进行收集时慢） </td>
            <td>中 </td>
            <td>差 </td>
            <td>中 </td>
            <td>中 </td>
            <td>否 </td>
            <td>几乎不 </td>
        </tr>
        <tr>
            <td>增量垃圾收集 </td>
            <td>中 </td>
            <td>中 </td>
            <td>中 </td>
            <td>中 </td>
            <td>中 </td>
            <td>否 </td>
            <td>几乎不 </td>
        </tr>
        <tr>
            <td>增量保守垃圾收集 </td>
            <td>中 </td>
            <td>中 </td>
            <td>中 </td>
            <td>容易 </td>
            <td>高 </td>
            <td>否 </td>
            <td>几乎不</td>
        </tr>
    </tbody>
</table>
</p>
<br><br>
<p><a name=resources><span class=atitle>参考资料 </span></a></p>
<ul>
    <li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/linux/library/l-memory/?S_TACT=105AGX52&amp;S_CMP=cn-a-l" target=_blank>英文原文</a>。 <br></li>
</ul>
<p><strong>Web 上的文档</strong>
<ul>
    <li><a href="http://www.gnu.org/software/libc/manual/html_node/Obstacks.html#Obstacks">GNU C Library 手册的 obstacks 部分</a> 提供了 obstacks 编程接口。 <br><br>
    <li><a href="http://apr.apache.org/docs/apr/group__apr__pools.html">Apache Portable Runtime 文档</a> 描述了它们的池式分配程序的接口。 <br></li>
</ul>
<strong>基本的分配程序</strong>
<ul>
    <li><a href="http://gee.cs.oswego.edu/dl/html/malloc.html">Doug Lea 的 Malloc</a> 是最流行的内存分配程序之一。 <br><br>
    <li><a href="http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdlib/malloc.c">BSD Malloc</a> 用于大部分基于 BSD 的系统中。 <br><br>
    <li><a href="http://www.malloc.de/en/">ptmalloc</a> 起源于 Doug Lea 的 malloc，用于 GLIBC 之中。 <br><br>
    <li><a href="http://www.cs.umass.edu/~emery/hoard/">Hoard</a> 是一个为多线程应用程序优化的 <code>malloc</code> 实现。 <br><br>
    <li><a href="http://ftp.gnu.org/gnu/gdb/gdb-6.2.tar.gz">GNU Memory-Mapped Malloc（GDB 的组成部分）</a> 是一个基于 <code>mmap()</code> 的 <code>malloc</code> 实现。 <br></li>
</ul>
<strong>池式分配程序</strong>
<ul>
    <li><a href="http://www.gnu.org/software/libc/">GNU Obstacks</a>（GNU Libc 的组成部分）是安装最多的池式分配程序，因为在每一个基于 glibc 的系统中都有它。 <br><br>
    <li><a href="http://apr.apache.org/">Apache 的池式分配程序（Apache Portable Runtime 中）</a> 是应用最为广泛的池式分配程序。 <br><br>
    <li><a href="ftp://ftp.vistech.net/pub/squid/squid-2/STABLE/">Squid</a> 有其自己的池式分配程序。 <br><br>
    <li><a href="http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/kern/subr_pool.c">NetBSD</a> 也有其自己的池式分配程序。 <br><br>
    <li><a href="http://www.samba.org/samba/ftp/samba-latest.tar.gz">talloc</a> 是一个池式分配程序，是 Samba 的组成部分。 <br></li>
</ul>
<strong>智能指针和定制分配程序</strong>
<ul>
    <li><a href="http://sourceforge.net/projects/loki-lib/">Loki C++ Library</a> 有很多为 C++ 实现的通用模式，包括智能指针和一个定制的小对象分配程序。 <br></li>
</ul>
<strong>垃圾收集器</strong>
<ul>
    <li><a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Hahns Boehm Conservative Garbage Collector</a> 是最流行的开源垃圾收集器，它可以用于常规的 C/C++ 程序。 <br></li>
</ul>
<strong>关于现代操作系统中的虚拟内存的文章</strong>
<ul>
    <li>Marshall Kirk McKusick 和 Michael J. Karels 合著的 <a href="http://docs.freebsd.org/44doc/papers/newvm.html">A New Virtual Memory Implementation for Berkeley UNIX</a> 讨论了 BSD 的 VM 系统。 <br><br>
    <li><a href="http://www.skynet.ie/~mel/projects/vm/">Mel Gorman's Linux VM Documentation</a> 讨论了 Linux VM 系统。 <br></li>
</ul>
<strong>关于 malloc 的文章</strong>
<ul>
    <li>Poul-Henning Kamp 撰写的 <a href="http://docs.freebsd.org/44doc/papers/malloc.html">Malloc in Modern Virtual Memory Environments</a> 讨论的是 <code>malloc</code> 以及它如何与 BSD 虚拟内存交互。 <br><br>
    <li>Berger、McKinley、Blumofe 和 Wilson 合著的 <a href="ftp://ftp.cs.utexas.edu/pub/emery/papers/asplos2000.pdf">Hoard -- a Scalable Memory Allocator for Multithreaded Environments</a> 讨论了 Hoard 分配程序的实现。 <br><br>
    <li>Marshall Kirk McKusick 和 Michael J. Karels 合著的 <a href="http://docs.freebsd.org/44doc/papers/kernmalloc.html">Design of a General Purpose Memory Allocator for the 4.3BSD UNIX Kernel</a> 讨论了内核级的分配程序。 <br><br>
    <li>Doug Lea 撰写的 <a href="http://gee.cs.oswego.edu/dl/html/malloc.html">A Memory Allocator</a> 给出了一个关于设计和实现分配程序的概述，其中包括设计选择与折衷。 <br><br>
    <li>Emery D. Berger 撰写的 <a href="http://www.cs.utexas.edu/ftp/pub/techreports/tr02-52.pdf">Memory Management for High-Performance Applications</a> 讨论的是定制内存管理以及它如何影响高性能应用程序。 <br></li>
</ul>
<strong>关于定制分配程序的文章</strong>
<ul>
    <li>Doug Lea 撰写的 <a href="ftp://g.oswego.edu/pub/papers/C++Report89.txt">Some Storage Management Techniques for Container Classes</a> 描述的是为 C++ 类编写定制分配程序。 <br><br>
    <li>Berger、Zorn 和 McKinley 合著的 <a href="ftp://ftp.cs.utexas.edu/pub/emery/papers/pldi2001.pdf">Composing High-Performance Memory Allocators</a> 讨论了如何编写定制分配程序来加快具体工作的速度。 <br><br>
    <li>Berger、Zorn 和 McKinley 合著的 <a href="ftp://ftp.cs.utexas.edu/pub/emery/papers/reconsidering-custom.pdf">Reconsidering Custom Memory Allocation</a> 再次提及了定制分配的主题，看是否真正值得为其费心。 <br></li>
</ul>
<strong>关于垃圾收集的文章</strong>
<ul>
    <li>Paul R. Wilson 撰写的 <a href="ftp://ftp.cs.utexas.edu/pub/garbage/bigsurv.ps">Uniprocessor Garbage Collection Techniques</a> 给出了垃圾收集的一个基本概述。 <br><br>
    <li>Benjamin Zorn 撰写的 <a href="ftp://ftp.cs.colorado.edu/pub/techreports/zorn/CU-CS-573-92.ps.Z">The Measured Cost of Garbage Collection</a> 给出了关于垃圾收集和性能的硬数据（hard data）。 <br><br>
    <li>Hans-Juergen Boehm 撰写的 <a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/myths.ps">Memory Allocation Myths and Half-Truths</a> 给出了关于垃圾收集的神话（myths）。 <br><br>
    <li>Hans-Juergen Boehm 撰写的 <a href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi93.ps.Z">Space Efficient Conservative Garbage Collection</a> 是一篇描述他的用于 C/C++ 的垃圾收集器的文章。 <br></li>
</ul>
<strong>Web 上的通用参考资料</strong>
<ul>
    <li><a href="http://www.memorymanagement.org/">内存管理参考</a> 中有很多关于内存管理参考资料和技术文章的链接。 <br><br>
    <li><a href="http://www.cs.utexas.edu/users/oops/papers.html">关于内存管理和内存层级的 OOPS Group Papers</a> 是非常好的一组关于此主题的技术文章。 <br><br>
    <li><a href="http://www.cantrip.org/wave12.html">C++ 中的内存管理</a>讨论的是为 C++ 编写定制的分配程序。 <br><br>
    <li><a href="http://www.conman.org/projects/essays/memmgr.html">Programming Alternatives: Memory Management</a> 讨论了程序员进行内存管理时的一些选择。 <br><br>
    <li><a href="http://www.iecc.com/gclist/GC-faq.html">垃圾收集 FAQ</a> 讨论了关于垃圾收集您需要了解的所有内容。 <br><br>
    <li><a href="http://www.cs.ukc.ac.uk/people/staff/rej/gcbib/gcbibG.html">Richard Jones 的 Garbage Collection Bibliography</a> 有指向任何您想要的关于垃圾收集的文章的链接。 <br></li>
</ul>
<strong>书籍</strong>
<ul>
    <li>Michael Daconta 撰写的 <a href="http://www.amazon.com/exec/obidos/ASIN/0471049980/freeeducation-20/"><em>C++ Pointers and Dynamic Memory Management</em> </a>介绍了关于内存管理的很多技术。 <br><br>
    <li>Frantisek Franek 撰写的 <a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0521520436"><em>Memory as a Programming Concept in C and C++</em> </a>讨论了有效使用内存的技术与工具，并给出了在计算机编程中应当引起注意的内存相关错误的角色。 <br><br>
    <li>Richard Jones 和 Rafael Lins 合著的 <a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0471941484"><em>Garbage Collection: Algorithms for Automatic Dynamic Memory Management</em> </a>描述了当前使用的最常见的垃圾收集算法。 <br><br>
    <li>在 Donald Knuth 撰写的 <em>The Art of Computer Programming</em> 第 1 卷 <a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0201896834"><em>Fundamental Algorithms</em> </a>的第 2.5 节&#8220;Dynamic Storage Allocation&#8221;中，描述了实现基本的分配程序的一些技术。 <br><br>
    <li>在 Donald Knuth 撰写的 <em>The Art of Computer Programming</em> 第 1 卷 <a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0201896834"><em>Fundamental Algorithms</em> </a>的第 2.3.5 节&#8220;Lists and Garbage Collection&#8221;中，讨论了用于列表的垃圾收集算法。 <br><br>
    <li>Andrei Alexandrescu 撰写的 <a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0201704315"><em>Modern C++ Design</em> </a>第 4 章&#8220;Small Object Allocation&#8221;描述了一个比 C++ 标准分配程序效率高得多的一个高速小对象分配程序。 <br><br>
    <li>Andrei Alexandrescu 撰写的 <a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0201704315"><em>Modern C++ Design</em> </a>第 7 章&#8220;Smart Pointers&#8221;描述了在 C++ 中智能指针的实现。 <br><br>
    <li>Jonathan 撰写的 <a href="http://www.amazon.com/exec/obidos/ASIN/0975283847/freeeducation-20/"><em>Programming from the Ground Up</em> </a>第 8 章&#8220;Intermediate Memory Topics&#8221;中有本文使用的简单分配程序的一个汇编语言版本。 <br></li>
</ul>
<strong>来自 developerWorks</strong>
<ul>
    <li><a href="http://www.ibm.com/developerworks/cn/linux/wa-memmng/">自我管理数据缓冲区内存</a> （developerWorks，2004 年 1 月）略述了一个用于管理内存的自管理的抽象数据缓存器的伪 C （pseudo-C）实现。 <br><br>
    <li><a href="http://www.ibm.com/developerworks/eserver/articles/framework.html?S_TACT=105AGX52&amp;S_CMP=cn-a-l">A framework for the user defined malloc replacement feature</a> （developerWorks，2002 年 2 月）展示了如何利用 AIX 中的一个工具，使用自己设计的内存子系统取代原有的内存子系统。 <br><br>
    <li><a href="http://www.ibm.com/developerworks/cn/linux/sdk/l-debug/index.html">掌握 Linux 调试技术</a> （developerWorks，2002 年 8 月）描述了可以使用调试方法的 4 种不同情形：段错误、内存溢出、内存泄漏和挂起。 <br><br>
    <li>在 <a href="http://www.ibm.com/developerworks/cn/java/j-leaks/index.html">处理 Java 程序中的内存漏洞</a> （developerWorks，2001 年 2 月）中，了解导致 Java 内存泄漏的原因，以及何时需要考虑它们。 <br><br>
    <li>在 <a href="http://www.ibm.com/developerworks/cn/linux/">developerWorks Linux 专区</a>中，可以找到更多为 Linux 开发人员准备的参考资料。 <br><br>
    <li>从 developerWorks 的 <a href="http://www.ibm.com/developerworks/cn/linux/linux-speed-start/">Speed-start your Linux app</a> 专区中，可以下载运行于 Linux 之上的 IBM 中间件产品的免费测试版本，其中包括 WebSphere&#174; Studio Application Developer、WebSphere Application Server、DB2&#174; Universal Database、Tivoli&#174; Access Manager 和 Tivoli Directory Server，查找 how-to 文章和技术支持。 <br><br>
    <li>通过参与 <a href="http://www.ibm.com/developerworks/blogs/?S_TACT=105AGX52&amp;S_CMP=cn-a-l">developerWorks blogs</a> 加入到 developerWorks 社区。 <br><br>
    <li>可以在 Developer Bookstore Linux 专栏中定购 <a href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=300&amp;parent=Linux" target=new>打折出售的 Linux 书籍</a>。 <br></li>
</ul>
<br><br>
<p><a name=author><span class=atitle>关于作者</span></a></p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td colSpan=3><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></td>
        </tr>
        <tr vAlign=top align=left>
            <td>
            <p>&nbsp;</p>
            </td>
            <td><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></td>
            <td width="100%">
            <p>Jonathan Bartlett 是 <a href="http://www.cafeshops.com/bartlettpublish.8640017"><em>Programming from the Ground Up</em> </a>一书的作者，这本书介绍的是 Linux 汇编语言编程。Jonathan Bartlett 是 New Media Worx 的总开发师，负责为客户开发 Web、视频、kiosk 和桌面应用程序。您可以通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#106;&#111;&#104;&#110;&#110;&#121;&#98;&#64;&#101;&#115;&#107;&#105;&#109;&#111;&#46;&#99;&#111;&#109;&#63;&#99;&#99;&#61;">johnnyb@eskimo.com</a> 与 Jonathan 联系。 </p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/xixidabao/aggbug/108120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-03 09:33 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/03/108120.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BiJiao.java</title><link>http://www.blogjava.net/xixidabao/archive/2006/10/15/75198.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 14 Oct 2006 16:15:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/10/15/75198.html</guid><description><![CDATA[public class BiJiao<br />{<br />   boolean comp(Object a,Object b)<br />   {<br />     return a==b;<br />    }<br />    <br />    boolean comp1(Object a,Object b)<br />    {<br />     return a.equals(b);<br />    }<br />    <br />    boolean compInt(int i,int j)<br />    {<br />     return i==j;<br />    }<br />    <br />    boolean compInt1(int i,int j)<br />    {<br />     return (Integer.valueOf(i)).equals(Integer.valueOf(j));<br />    }<br />    <br />    public void biObject(Object a,Object b)<br />    {<br />     if(comp(a,b))<br />     System.out.println("用\"==\"比较的结果：       "+"true");<br />     else<br />     System.out.println("用\"==\"比较的结果：       "+"false");<br />     if(comp1(a,b))<br />     System.out.println("用\"equals()\"比较的结果： "+"true");<br />     else<br />     System.out.println("用\"equals()\"比较的结果： "+"flase");<br />    }<br />    <br />    public void biInt(int i,int j)<br />    {<br />     if(compInt(i,j))<br />     System.out.println("用\"==\"比较的结果：       "+"true");<br />     else<br />     System.out.println("用\"==\"比较的结果：       "+"false");<br />     if(compInt1(i,j))<br />     System.out.println("用\"equals()\"比较的结果： "+"true");<br />     else<br />     System.out.println("用\"equals()\"比较的结果： "+"flase");<br />    }<br />     <br />    public int test(String t)<br />       {<br />        int k=0;<br />         String str="0123456789";<br />        for(int i=0;i&lt;t.length();i++)<br />       {<br />        char ch1=(char)t.charAt(i);<br />        <br />        if(str.indexOf(ch1)==-1)<br />          k++;<br />       }<br />       return k;<br />     }<br />     <br />    public static void main(String[] args)<br />    {<br />      BiJiao biJiao=new BiJiao();<br />      <br />      if(args.length&lt;2)<br />      {<br />        <br />        System.out.println("请输入两个字符或数字：");<br />        System.exit(0);<br />       }<br />       <br />       String a=args[0];<br />       String b=args[1];<br />       <br />       int k=biJiao.test(a);<br />       int p=biJiao.test(b);<br />      <br />       <br />       if(k==0&amp;&amp;p==0)<br />       {<br />        System.out.println("您输入的是数字：       "+a+" 和 "+b);<br />        int i=Integer.parseInt(a);<br />         int j=Integer.parseInt(b);<br />        biJiao.biInt(i,j);<br />       }<br />       else<br />         {<br />          System.out.println("您输入的是字符：     "+a+" 和 "+b);<br />          biJiao.biObject(a,b);<br />         }<br />    }<br />}<br />       <img src ="http://www.blogjava.net/xixidabao/aggbug/75198.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-10-15 00:15 <a href="http://www.blogjava.net/xixidabao/archive/2006/10/15/75198.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的垃圾回收（Garbage Collection）机制 </title><link>http://www.blogjava.net/xixidabao/archive/2006/09/15/69776.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Thu, 14 Sep 2006 16:56:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/09/15/69776.html</guid><description><![CDATA[
		<table cellspacing="0" cellpadding="0" align="left" border="0">
				<tbody>
						<tr>
								<td>
										<span id="leftAD">
												<div id="BannerZoneAD_Div11" style="Z-INDEX: 1; VISIBILITY: visible; WIDTH: 360px; HEIGHT: 300px">
														<object id="AD_11" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" height="300" width="360" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="AD_11">
																<param name="_cx" value="9525" />
																<param name="_cy" value="7938" />
																<param name="FlashVars" value="" />
																<param name="Movie" value="http://www.chinaitlab.com/cms/images/ad/360-300green.swf" />
																<param name="Src" value="http://www.chinaitlab.com/cms/images/ad/360-300green.swf" />
																<param name="WMode" value="Window" />
																<param name="Play" value="-1" />
																<param name="Loop" value="-1" />
																<param name="Quality" value="AutoHigh" />
																<param name="SAlign" value="" />
																<param name="Menu" value="-1" />
																<param name="Base" value="" />
																<param name="AllowScriptAccess" value="" />
																<param name="Scale" value="ShowAll" />
																<param name="DeviceFont" value="0" />
																<param name="EmbedMovie" value="0" />
																<param name="BGColor" value="" />
																<param name="SWRemote" value="" />
																<param name="MovieData" value="" />
																<param name="SeamlessTabbing" value="1" />
																<param name="Profile" value="0" />
																<param name="ProfileAddress" value="" />
																<param name="ProfilePort" value="0" />
																<embed name="AD_11" id="AD_11" width="360" height="300" src="http://www.chinaitlab.com/cms/images/ad/360-300green.swf" quality="autohigh" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash">
																</embed>
														</object>
												</div>
												<script src="/adjs/ADCount.asp?Action=View&amp;ADID=11">
												</script>
										</span>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<strong>    一。谁在做Garbage Collection？</strong>
		</p>
		<p>    一种流行的说法：在C++里，是系统在做垃圾回收；而在Java里，是Java自身在做。</p>
		<p>    在C++里，释放内存是手动处理的，要用delete运算符来释放分配的内存。这是流行的说法。确切地说，是应用认为不需要某实体时，就需用delete告诉系统，可以回收这块空间了。这个要求，对编码者来说，是件很麻烦、很难做到的事。随便上哪个BBS，在C/C++版块里总是有一大堆关于内存泄漏的话题。</p>
		<p>    Java采用一种不同的，很方便的方法：Garbage Collection.垃圾回收机制放在JVM里。JVM完全负责垃圾回收事宜，应用只在需要时申请空间，而在抛弃对象时不必关心空间回收问题。</p>
		<p>
				<strong>    二。对象在啥时被丢弃？</strong>
		</p>
		<p>    在C++里，当对象离开其作用域时，该对象即被应用抛弃。</p>
		<p>    是对象的生命期不再与其作用域有关，而仅仅与引用有关。</p>
		<p>    Java的垃圾回收机制一般包含近十种算法。对这些算法中的多数，我们不必予以关心。只有其中最简单的一个：引用计数法，与编码有关。</p>
		<p>    一个对象，可以有一个或多个引用变量指向它。当一个对象不再有任何一个引用变量指向它时，这个对象就被应用抛弃了。或者说，这个对象可以被垃圾回收机制回收了。</p>
		<p>    这就是说，当不存在对某对象的任何引用时，就意味着，应用告诉JVM：我不要这个对象，你可以回收了。</p>
		<p>    JVM的垃圾回收机制对堆空间做实时检测。当发现某对象的引用计数为0时，就将该对象列入待回收列表中。但是，并不是马上予以销毁。</p>
		<p> <strong>   三。丢弃就被回收？</strong></p>
		<p>    该对象被认定为没有存在的必要了，那么它所占用的内存就可以被释放。被回收的内存可以用于后续的再分配。</p>
		<p>    但是，并不是对象被抛弃后当即被回收的。JVM进程做空间回收有较大的系统开销。如果每当某应用进程丢弃一个对象，就立即回收它的空间，势必会使整个系统的运转效率非常低下。</p>
		<p>    前面说过，JVM的垃圾回收机制有多个算法。除了引用计数法是用来判断对象是否已被抛弃外，其它算法是用来确定何时及如何做回收。JVM的垃圾回收机制要在时间和空间之间做个平衡。</p>
		<p>    因此，为了提高系统效率，垃圾回收器通常只在满足两个条件时才运行：即有对象要回收且系统需要回收。切记垃圾回收要占用时间，因此，Java运行时系统只在需要的时候才使用它。因此你无法知道垃圾回收发生的精确时间。</p>
		<p> <strong>   四。没有引用变量指向的对象有用吗？</strong></p>
		<p>    前面说了，没挂上引用变量的对象是被应用丢弃的，这意味着，它在堆空间里是个垃圾，随时可能被JVM回收。</p>
		<p>    不过，这里有个不是例外的例外。对于一次性使用的对象（有些书称之为临时对象），可以不用引用变量指向它。举个最简单也最常见的例子：</p>
		<p>    System.out.println（“I am Java！”）；</p>
		<p>    就是创建了一个字符串对象后，直接传递给println（）方法。</p>
		<p>
				<strong>    五。应用能干预垃圾回收吗？</strong>
		</p>
		<p>    许多人对Java的垃圾回收不放心，希望在应用代码里控制JVM的垃圾回收运作。这是不可能的事。对垃圾回收机制来说，应用只有两个途径发消息给JVM.第一个前面已经说了，就是将指向某对象的所有引用变量全部移走。这就相当于向JVM发了一个消息：这个对象不要了。第二个是调用库方法System.gc（），多数书里说调用它让Java做垃圾回收。</p>
		<p>    第一个是一个告知，而调用System.gc（）也仅仅是一个请求。JVM接受这个消息后，并不是立即做垃圾回收，而只是对几个垃圾回收算法做了加权，使垃圾回收操作容易发生，或提早发生，或回收较多而已。</p>
		<p>    希望JVM及时回收垃圾，是一种需求。其实，还有相反的一种需要：在某段时间内最好不要回收垃圾。要求运行速度最快的实时系统，特别是嵌入式系统，往往希望如此。</p>
		<p>    Java的垃圾回收机制是为所有Java应用进程服务的，而不是为某个特定的进程服务的。因此，任何一个进程都不能命令垃圾回收机制做什么、怎么做或做多少。</p>
		<p>
				<strong>    六。对象被回收时要做的事</strong>
		</p>
		<p>    一个对象在运行时，可能会有一些东西与其关连。因此，当对象即将被销毁时，有时需要做一些善后工作。可以把这些操作写在finalize（）方法（常称之为终止器）里。</p>
		<p>    protected void finalize（）</p>
		<p>    {</p>
		<p>    // finalization code here</p>
		<p>    }</p>
		<p>    这个终止器的用途类似于C++里的析构函数，而且都是自动调用的。但是，两者的调用时机不一样，使两者的表现行为有重大区别。C++的析构函数总是当对象离开作用域时被调用。这就是说，C++析构函数的调用时机是确定的，且是可被应用判知的。但是，Java终止器却是在对象被销毁时。由上所知，被丢弃的对象何时被销毁，应用是无法获知的。而且，对于大多数场合，被丢弃对象在应用终止后仍未销毁。</p>
		<p>    在编码时，考虑到这一点。譬如，某对象在运作时打开了某个文件，在对象被丢弃时不关闭它，而是把文件关闭语句写在终止器里。这样做对文件操作会造成问题。如果文件是独占打开的，则其它对象将无法访问这个文件。如果文件是共享打开的，则另一访问该文件的对象直至应用终结仍不能读到被丢弃对象写入该文件的新内容。</p>
		<p>    至少对于文件操作，编码者应认清Java终止器与C++析构函数之间的差异。</p>
		<p>    那么，当应用终止，会不会执行应用中的所有finalize（）呢？据Bruce Eckel在Thinking in Java里的观点：“到程序结束的时候，并非所有收尾模块都会得到调用”。这还仅仅是指应用正常终止的场合，非正常终止呢？</p>
		<p>    因此，哪些收尾操作可以放在finalize（）里，是需要酌酎的。</p>
<img src ="http://www.blogjava.net/xixidabao/aggbug/69776.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-09-15 00:56 <a href="http://www.blogjava.net/xixidabao/archive/2006/09/15/69776.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎样取得class文件的路径</title><link>http://www.blogjava.net/xixidabao/archive/2006/09/15/69775.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Thu, 14 Sep 2006 16:52:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/09/15/69775.html</guid><description><![CDATA[
		<table cellspacing="0" cellpadding="0" align="left" border="0">
				<tbody>
						<tr>
								<td>
										<span id="leftAD">
												<div id="BannerZoneAD_Div11" style="Z-INDEX: 1; VISIBILITY: visible; WIDTH: 360px; HEIGHT: 300px">
														<object id="AD_11" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" height="300" width="360" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="AD_11">
																<param name="_cx" value="9525" />
																<param name="_cy" value="7938" />
																<param name="FlashVars" value="" />
																<param name="Movie" value="http://www.chinaitlab.com/cms/images/ad/360-300green.swf" />
																<param name="Src" value="http://www.chinaitlab.com/cms/images/ad/360-300green.swf" />
																<param name="WMode" value="Window" />
																<param name="Play" value="-1" />
																<param name="Loop" value="-1" />
																<param name="Quality" value="AutoHigh" />
																<param name="SAlign" value="" />
																<param name="Menu" value="-1" />
																<param name="Base" value="" />
																<param name="AllowScriptAccess" value="" />
																<param name="Scale" value="ShowAll" />
																<param name="DeviceFont" value="0" />
																<param name="EmbedMovie" value="0" />
																<param name="BGColor" value="" />
																<param name="SWRemote" value="" />
																<param name="MovieData" value="" />
																<param name="SeamlessTabbing" value="1" />
																<param name="Profile" value="0" />
																<param name="ProfileAddress" value="" />
																<param name="ProfilePort" value="0" />
																<embed name="AD_11" id="AD_11" width="360" height="300" src="http://www.chinaitlab.com/cms/images/ad/360-300green.swf" quality="autohigh" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash">
																</embed>
														</object>
												</div>
												<script src="/adjs/ADCount.asp?Action=View&amp;ADID=11">
												</script>
										</span>
								</td>
						</tr>
				</tbody>
		</table>    在我们的应用中,很多时候我们需要得到我们加载的类文件的路径,那么我们如何得到呢?<br />    在此我使用了一种方法,也许可以给需要的朋友们提供一种参考.<br />    看我的一个例子代码:<br />    package org.zy.base;<br />    <br />    import java.io.File;<br />    <br />    public class FileDemo {<br />      public FileDemo() {<br />        super();<br />      }<br />    <br />      //----------------------------------------<br />    <br />      public String getFileDirectory() {<br />    <br />        return null;<br />      }<br />    <br />      public String getCurrentWorkingDirectory() {<br />        File f = new File(".");<br />        return f.getAbsolutePath();<br />      }<br />    <br />      public String getClassDirectory() {<br />        String cla = this.getClass().getClassLoader().getResource(".").getPath();<br />        return cla;<br />      }<br />    <br />      //----------------------------------------------------------------------------<br />    <br />      public static void main(String[] args) {<br />        FileDemo filedemo = new FileDemo();<br />        System.out.println(filedemo.getCurrentWorkingDirectory());<br />        System.out.println(filedemo.getClassDirectory());<br />      }<br />    }<br />    <br />    运行的结果是:<br />    G:\develop\demo\JSFDemo\.<br />    /G:/develop/demo/JSFDemo/classes/<br />    由此,我们看到,我们可以通过类加载器来取得其在加载类时的类路径.<br />    getCurrentWorkingDirectory()方法得到市当前的工程的工作目录.<br />    以上是我的浅见,以供参考. <img src ="http://www.blogjava.net/xixidabao/aggbug/69775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-09-15 00:52 <a href="http://www.blogjava.net/xixidabao/archive/2006/09/15/69775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机类装载的原理及实现</title><link>http://www.blogjava.net/xixidabao/archive/2006/09/15/69774.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Thu, 14 Sep 2006 16:45:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/09/15/69774.html</guid><description><![CDATA[　<strong>一、引言</strong><br /><br />　　Java虚拟机(JVM)的类装载就是指将包含在类文件中的字节码装载到JVM中, 并使其成为JVM一部分的过程。JVM的类动态装载技术能够在运行时刻动态地加载或者替换系统的某些功能模块, 而不影响系统其他功能模块的正常运行。本文将分析JVM中的类装载系统，探讨JVM中类装载的原理、实现以及应用。<br />　　<br /><strong>   二、Java虚拟机的类装载实现与应用</strong><br /><br />　　2.1 装载过程简介<br /><br />　　所谓装载就是寻找一个类或是一个接口的二进制形式并用该二进制形式来构造代表这个类或是这个接口的class对象的过程，其中类或接口的名称是给定了的。当然名称也可以通过计算得到，但是更常见的是通过搜索源代码经过编译器编译后所得到的二进制形式来构造。<br /><br />　　在Java中，类装载器把一个类装入Java虚拟机中，要经过三个步骤来完成：装载、链接和初始化，其中链接又可以分成校验、准备和解析三步，除了解析外，其它步骤是严格按照顺序完成的，各个步骤的主要工作如下：<br /><br />　　装载：查找和导入类或接口的二进制数据； <br /><br />　　链接：执行下面的校验、准备和解析步骤，其中解析步骤是可以选择的； <br /><br />　　校验：检查导入类或接口的二进制数据的正确性； <br /><br />　　准备：给类的静态变量分配并初始化存储空间； <br /><br />　　解析：将符号引用转成直接引用； <br /><br />　　初始化：激活类的静态变量的初始化Java代码和静态Java代码块。 <br /><br />　　至于在类装载和虚拟机启动的过程中的具体细节和可能会抛出的错误，请参看《<a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html" target="_blank"><font color="#000000">Java虚拟机规范</font></a>》以及《<a href="http://www.artima.com/insidejvm/ed2/index.html" target="_blank"><font color="#000000">深入Java虚拟机</font></a>》。 由于本文的讨论重点不在此就不再多叙述。<br /><br />　　2.2 装载的实现<br /><br />　　JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。<br /><br />　　在Java中，ClassLoader是一个抽象类，它在包java.lang中,可以这样说，只要了解了在ClassLoader中的一些重要的方法，再结合上面所介绍的JVM中类装载的具体的过程，对动态装载类这项技术就有了一个比较大概的掌握，这些重要的方法包括以下几个:<br /><br />　　①loadCass方法 loadClass(String name ,boolean resolve)其中name参数指定了JVM需要的类的名称,该名称以包表示法表示,如Java.lang.Object；resolve参数告诉方法是否需要解析类，在初始化类之前,应考虑类解析，并不是所有的类都需要解析，如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要解析。这个方法是ClassLoader 的入口点。<br /><br />　　②defineClass方法 这个方法接受类文件的字节数组并把它转换成Class对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。<br /><br />　　③findSystemClass方法 findSystemClass方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass将字节数组转换成Class对象,以将该文件转换成类。当运行Java应用程序时,这是JVM 正常装入类的缺省机制。<br /><br />　　④resolveClass方法 resolveClass(Class c)方法解析装入的类,如果该类已经被解析过那么将不做处理。当调用loadClass方法时,通过它的resolve 参数决定是否要进行解析。<br /><br />　　⑤findLoadedClass方法 当调用loadClass方法装入类时,调用findLoadedClass 方法来查看ClassLoader是否已装入这个类,如果已装入,那么返回Class对象,否则返回NULL。如果强行装载已存在的类,将会抛出链接错误。<br />　<br />2.3 装载的应用<br /><br />　　一般来说，我们使用虚拟机的类装载时需要继承抽象类java.lang.ClassLoader,其中必须实现的方法是loadClass()，对于这个方法需要实现如下操作:(1) 确认类的名称;(2) 检查请求要装载的类是否已经被装载;(3) 检查请求加载的类是否是系统类;(4) 尝试从类装载器的存储区获取所请求的类;(5) 在虚拟机中定义所请求的类;(6) 解析所请求的类;(7) 返回所请求的类。<br /><br />　　所有的Java 虚拟机都包括一个内置的类装载器，这个内置的类库装载器被称为根装载器(bootstrap ClassLoader)。根装载器的特殊之处是它只能够装载在设计时刻已知的类,因此虚拟机假定由根装载器所装载的类都是安全的、可信任的,可以不经过安全认证而直接运行。当应用程序需要加载并不是设计时就知道的类时,必须使用用户自定义的装载器(user-defined ClassLoader)。下面我们举例说明它的应用。<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>public abstract class MultiClassLoader extends ClassLoader{<br />　...<br />　public synchronized Class loadClass(String s, boolean flag)<br />　　throws ClassNotFoundException<br />　　{<br />　　　/* 检查类s是否已经在本地内存*/<br />　　　Class class1 = (Class)classes.get(s);<br /><br />　　　/* 类s已经在本地内存*/<br />　　　if(class1 != null) return class1; <br />　　　try/*用默认的ClassLoader 装入类*/ {<br />　　　　class1 = super.findSystemClass(s);<br />　　　　return class1;<br />　　　}<br />　　　catch(ClassNotFoundException _ex) {<br />　　　　System.out.println("&gt;&gt; Not a system class.");<br />　　　}<br /><br />　　　/* 取得类s的字节数组*/<br />　　　byte abyte0[] = loadClassBytes(s);<br />　　　if(abyte0 == null) throw new ClassNotFoundException();<br />　　　/* 将类字节数组转换为类*/<br />　　　class1 = defineClass(null, abyte0, 0, abyte0.length);<br />　　　if(class1 == null) throw new ClassFormatError();<br />　　　if(flag) resolveClass(class1); /*解析类*/<br />　　　/* 将新加载的类放入本地内存*/<br />　　　classes.put(s, class1);<br />　　　System.out.println("&gt;&gt; Returning newly loaded class.");<br /><br />　　　/* 返回已装载、解析的类*/<br />　　　return class1;<br />　　}<br />　　...<br />}</td></tr></tbody></table><br /><span class="f14">　　<strong>三、Java虚拟机的类装载原理</strong><br /><br />　　前面我们已经知道，一个Java应用程序使用两种类型的类装载器：根装载器(bootstrap)和用户定义的装载器(user-defined)。根装载器是Java虚拟机实现的一部分，举个例子来说，如果一个Java虚拟机是在现在已经存在并且正在被使用的操作系统的顶部用C程序来实现的，那么根装载器将是那些C程序的一部分。根装载器以某种默认的方式将类装入，包括那些Java API的类。在运行期间一个Java程序能安装用户自己定义的类装载器。根装载器是虚拟机固有的一部分，而用户定义的类装载器则不是，它是用Java语言写的，被编译成class文件之后然后再被装入到虚拟机，并像其它的任何对象一样可以被实例化。 Java类装载器的体系结构如下所示：<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img onerror="this.src='http://www.yesky.com/image20010518/159568.gif';" hspace="3" src="http://www.yesky.com/image20010518/159568.gif" align="center" vspace="1" border="1" /><br />图1 Java的类装载的体系结构</div></td></tr></tbody></table><br />　　Java的类装载模型是一种代理(delegation)模型。当JVM 要求类装载器CL(ClassLoader)装载一个类时,CL首先将这个类装载请求转发给他的父装载器。只有当父装载器没有装载并无法装载这个类时,CL才获得装载这个类的机会。这样, 所有类装载器的代理关系构成了一种树状的关系。树的根是类的根装载器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根装载器以外的类装载器有且仅有一个父装载器。在创建一个装载器时, 如果没有显式地给出父装载器, 那么JVM将默认系统装载器为其父装载器。Java的基本类装载器代理结构如图2所示：<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img onerror="this.src='http://www.yesky.com/image20010518/159569.gif';" hspace="3" src="http://www.yesky.com/image20010518/159569.gif" align="center" vspace="1" border="1" /><br />图2 Java类装载的代理结构</div></td></tr></tbody></table><br />　　下面针对各种类装载器分别进行详细的说明。 <br /><br />　　根(Bootstrap) 装载器:该装载器没有父装载器，它是JVM实现的一部分，从sun.boot.class.path装载运行时库的核心代码。 <br /><br />　　扩展(Extension) 装载器:继承的父装载器为根装载器，不像根装载器可能与运行时的操作系统有关，这个类装载器是用纯Java代码实现的，它从java.ext.dirs (扩展目录)中装载代码。 <br /><br />　　系统(System or Application) 装载器:装载器为扩展装载器，我们都知道在安装JDK的时候要设置环境变量(CLASSPATH )，这个类装载器就是从java.class.path(CLASSPATH 环境变量)中装载代码的，它也是用纯Java代码实现的，同时还是用户自定义类装载器的缺省父装载器。 <br /><br />　　小应用程序(Applet) 装载器: 装载器为系统装载器，它从用户指定的网络上的特定目录装载小应用程序代码。 <br /><br />　　在设计一个类装载器的时候，应该满足以下两个条件：<br /><br />　　对于相同的类名，类装载器所返回的对象应该是同一个类对象 <br /><br />　　如果类装载器CL1将装载类C的请求转给类装载器CL2，那么对于以下的类或接口,CL1和CL2应该返回同一个类对象:a)S为C的直接超类;b)S为C的直接超接口;c)S为C的成员变量的类型;d)S为C的成员方法或构建器的参数类型;e)S为C的成员方法的返回类型。 <br /><br />　　每个已经装载到JVM中的类都隐式含有装载它的类装载器的信息。类方法getClassLoader 可以得到装载这个类的类装载器。一个类装载器认识的类包括它的父装载器认识的类和它自己装载的类，可见类装载器认识的类是它自己装载的类的超集。注意我们可以得到类装载器的有关的信息，但是已经装载到JVM中的类是不能更改它的类装载器的。 <br /><br />　　Java中的类的装载过程也就是代理装载的过程。比如:Web浏览器中的JVM需要装载一个小应用程序TestApplet。JVM调用小应用程序装载器ACL(Applet ClassLoader)来完成装载。ACL首先请求它的父装载器, 即系统装载器装载TestApplet是否装载了这个类, 由于TestApplet不在系统装载器的装载路径中, 所以系统装载器没有找到这个类, 也就没有装载成功。接着ACL自己装载TestApplet。ACL通过网络成功地找到了TestApplet.class 文件并将它导入到了JVM中。在装载过程中, JVM发现TestAppet是从超类java.applet.Applet继承的。所以JVM再次调用ACL来装载java.applet.Applet类。ACL又再次按上面的顺序装载Applet类, 结果ACL发现他的父装载器已经装载了这个类, 所以ACL就直接将这个已经装载的类返回给了JVM , 完成了Applet类的装载。接下来,Applet类的超类也一样处理。最后, TestApplet及所有有关的类都装载到了JVM中。<br /><br />　　<strong>四、结论</strong><br /><br />　　类的动态装载机制是JVM的一项核心技术, 也是容易被忽视而引起很多误解的地方。本文介绍了JVM中类装载的原理、实现以及应用，尤其分析了ClassLoader的结构、用途以及如何利用自定义的ClassLoader装载并执行Java类，希望能使读者对JVM中的类装载有一个比较深入的理解。</span><br /><br /><img src ="http://www.blogjava.net/xixidabao/aggbug/69774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-09-15 00:45 <a href="http://www.blogjava.net/xixidabao/archive/2006/09/15/69774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在 Java 应用程序中访问USB设备</title><link>http://www.blogjava.net/xixidabao/archive/2006/06/18/53628.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 18 Jun 2006 12:42:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/06/18/53628.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &lt;				p				&gt;				Java 平台一直都以其平台无关性自豪。虽然这种无关性有许多好处，但是它也使得编写与硬件交互的               Java 应用程序的过程变得相当复杂。在本文中，研究科学家蒋清野讨论了两个项目，它们通过提供使Java 应用程序可以使用 USB               设备的 API 而使这个过程变得更容易。虽然这两个项目仍然处于萌...&nbsp;&nbsp;<a href='http://www.blogjava.net/xixidabao/archive/2006/06/18/53628.html'>阅读全文</a><img src ="http://www.blogjava.net/xixidabao/aggbug/53628.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-06-18 20:42 <a href="http://www.blogjava.net/xixidabao/archive/2006/06/18/53628.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java编写TCP&amp;&amp;UDP方式的通信程序</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/27/48514.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 27 May 2006 12:55:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/27/48514.html</guid><description><![CDATA[
		<pre>Java API中封装了大量的函数,供编写网络通信程序时使用.
这使得java在网络方面具有强大的功能.
用java编写TCP方式的通信程序比较简单,但也有一些问题需要注意.

以下为监听主程序,监听程序在发现客户端连接后,启动一个会话socket线程,以实现实时发送,接收信息
和多客户端同时工作.
import java.io.*;
import java.lang.*;
import java.net.ServerSocket;
import java.net.Socket;
//主程序一直处于监听状态，有连接则启动一个线程进行处理，以实现多个客户端
public class listenserve
{
private ServerSocket ss;
private boolean listening=true;
public listenserve()
{
  Init();//初始化
  lisn();//启动监听 
}
public void Init()
{
  try
  {
   ss=new ServerSocket(10015,10);
  }
  catch(IOException ie)
  {
    System.out.println("无法在10015端口监听");
    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();
}
}

//以下为会话主程序
应该特别注意,如果客户端先关闭,会话socket中可能抛出socketexception:connection reset
这应该在程序中进行处理,这也是较易忽略的问题.
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("会话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方式发送数据！";
   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();
}
}
总的来说,以上所列代码较为简单,但已基本反映出java编写简单tcp通信程序的原理.
希望各位朋友批评.大家共同学习交流.

</pre>
		<pre>什么是UDP协议

　　UDP协议的全称是用户数据报，在网络中它与TCP协议一样用于处理数据包。在OSI模型中，在第四层——传输层，处于IP协议的上一层。UDP有不提供数据报分组、组装和不能对数据包的排序的缺点，也就是说，当报文发送之后，是无法得知其是否安全完整到达的。

　　为什么要使用UDP

　　在选择使用协议的时候，选择UDP必须要谨慎。在网络质量令人不十分满意的环境下，UDP协议数据包丢失会比较严重。但是由于UDP的特性：它不属于连接型协议，因而具有资源消耗小，处理速度快的优点，所以通常音频、视频和普通数据在传送时使用UDP较多，因为它们即使偶尔丢失一两个数据包，也不会对接收结果产生太大影响。比如我们聊天用的ICQ和OICQ就是使用的UDP协议。

　　在Java中操纵UDP

　　使用位于JDK中Java.net包下的DatagramSocket和DatagramPacket类，可以非常方便地控制用户数据报文。

　　在描述它们之前，必须了解位于同一个位置的InetAddress类。InetAddress实现了Java.io. Serializable接口，不允许继承。它用于描述和包装一个Internet IP地址，通过三个方法返回InetAddress实例：

　　getLocalhost()：返回封装本地地址的实例。

　　getAllByName(String host)：返回封装Host地址的InetAddress实例数组。

　　getByName(String host)：返回一个封装Host地址的实例。其中，Host可以是域名或者是一个合法的IP地址。

　　DatagramSocket类用于创建接收和发送UDP的Socket实例。和Socket类依赖SocketImpl类一样，DatagramSocket类的实现也依靠专门为它设计的DatagramScoketImplFactory类。DatagramSocket类有3个构建器：

　　DatagramSocket()：创建实例。这是个比较特殊的用法，通常用于客户端编程，它并没有特定监听的端口，仅仅使用一个临时的。

　　DatagramSocket(int port)：创建实例，并固定监听Port端口的报文。

　　DatagramSocket(int port, InetAddress localAddr)：这是个非常有用的构建器，当一台机器拥有多于一个IP地址的时候，由它创建的实例仅仅接收来自LocalAddr的报文。

　　值得注意的是，在创建DatagramSocket类实例时，如果端口已经被使用，会产生一个SocketException的异常抛出，并导致程序非法终止，这个异常应该注意捕获。DatagramSocket类最主要的方法有4个：

　　Receive(DatagramPacket d)：接收数据报文到d中。receive方法产生一个“阻塞”。

　　Send(DatagramPacket d)：发送报文d到目的地。

　　SetSoTimeout(int timeout)：设置超时时间，单位为毫秒。

　　Close()：关闭DatagramSocket。在应用程序退出的时候，通常会主动释放资源，关闭Socket，但是由于异常地退出可能造成资源无法回收。所以，应该在程序完成时，主动使用此方法关闭Socket，或在捕获到异常抛出后关闭Socket。

　　“阻塞”是一个专业名词，它会产生一个内部循环，使程序暂停在这个地方，直到一个条件触发。

　　DatagramPacket类用于处理报文，它将Byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成Byte数组。应用程序在产生数据包是应该注意，TCP/IP规定数据报文大小最多包含65507个，通常主机接收548个字节，但大多数平台能够支持8192字节大小的报文。DatagramPacket类的构建器共有4个：

　　DatagramPacket(byte[] buf, int length, InetAddress addr, int port)：从Buf数组中，取出Length长的数据创建数据包对象，目标是Addr地址，Port端口。

　　DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)：从Buf数组中，取出Offset开始的、Length长的数据创建数据包对象，目标是Addr地址，Port端口。

　　DatagramPacket(byte[] buf, int offset, int length)：将数据包中从Offset开始、Length长的数据装进Buf数组。

　　DatagramPacket(byte[] buf, int length)：将数据包中Length长的数据装进Buf数组。

　　DatagramPacket类最重要的方法就是getData()了，它从实例中取得报文的Byte数组编码。

　　★简单的实例说明

{接收数据的服务器}
byte[] buf = new byte[1000];
DatagramSocket ds = new DatagramSocket(12345);
//开始监视12345端口
DatagramPacket ip = new DatagramPacket(buf, buf.length);
//创建接收数据报的实例
while (true)
　　{
　　ds.receive(ip);
　　//阻塞，直到收到数据报后将数据装入IP中
　　System.out.println(new String(buf));
　　}
　　{发送数据的客户端}
　　InetAddress target = InetAddress.getByName(“www.xxx.com“);
　　//得到目标机器的地址实例
　　DatagramSocket ds = new DatagramSocket(9999);
　　//从9999端口发送数据报
　　String hello = “Hello, I am come in!”;
　　//要发送的数据
　　byte[] buf = hello.getBytes();
　　//将数据转换成Byte类型
　　op = new DatagramPacket(buf, buf.length, target, 12345);
　　//将BUF缓冲区中的数据打包
　　ds.send(op);
　　//发送数据
　　ds.close();
　　//关闭连接<pre></pre></pre>
<img src ="http://www.blogjava.net/xixidabao/aggbug/48514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-27 20:55 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/27/48514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实例分析Java SE 6.0新增功能</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/26/48346.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Fri, 26 May 2006 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/26/48346.html</guid><description><![CDATA[
		<br />　　Java 6.0标准版（Mustang）包含了大量使Java开发更为容易的特性。在本文中，我们将讨论通过部分新特性来帮助你实现如下功能：<br /><br />　　· 设置文件和目录许可权<br /><br />　　· 获取分区上自由空间和可用空间数<br /><br />　　· 把Component对象添加到JTabbedPane的选项卡上<br /><br />　　· 在你的Java基础类/Swing(JFC/Swing)应用程序中使用流行的SwingWorker类<br /><br />　　因此，如果JSR 270专家组同意采纳这些特征，那么在Mustang的下一个发行版本中你就会看到这些特征。<br /><br />　　注意：为了运行本文中的源码，你必须下载并安装Mustang的最新版本。<br /><br />　　<b>一、 设置文件和目录权限</b><br /><br />　　现在，从Mustang build 31开始，你可以在本地文件系统中设置一个文件的可读、可写和可执行标志。这项功能已经被添加到java.io.File类中，并通过使用下列方法来实现：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public boolean setReadable(boolean readable， boolean ownerOnly) <br />public boolean setReadable(boolean readable)<br />public boolean setWritable(boolean writable， boolean ownerOnly) <br />public boolean setWritable(boolean writable) <br />public boolean setExecutable(boolean executable， boolean ownerOnly) <br />public boolean setExecutable(boolean executable) </td></tr></tbody></table><br />　　如果你曾某种UNIX系统上工作过，那么你应该对这些方法非常熟悉-其实它们实现了chmod命令的一些功能。这些方法试图设置由现在的File对象所描述的文件或目录的适当权限。如果把第二个可选参数设置为true，那么该权限将仅应用于当前所有者标志。否则，这些方法将应用到所有用户。注意，如果底层文件系统没法区分该所有者和其他所有者的权限（在一些版本的Windows中就是这样），那么这一权限将应用到每一个人，而不管传递的是什么值。<br /><br />　　如果你是一个使用NT文件系统的Windows用户，那么你应该读一下这个文档，它解释了如何使用各种不同的选项来控制不同用户的文件存取权限问题。<br /><br />　　如你所想，如果用户没有权限来改变这个抽象路径名的存取权限，那么第一个方法就会失败（也就是说，返回false）；而且，这些方法也会抛出一个java.lang.SecurityException异常-如果存在一个Java安全管理器并且它的checkRead()/checkWrite()/checkExecute()方法不允许存取该文件的话。<br /><br />　　下表1显示了在多种文件系统上运行这些命令的典型结果，以及这些命令在不同目标操作系统上的可用性。<br /><br />　　表1.在常用OS文件系统上的java.io.File权限操作<br /><br /><table cellspacing="0" cellpadding="0" width="92%" align="center" border="1"><tbody><tr><td>命令</td><td>在Windows XP系统上的返回值</td><td>在Linux系统上的返回值</td><td>在solaris系统上的返回值</td></tr><tr><td>setReadable(true)</td><td>true </td><td>True（等价于chmod+r）</td><td>True（等价于chmod+r）</td></tr><tr><td>setReadable(false)</td><td>False（在Windows中文件可读性不能被设置为False）</td><td>True（等价于chmod-r）</td><td>True（等价于chmod-r）</td></tr><tr><td>setWritable(true)</td><td>True（切换Windows的只读文件属性）</td><td>True（等价于chmod+w）</td><td>True（等价于chmod+w）</td></tr><tr><td>setWritable(false) </td><td>true（切换Windows的只读文件属性）</td><td>True（等价于chmod-w）</td><td>True（等价于chmod-w）</td></tr><tr><td>setExecutable(true)</td><td>true</td><td>True（等价于chmod+x）</td><td>True（等价于chmod+x）</td></tr><tr><td>setExecutable(false)</td><td>false（在Windows中文件可执行属性不能被设置为False）</td><td>True（等价于chmod-x）</td><td>True（等价于chmod-x）</td></tr></tbody></table><br />　　决定是否文件是可读，可写或可执行的方法与这个平台的前一个版本-Java 2平台，标准版(J2SE)5.0-保持一致。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public boolean canRead();<br />public boolean canWrite();<br />public boolean canExecute();</td></tr></tbody></table><br />　　<b>二、 取得硬盘分配空间</b><br /><br />　　除了允许你设置文件和目录权限外，Mustang还为你提供了三个新方法来决定当前磁盘分区中的可用空间数，这是由一个java.io.File对象来描述的：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public long getTotalSpace();<br />public long getFreeSpace();<br />public long getUsableSpace();</td></tr></tbody></table><br />　　每一个这些方法返回要求的由java.io.File所描述的分区的字节大小，否则，如果从File对象中无法取得一个分区则返回值为0L。<br /><br />　　借助于getFreeSpace()和getUsableSpace()方法，未分配字节的返回数是（根据有关文档）："这仅是一种提示而不是保证-有可能使用大多数或所有这些字节；但紧跟这个调用之后的未分配的字节数很可能是准确的，当然也有可能因某些外部I/O操作（包括在该虚拟机外面所作的系统调用）而导致不准确。"<br /><br />　　那么，在这个两个方法之间有什么区别呢？getFreeSpace()方法返回分区的<a href="http://www.sogou.com/sogoupedia?query=自由空间" target="_blank">自由空间</a>数量的一个即时数。而getUsableSpace()方法还包含了另外一些功能来检查写许可和其它操作系统限制，这将返回一个可用空间数的更好的估计值。如果你想决定在写向一个文件之前是否你有足够的磁盘空间，那么，典型情况下getUsableSpace()将给你一个更精确的估计值。注意，如果安装了一个安全管理器并且它不允许对于RuntimePermission("getFileSystemAttributes")进行调用，那么这两个方法都将抛出一个SecurityException异常。<br /><br /><br />　<b>三、 使用Component描述JTabbedPane中的选项卡</b><br /><br />　　这是Swing的JtabbedPane中的一处微妙但是很有价值的改进。在以前的JtabbedPane中，你被限制仅用一个字符串，一个图标（或二者的结合）来描述一个选项卡。另外，如果你想的话，你还可以把一个提示小窗加到该选项卡上去。从Mustang的build 39开始，现在有可能使用一个Component来描述JtabbedPane中的一个选项卡。尽管这可能带来一系列的问题，但是，这种特性的最常用的方式是：添加一个Close按钮-它将从JTabbedPane中删除该选项卡。<br /><br />　　Sun程序员和Swing工程师Alexander Potochkin在他的最近的一个有关这个主题的博客日志中指出，这三个新方法已经被添加到JTabbedPane。<br /><br />　　你可以使用下列方法把Component设置为一个选项卡：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public void setTabComponentAt(int index， Component component)</td></tr></tbody></table><br />　　你可以使用下列方法得到这个组件： <br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public Component getTabComponentAt(int index)</td></tr></tbody></table><br />　　你可以使用下列这个方法来测试是否有组件被应用于这个JtabbedPane中：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public int indexOfTabComponent(Component tabComponent)</td></tr></tbody></table><br />　　下面是一个选项卡面板示例源代码-它允许你从一个JTabbedPane中动态地添加和删除选项卡。注意，在这个例子中我们创建了一个Jpanel，它包含两个组件：一个位于面板左边(BorderLayout.WEST)的JLabel和一个位于面板右边（BorderLayout.EAST）的带有一个ImageIcon的按钮。这里所用的图形是一个10x10像素大小的gif文件-它包含了一个小X。为了确保按钮的尺寸小一些，我们把它的尺寸重置为图标的宽度和高度各自加上2个像素。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>import java.awt.*;<br />import java.awt.event.*;<br />import javax.swing.*;<br />public class TabbedPaneExample implements ActionListener {<br />　private JFrame frame;<br />　private JTabbedPane tabbedPane;<br />　private JButton addTabButton;<br />　private ImageIcon closeXIcon;<br />　private Dimension closeButtonSize;<br />　private int tabCounter = 0;<br />　public TabbedPaneExample() {<br />　　//创建选项卡面板<br />　　tabbedPane = new JTabbedPane();<br />　　//创建一个按钮-用户可用来添加一个选项卡到选项卡面板<br />　　addTabButton = new JButton("Add Tab");<br />　　addTabButton.addActionListener(this);<br />　　//创建一个框架来包含这个选项卡面板<br />　　frame = new JFrame();<br />　　//创建一个图像图标'X'以实现在每一个选项卡上的关闭功能。加载的gif是一个10x10图形（非黑色部分是透明的）<br />　　closeXIcon = new ImageIcon("C:/CloseX.gif");<br />　　//创建一个Dimension用来调整close按钮的大小<br />　　closeButtonSize = new Dimension(<br />　　　closeXIcon.getIconWidth()+2，<br />　　　closeXIcon.getIconHeight()+2);<br />　　　//所选项卡面板添加到图形中央，把"Add Tab"按钮置于南面。然后包装它，调整其大小并显示它。<br />　　frame.add(tabbedPane， BorderLayout.CENTER);<br />　　frame.add(addTabButton， BorderLayout.SOUTH);<br />　　frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />　　frame.pack();<br />　　frame.setMinimumSize(new Dimension(300， 300));<br />　　frame.setVisible(true);<br />　}<br />　public void actionPerformed(ActionEvent e) {<br />　　final JPanel content = new JPanel();<br />　　//创建一个描述该选项卡的面板并确保它是透明的<br />　　JPanel tab = new JPanel();<br />　　tab.setOpaque(false);<br />　　//为该选项卡创建一个标签和一个Close按钮。一定要<br />　　//把它的尺寸设置为几乎该图标的大小，并且<br />　　//创建一个行为听取器-它将定位该选项卡并且从选项卡面板上删除它<br />　　JLabel tabLabel = new JLabel("Tab " + (++tabCounter));<br />　　JButton tabCloseButton = new JButton(closeXIcon);<br />　　tabCloseButton.setPreferredSize(closeButtonSize);<br />　　tabCloseButton.addActionListener(new ActionListener() {<br />　　　public void actionPerformed(ActionEvent e) {<br />　　　　int closeTabNumber = tabbedPane.indexOfComponent(content);<br />　　　　tabbedPane.removeTabAt(closeTabNumber);<br />　　　}<br />　　});<br />　　tab.add(tabLabel， BorderLayout.WEST);<br />　　tab.add(tabCloseButton， BorderLayout.EAST);<br />　　//把该选项卡添加到选项卡面板。注意，<br />　　//第一个参数（它正常是一个描述选项卡标题的String<br />　　//），为null.<br />　　tabbedPane.addTab(null， content);<br />　　//不是在选项卡上使用String/Icon的结合，<br />　　//而是使用我们的面板。<br />　　tabbedPane.setTabComponentAt(tabbedPane.getTabCount()-1， tab);<br />　}<br />　public static void main(String[] args) {<br />　　TabbedPaneExample main = new TabbedPaneExample();<br />　}<br />}</td></tr></tbody></table><br />　　结果显示于图1中。<br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="/imagelist/06/21/71229zb8fu40.gif" border="1" /><br />图1.一个把多个JComponent用作选项卡的JTabbedPane</div></td></tr></tbody></table><br />　　注意，Alexander Potochkin的博客中提供了另外一种不同的方法，它子类化JButton-重载paintComponent()并且画出它自己的("X")。如果你不想使用你的代码发布一个gif文件，那么使用这种更为复杂的方法是非常有用的。<br /><br /><br /><b>四、 SwingWorker现在包含到Mustang中</b><br /><br />　　大多数Swing程序员知道，无论什么时候编写事件驱动的代码，例如当一个按钮按下时调用ActionListener，都需要快速处理事件。你永远不需要花费比你必须处理事件驱动的线程更多的时间，否则，你的Swing GUI将成为不可响应并且不能有效地重绘它自己。在事件调度线程中实现一项较大的任务经常意味着你要从事件调度线程中"剔除"一个独立工作者线程并且让该线程运行于后台。这样以来，当使用Swing编写一个多线程的应用程序时，程序员需要牢记下面两条规则： <br /><br />　　· 如果把耗时的任务安排到一个调度线程中，那么这有可能导致应用程序具有不可响应性。因此，这样的任务应该用一个工作者线程来专门实现。<br /><br />　　· 对Swing组件的更新应该仅安排给一个事件调度线程来完成。<br /><br />　　因为这意味着，至少有两个线程在同时运行，因此创建一个处理线程间通讯的类是很有帮助的。有一个消息是，最新的Mustang发行版本中加入了对SwingWorker类（它是前一段时间Swing程序员使用的一种流行的解决方案）的支持。<br /><br />　　下面是来自于Javadoc的SwingWorker的正式声明。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public abstract class SwingWorker&lt;T，V&gt; extends Object<br />implements RunnableFuture&lt;T&gt;</td></tr></tbody></table><br />　　注意，这里的声明包含了两个泛型类型变量：T和V。如果你还不熟悉泛型类型变量的话，那么你可以先读一下有关泛型的基础知识，这是在J2SE 5.0中引入的一种特性。下面是定义：<br /><br />　　· T：由这个SwingWorker的doInBackground()和get()方法返回的结果类型<br /><br />　　· V：被这个SwingWorker的publish()和process()方法用来执行中间结果的类型<br /><br />　　一会之后，我们再讨论这些方法。然而，首先，让我们介绍一下SwingWorker中所使用的线程架构。这里援引Javadoc的描述：在一个SwingWorker的生命周期中共包含三个线程： <br /><br />　　· 当前线程：execute()方法。它调度SwingWorker在一个工作者线程上的执行并且立即返回。你可以使用两个get()方法之一来等待SwingWorker完成。<br /><br />　　· 工作者线程：这个线程上调用doInBackground()方法。这正是所有后台活动发生的地方。为了通知PropertyChangeListeners关于绑定属性的变化，你可以使用firePropertyChange和getPropertyChangeSupport()方法。默认情况下，有两个绑定属性可用-state和progress。<br /><br />　　· 事件调度线程：所有的Swing相关的活动都发生在这种线程中。SwingWorker调用process()和done()方法并且通知这个线程上的任何PropertyChangeListeners。<br /><br />　　典型地，你在其上实例化SwingWorker子类的当前线程是事件调度线程。这个SwingWorker通常响应下列一些事件：<br /><br />　　1. execute()方法被调用或运行于这个事件调度线程上。<br /><br />　　2. SwingWorker通知任何PropertyChangeListeners其状态已经变为SwingWorker.StateValue.STARTED。<br /><br />　　3. doInBackground()方法在工作者线程上执行。<br /><br />　　4. 一旦doInBackground()方法完成，即在当前线程上执行done()方法。<br /><br />　　5. SwingWorker通知任何PropertyChangeListeners其状态已经变为SwingWorker.StateValue.DONE。<br /><br />　　当在doInBackground()方法中时，你可以设置一个整型的progress属性-它使用下列方法指示工作者线程的当前进度：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>protected void setProgress(int progress);</td></tr></tbody></table><br />　　一旦调用这个方法，SwingWorker就向所有的已经登记的听者激发一个属性事件来通知它们已经得到更新的进度值。<br /><br />　　你可以设置或添加工作者线程的最后结果-通过使用下面两个方法之一：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>protected void process(V... chunks);<br />protected void publish(V... chunks);</td></tr></tbody></table><br />　　第二个方法，publish()，将使用一些中间类型V的一些可变个数的对象并且把它们发送到process()方法中进行处理。典型情况下，你将从doInBackground()线程中调用process()方法。这个process()方法应该总是被重载以接收输入的V参数并且把这些中间对象以某种形式连接成一个T类型。当然，至于process()方法如何实现这一任务要依赖于参数类型-它在你的SwingWorker子类中指定。<br /><br />　　同时，在当前线程中，你可以调用两个get()方法之一来检索工作者线程的结果。第一个get()方法，在工作者线程完成其任务之前将会无限地阻塞。第二个方法在检索已经被处理的结果宽之前将阻塞一段指定的时间。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public T get();<br />public T get(long timeout，TimeUnit unit);</td></tr></tbody></table><br />　　如果你希望取消这个工作者线程，那么在它完成执行之前，你可以从当前线程中调用cancel()方法。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public final boolean cancel(boolean mayInterruptIfRunning)</td></tr></tbody></table><br />　　这里的mayInterruptIfRunning参数指定，在试图停止这项任务时是否执行该任务的线程应该被中断。注意，调用cancel()方法将失败-如果该任务已经完成，如果该任务已经被取消或如果它因某些理由可能无法被取消。然而，如果该方法调用返回true并且当调用cancel()方法时这项任务还没有开始，那么SwingWorker永远不应该执行。<br /><br />　　<b>五、 结论</b><br /><br />　　尽管本文中介绍的这些特征基本上相互独立，但是它们的确代表Mustang开发团队希望满足Java开发社区提出的一小部分实现请求。特别是，当创建Swing应用程序时，学习使用SwingWorker类是必须的-它可以把程序员从复杂的GUI线程问题中解脱出来。记住，和往常一样，这些特征在最终成为Java SE 6的最后发行版本之前要征得JSR 270专家组的同意。<br /><br /><img src ="http://www.blogjava.net/xixidabao/aggbug/48346.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-26 16:09 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/26/48346.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Java实现HTTP文件队列下载</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/20/47218.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 20 May 2006 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/20/47218.html</guid><description><![CDATA[
		<pre>　许多用户可能会遇到这样的情况：在网站上发现一个很好的资源，但是这个资源是分成了很多个文件存放的，如果想把它保存到本地，只有靠用户点击另存来完成保存，如果资源分了几百甚至上千上万，那简直是个灾难。 

　　在Internet上很多的资源分成多个文件存放时，它的文件命名是有一定的规则的；正因如此，我们就可以用程序来完成这个资源的完全下载。

　　1. 基础知识

　　在Internet上，我们要下载网站上的某个资源，我们会获得一个URL（Uniform Resource Locator），它是一个服务器资源定位的描述，下载的过程总是如下步骤:

　　步骤1:客户端发起连接请求一个URL 

　　步骤2:服务器解析URL，并将指定的资源返回一个输入流给客户 

　　步骤3:客户端接收输入流，将流中的内容存到文件 

　　2. 网络连接的建立

　　Java提供了对URL访问和大量的流操作的的API，我们可以很容易的完成对网络上资源的存取,下面的代码段就完成了对一个网站的资源进行访问:

......
destUrl="http://www.ebook.com/java/网络编程001.zip";
url = new URL(destUrl);
httpUrl = (HttpURLConnection) url.openConnection();
//连接指定的网络资源
httpUrl.connect();
//获取网络输入流
bis = new BufferedInputStream(httpUrl.getInputStream());
...... 

　　3. 代理的访问

　　Java 中通过代理服务器访问外网的方法已经是世人皆知的秘密了。这里就不再多描述了，访问的JAVA代码如下:

//设置代理服务器
System.getProperties().put("proxySet", "true");
System.getProperties().put("proxyHost", "10.154.134.110");
System.getProperties().put("proxyPort", "8080"); 

　　4. 网络资源的保存

　　在上节中，我们已经获取了指定网络资源的输入流，接下来我们要完成的就是读取输入流中的所以内容，并将其保存在文件中。示例代码:

......
fos = new FileOutputStream(fileName);
if (this.DEBUG) 
System.out.println("正在获取链接[" + destUrl + "]的内容...\n将其保存为文件[" + fileName +"]");

//保存文件
while ( (size = bis.read(buf)) != -1)
fos.write(buf, 0, size);
...... 

　　上面的示例代码就将网络资源的内容保存到了本地指定的文件中。

　　5. 代码清单

import java.io.*;
import java.net.*;
import java.util.*;

/**
* ＜p＞Title: 个人开发的API＜/p＞
* ＜p＞Description: 将指定的HTTP网络资源在本地以文件形式存放＜/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;//调试用
　private static int BUFFER_SIZE = 8096;//缓冲区大小
　private Vector vDownLoad = new Vector();//URL列表
　private Vector vFileList = new Vector();//下载后的保存文件名列表

　/**
　* 构造方法
　*/
　public HttpGet() {}

　/**
　* 清除下载列表
　*/
　public void resetList() {
　　vDownLoad.clear();
　　vFileList.clear();
　}

　/**
　* 增加下载列表项
　*
　* @param url String
　* @param filename String
　*/

public void addItem(String url, String filename) {
　vDownLoad.add(url);
　vFileList.add(filename);
}

　/**
　* 根据列表下载资源
　*/
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();
　//获取网络输入流
　bis = new BufferedInputStream(httpUrl.getInputStream());
　//建立文件
　fos = new FileOutputStream(fileName);

　if (this.DEBUG) 
　　System.out.println("正在获取链接[" + destUrl + "]的内容...\n将其保存为文件[" + fileName + "]");

　//保存文件
　while ( (size = bis.read(buf)) != -1) 
　　fos.write(buf, 0, size);

　fos.close();
　bis.close();
　httpUrl.disconnect();
}

/**
* 设置代理服务器
*
* @param proxy String
* @param proxyPort String
*/
public void setProxyServer(String proxy, String proxyPort) {
　//设置代理服务器 
　System.getProperties().put("proxySet", "true");
　System.getProperties().put("proxyHost", proxy);
　System.getProperties().put("proxyPort", proxyPort);
}

/**
* 设置认证用户名与密码
*
* @param uid String
* @param pwd String
*/
public void setAuthenticator(String uid, String pwd) {
Authenticator.setDefault(new MyAuthenticator(uid, pwd));
}

/**
* 主方法(用于测试)
*
* @param argv String[]
*/
public static void main(String argv[]) {
　HttpGet oInstance = new HttpGet();
　try {
　　//增加下载列表（此处用户可以写入自己代码来增加下载列表）
　　oInstance.addItem("http://www.ebook.com/java/网络编程001.zip","./网络编程1.zip");
　　oInstance.addItem("http://www.ebook.com/java/网络编程002.zip","./网络编程2.zip");
　　oInstance.addItem("http://www.ebook.com/java/网络编程003.zip","./网络编程3.zip");
　　oInstance.addItem("http://www.ebook.com/java/网络编程004.zip","./网络编程4.zip");
　　oInstance.addItem("http://www.ebook.com/java/网络编程005.zip","./网络编程5.zip");
　　oInstance.addItem("http://www.ebook.com/java/网络编程006.zip","./网络编程6.zip");
　　oInstance.addItem("http://www.ebook.com/java/网络编程007.zip","./网络编程7.zip");
　　//开始下载
　　oInstance.downLoadByList();
　}
　catch (Exception err) {
　　System.out.println(err.getMessage());
　}
}
}

</pre>
<img src ="http://www.blogjava.net/xixidabao/aggbug/47218.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-20 21:17 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/20/47218.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java程序类加载完全揭密</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/20/47160.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 20 May 2006 05:08:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/20/47160.html</guid><description><![CDATA[　　版权声明：可以任意转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明<br /><br />　　类加载是java语言提供的最强大的机制之一。尽管类加载并不是讨论的热点话题，但所有的编程人员都应该了解其工作机制，明白如何做才能让其满足我们的需要。这能有效节省我们的编码时间，从不断调试ClassNotFoundException, ClassCastException的工作中解脱出来。 <br /><br />　　这篇文章从基础讲起，比如代码与数据的不同之处是什么，他们是如何构成一个实例或对象的。然后深入探讨<a class="bluekey" href="http://www.yesky.com/key/3259/8259.html" target="_blank">java虚拟机</a>（JVM）是如何利用类加载器读取代码，以及java中类加载器的主要类型。接着用一个类加载的基本算法看一下类加载器如何加载一个内部类。本文的下一节演示一段代码来说明扩展和开发属于自己的类加载器的必要性。紧接着解释如何使用定制的类加载器来完成一个一般意义上的任务，使其可以加载任意远端客户的代码，在JVM中定义，实例化并执行它。本文包括了J2EE关于类加载的规范——事实上这已经成为了J2EE的标准之一。<br /><br />　　<b>类与数据</b><br /><br />　　一个类代表要执行的代码，而数据则表示其相关状态。状态时常改变，而代码则不会。当我们将一个特定的状态与一个类相对应起来，也就意味着将一个类事例化。尽管相同的类对应的实例其状态千差万别，但其本质都对应着同一段代码。在JAVA中，一个类通常有着一个.class文件，但也有例外。在JAVA的运行时环境中（Java runtime），每一个类都有一个以第一类(first-class)的Java对象所表现出现的代码，其是java.lang.Class的实例。我们编译一个JAVA文件，编译器都会嵌入一个public, static, <a class="bluekey" href="http://www.yesky.com/key/3976/3976.html" target="_blank">final</a>修饰的类型为java.lang.Class，名称为class的域变量在其字节码文件中。因为使用了public修饰，我们可以采用如下的形式对其访问：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>java.lang.Class klass = Myclass.class;</td></tr></tbody></table><br />　　一旦一个类被载入JVM中，同一个类就不会被再次载入了（切记，同一个类）。这里存在一个问题就是什么是“同一个类”？正如一个对象有一个具体的状态，即标识，一个对象始终和其代码(类)相关联。同理，载入JVM的类也有一个具体的标识，我们接下来看。<br /><br />　　在Java中，一个类用其完全匹配类名(fully qualified class name)作为标识，这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此，如果一个名为Pg的包中，有一个名为Cl的类，被类加载器KlassLoader的一个实例kl1加载，Cl的实例，即C1.class在JVM中表示为(Cl, Pg, kl1)。这意味着两个类加载器的实例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的，被它们所加载的类也因此完全不同，互不兼容的。那么在JVM中到底有多少种类加载器的实例？下一节我们揭示答案。<br /><br />　　<b>类加载器</b><br /><br />　　在JVM中，每一个类都被java.lang.ClassLoader的一些实例来加载.类ClassLoader是在包中java.lang里，开发者可以自由地继承它并添加自己的功能来加载类。<br /><br />　　无论何时我们键入java MyMainClass来开始运行一个新的JVM，“引导类加载器(bootstrap class loader)”负责将一些关键的Java类，如java.lang.Object和其他一些运行时代码先加载进内存中。运行时的类在JRE\lib\rt.jar包文件中。因为这属于系统底层执行动作，我们无法在JAVA文档中找到引导类加载器的工作细节。基于同样的原因，引导类加载器的行为在各JVM之间也是大相径庭。<br />同理，如果我们按照如下方式：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>log(java.lang.String.class.getClassLoader());</td></tr></tbody></table><br />　　来获取java的核心运行时类的加载器，就会得到null。<br /><br />　　接下来介绍java的扩展类加载器。扩展库提供比java运行代码更多的特性，我们可以把扩展库保存在由java.ext.dirs属性提供的路径中。<br /><br />　　(编辑注：java.ext.dirs属性指的是系统属性下的一个key，所有的系统属性可以通过System.getProperties()方法获得。在编者的系统中，java.ext.dirs的value是” C:\Program Files\Java\<a class="bluekey" href="http://www.yesky.com/key/4410/4410.html" target="_blank">jdk1.5</a>.0_04\jre\lib\ext”。下面将要谈到的如java.class.path也同属系统属性的一个key。)<br /><br />　　类ExtClassLoader专门用来加载所有java.ext.dirs下的.jar文件。开发者可以通过把自己的.jar文件或库文件加入到扩展目录的<a class="bluekey" href="http://www.yesky.com/key/907/5907.html" target="_blank">classpath</a>，使其可以被扩展类加载器读取。<br /><br />　　从开发者的角度，第三种同样也是最重要的一种类加载器是AppClassLoader。这种类加载器用来读取所有的对应在java.class.path系统属性的路径下的类。<br /><br />　　Sun的java指南中，文章“理解扩展类加载”（Understanding Extension Class Loading）对以上三个类加载器路径有更详尽的解释，这是其他几个JDK中的类加载器<br /><br />　　●java.net.URLClassLoader <br /><br />　　●java.security.SecureClassLoader <br /><br />　　●java.<a class="bluekey" href="http://www.yesky.com/key/120/10120.html" target="_blank">rmi</a>.server.RMIClassLoader <br /><br />　　●sun.applet.AppletClassLoader <br /><br />　　java.lang.Thread，包含了public ClassLoader getContextClassLoader()方法，这一方法返回针对一具体线程的上下文环境类加载器。此类加载器由线程的创建者提供，以供此线程中运行的代码在需要加载类或资源时使用。如果此加载器未被建立，缺省是其父线程的上下文类加载器。原始的类加载器一般由读取应用程序的类加载器建立。<br /><br /><br /><div></div><div class="left fontsize3">类加载是java语言提供的最强大的机制之一。尽管类加载并不是讨论的热点话题，但所有的编程人员都应该了解其工作机制，明白如何做才能让其满足我们的需要。</div><div class="fontclear"></div><hr class="hr1" /><center><span id="ad9"></span></center><div class="left fontsize4"><div class="guanggao"><span id="ad3"></span></div><div class="guanggao"><span id="contentAdv"></span></div>　　<b>类加载器如何工作？</b><br /><br />　　除了引导类加载器，所有的类加载器都有一个父类加载器，不仅如此，所有的类加载器也都是java.lang.ClassLoader类型。以上两种类加载器是不同的，而且对于开发者自订制的类加载器的正常运行也至关重要。最重要的方面是正确设置父类加载器。任何类加载器，其父类加载器是加载该类加载器的类加载器实例。（记住，类加载器本身也是一个类！）<br /><br />　　使用loadClass()方法可以从类加载器中获得该类。我们可以通过java.lang.ClassLoader的源代码来了解该方法工作的细节，如下：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>protected synchronized Class&lt;?&gt; loadClass (String name, boolean resolve) throws ClassNotFoundException<br />{<br />　// First check if the class is already loaded <br />　Class c = findLoadedClass(name); <br />　if (c == null) <br />　{<br />　　try <br />　　{<br />　　　if (parent != null)<br />　　　{<br />　　　　c = parent.loadClass(name, false);<br />　　　} else {<br />　　　　c = findBootstrapClass0(name);<br />　　　}<br />　　} catch (ClassNotFoundException e) { <br />　　　// If still not found, then invoke // findClass to find the class. <br />　　　c = findClass(name);<br />　　}<br />　}<br />　if (resolve) <br />　{<br />　　resolveClass(c);<br />　}<br />　return c;<br />}</td></tr></tbody></table><br />　　我们可以使用ClassLoader的两种构造方法来设置父类加载器：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public class MyClassLoader extends ClassLoader<br />{<br />　public MyClassLoader()<br />　{<br />　　super(MyClassLoader.class.getClassLoader());<br />　}<br />}</td></tr></tbody></table><br />　　或<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public class MyClassLoader extends ClassLoader<br />{<br />　public MyClassLoader()<br />　{<br />　　super(getClass().getClassLoader());<br />　}<br />}</td></tr></tbody></table><br />　　第一种方式较为常用，因为通常不建议在构造方法里调用getClass()方法，因为对象的初始化只是在构造方法的出口处才完全完成。因此，如果父类加载器被正确建立，当要示从一个类加载器的实例获得一个类时，如果它不能找到这个类，它应该首先去访问其父类。如果父类不能找到它(即其父类也不能找不这个类，等等)，而且如果findBootstrapClass0()方法也失败了，则调用findClass()方法。findClass()方法的缺省实现会抛出ClassNotFoundException，当它们继承java.lang.ClassLoader来订制类加载器时开发者需要实现这个方法。findClass()的缺省实现方式如下：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>protected Class&lt;?&gt; findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }</td></tr></tbody></table><br />　　在findClass()方法内部，类加载器需要获取任意来源的字节码。来源可以是文件系统，URL，数据库，可以产生字节码的另一个应用程序，及其他类似的可以产生java规范的字节码的来源。你甚至可以使用BCEL (Byte Code Engineering Library：字节码工程库)，它提供了运行时创建类的捷径。BCEL已经被成功地使用在以下方面：编译器，优化器，混淆器，代码产生器及其他分析工具。一旦字节码被检索，此方法就会调用defineClass()方法，此行为对不同的类加载实例是有差异的。因此，如果两个类加载实例从同一个来源定义一个类，所定义的结果是不同的。<br /><br />　　JAVA语言规范（Java language specification）详细解释了JAVA执行引擎中的类或接口的加载（loading），链接（linking）或初始化（initialization）过程。<br /><br />　　图一显示了一个主类称为MyMainClass的应用程序。依照之前的阐述，MyMainClass.class会被AppClassLoader加载。 MyMainClass创建了两个类加载器的实例：CustomClassLoader1 和 CustomClassLoader2,他们可以从某数据源（比如网络）获取名为Target的字节码。这表示类Target的类定义不在应用程序类路径或扩展类路径。在这种情况下，如果MyMainClass想要用自定义的类加载器加载Target类，CustomClassLoader1和CustomClassLoader2会分别独立地加载并定义Target.class类。这在java中有重要的意义。如果Target类有一些静态的初始化代码，并且假设我们只希望这些代码在JVM中只执行一次，而这些代码在我们目前的步骤中会执行两次——分别被不同的CustomClassLoaders加载并执行。如果类Target被两个CustomClassLoaders加载并创建两个实例Target1和Target2，如图一显示，它们不是类型兼容的。换句话说，在JVM中无法执行以下代码：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>Target target3 = (Target) target2;</td></tr></tbody></table><br />　　以上代码会抛出一个ClassCastException。这是因为JVM把他们视为分别不同的类，因为他们被不同的类加载器所定义。这种情况当我们不是使用两个不同的类加载器CustomClassLoader1 和 CustomClassLoader2，而是使用同一个类加载器CustomClassLoader的不同实例时，也会出现同样的错误。这些会在本文后边用具体代码说明。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="566" alt="" src="/imagelist/05/10/364n0268z3zb.JPG" width="449" /><br /><br />图1. 在同一个JVM中多个类加载器加载同一个目标类</div></td></tr></tbody></table><br /><br /><br />　<b>为什么我们需要我们自己的类加载器</b><br /><br />　　原因之一为开发者写自己的类加载器来控制JVM中的类加载行为，java中的类靠其包名和类名来标识，对于实现了java.io.Serializable接口的类，serialVersionUID扮演了一个标识类版本的重要角色。这个唯一标识是一个类名、接口名、成员方法及属性等组成的一个64位的哈希字段，而且也没有其他快捷的方式来标识一个类的版本。严格说来，如果以上的都匹配，那么则属于同一个类。<br /><br />　　但是让我们思考如下情况：我们需要开发一个通用的执行引擎。可以执行实现某一特定接口的任何任务。当任务被提交到这个引擎，首先需要加载这个任务的代码。假设不同的客户对此引擎提交了不同的任务，凑巧，这些所有的任务都有一个相同的类名和包名。现在面临的问题就是这个引擎是否可以针对不同的用户所提交的信息而做出不同的反应。这一情况在下文的参考一节有可供下载的代码样例，samepath 和 differentversions，这两个目录分别演示了这一概念。 图2 显示了文件目录结构，有三个子目录samepath, differentversions, 和 differentversionspush，里边是例子：<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="461" alt="" src="/imagelist/05/10/9l33653u08js.JPG" width="446" /><br />图2. 文件夹结构组织示例</div></td></tr></tbody></table><br />　　在samepath 中，类version.Version保存在v1和v2两个子目录里，两个类具有同样的类名和包名，唯一不同的是下边这行：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public void fx(){ log("this = " + this + "; Version.fx(1)."); }</td></tr></tbody></table><br />　　V1中，日志记录中有Version.fx(1)，而在v2中则是Version.fx(2)。把这个两个存在细微不同的类放在一个classpath下，然后运行Test类：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>set CLASSPATH=.;%CURRENT_ROOT%\v1;%CURRENT_ROOT%\v2<br />%JAVA_HOME%\bin\java Test</td></tr></tbody></table><br />　　图3显示了控制台输出。我们可以看到对应着Version.fx(1)的代码被执行了，因为类加载器在classpath首先看到此版本的代码。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="193" alt="" src="/imagelist/05/10/spb0js0651u8.JPG" width="428" /><br />图3. 在类路径中samepath测试排在最前面的version 1</div></td></tr></tbody></table><br />　　再次运行，类路径做如下微小改动。 <br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>set CLASSPATH=.;%CURRENT_ROOT%\v2;%CURRENT_ROOT%\v1<br />%JAVA_HOME%\bin\java Test</td></tr></tbody></table><br />　　控制台的输出变为图4。对应着Version.fx(2)的代码被加载，因为类加载器在classpath中首先找到它的路径。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="192" alt="" src="/imagelist/05/10/wb8py2r57509.JPG" width="428" /><br />图4. 在类路径中samepath测试排在最前面的version 2</div></td></tr></tbody></table><br />　　根据以上例子可以很明显地看出，类加载器加载在类路径中被首先找到的元素。如果我们在v1和v2中删除了version.Version，做一个非version.Version形式的.jar文件，如myextension.jar，把它放到对应java.ext.dirs的路径下，再次执行后看到version.Version不再被AppClassLoader加载，而是被扩展类加载器加载。如图5所示。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="158" alt="" src="/imagelist/05/10/rm118409yo66.JPG" width="429" /><br />图5. AppClassLoader及ExtClassLoader</div></td></tr></tbody></table><br />　　继续这个例子，文件夹differentversions包含了一个RMI执行引擎，客户端可以提供给执行引擎任何实现了common.TaskIntf接口的任务。子文件夹client1 和 client2包含了类client.TaskImpl有个细微不同的两个版本。两个类的区别在以下几行：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>static<br />{<br />　log("client.TaskImpl.class.getClassLoader (v1) : " + TaskImpl.class.getClassLoader());<br />}<br /><br />public void execute(){ log("this = " + this + "; execute(1)"); }</td></tr></tbody></table><br />　　在client1和client2里分别有getClassLoader(v1) 与 execute(1)和getClassLoader(v2) 与 execute(2)的的log语句。并且，在开始执行引擎RMI服务器的代码中，我们随意地将client2的任务实现放在类路径的前面。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>CLASSPATH=%CURRENT_ROOT%\common;%CURRENT_ROOT%\server;<br />%CURRENT_ROOT%\client2;%CURRENT_ROOT%\client1<br />%JAVA_HOME%\bin\java server.Server</td></tr></tbody></table><br />　　如图6，7，8的屏幕截图，在客户端VM，各自的client.TaskImpl类被加载、实例化，并发送到服务端的VM来执行。从服务端的控制台，可以明显看到client.TaskImpl代码只被服务端的VM执行一次，这个单一的代码版本在服务端多次生成了许多实例，并执行任务。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="252" alt="" src="/imagelist/05/10/511b346uj8rr.JPG" width="429" /><br />图6. 执行引擎服务器控制台</div></td></tr></tbody></table><br />　　图6显示了服务端的控制台，加载并执行两个不同的客户端的请求，如图7、8所示。需要注意的是，代码只被加载了一次（从静态初始化块的日志中也可以明显看出），但对于客户端的调用这个方法被执行了两次。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="169" alt="" src="/imagelist/05/10/806698qpznjf.JPG" width="429" /><br />图7. 执行引擎客户端 1控制台　</div></td></tr></tbody></table><br />　　图7中，客户端VM加载了含有client.TaskImpl.class.getClassLoader(v1)的日志内容的类TaskImpl的代码，并提供给服务端的执行引擎。图8的客户端VM加载了另一个TaskImpl的代码，并发送给服务端。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img height="169" alt="" src="/imagelist/05/10/75d4v3u7sq8y.JPG" width="428" /><br />图8. 执行引擎客户端 2控制台　</div></td></tr></tbody></table><br />　　在客户端的VM中，类client.TaskImpl被分别加载，初始化，并发送到服务端执行。图6还揭示了client.TaskImpl的代码只在服务端的VM中加载了一次，但这“唯一的一次”却在服务端创造了许多实例并执行。或许客户端1该不高兴了因为并不是它的client.TaskImpl(v1)的方法调用被服务端执行了，而是其他的一些代码。如何解决这一问题？答案就是实现定制的类加载器。<br /><br /><br /></div><img src ="http://www.blogjava.net/xixidabao/aggbug/47160.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-20 13:08 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/20/47160.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 5.0泛型编程之泛型类型</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/19/47063.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Fri, 19 May 2006 07:17:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/19/47063.html</guid><description><![CDATA[　Java5.0的新特性之一是引入了泛型类型和泛型方法。一个泛型类型通过使用一个或多个类型变量来定义，并拥有一个或多个使用一个类型变量作为一个参数或者返回值的占位符。例如，类型java.util.List&lt;E&gt;是一个泛型类型：一个list，其元素的类型被占位符E描述。这个类型有一个名为add()的方法，被声明为有一个类型为E的参数，同时，有一个get()方法，返回值被声明为E类型。<br /><br />　　为了使用泛型类型，你应该为类型变量详细指明实际的类型，形成一个就像List&lt;String&gt;类似的参数化类型。[1]指明这些额外的类型信息的原因是编译器据此能够在编译期为您提供很强的类型检查，增强您的程序的类型安全性。举个例子来说，您有一个只能保持String对象的List，那么这种类型检查就能够阻止您往里面加入String[]对象。同样的，增加的类型信息使编译器能够为您做一些类型转换的事情。比如，编译器知道了一个List&lt;String&gt;有个get()方法，其返回值是一个String对象，因此您不再需要去将返回值由一个Object强制转换为String。<br /><br />　　Java.util包中的集合类在java5.0中已经被做成了泛型，也许您将会在您的程序中频繁的使用到他们。类型安全的集合类就是一个泛型类型的典型案例。即便您从没有定义过您自己的泛型类型甚至从未用过除了java.util中的集合类以外的泛型类型，类型安全的集合类的好处也是极有意义的一个标志——他们证明了这个主要的新语言特性的复杂性。<br /><br />　　我们从探索类型安全的集合类中的基本的泛型用法开始，进而研究更多使用泛型类型的复杂<a class="bluekey" href="http://www.yesky.com/key/1922/166922.html" target="_blank">细节</a>。然后我们讨论类型参数通配符和有界通配符。描绘了如何使用泛型以后，我们阐明如何编写自己的泛型类型和泛型方法。我们对于泛型的讨论将结束于一趟对于JavaAPI的核心中重要的泛型类型的旅行。这趟旅程将探索这些类型以及他们的用法，旅程的目的是为了让您对泛型如何工作这个问题有个深入的理解。<br /><br /><b><span style="FONT-SIZE: 16px">　　类型安全集合类</span></b><br /><br />　　Java.util类包包含了Java集合框架（Java Collections Framework），这是一批包含对象的set、对象的list以及基于key-<a class="bluekey" href="http://www.yesky.com/key/1844/151844.html" target="_blank">value</a>的map。第五章将谈到集合类。这里，我们讨论的是在java5.0中集合类使用类型参数来界定集合中的对象的类型。这个讨论并不适合java1.4或更早期版本。如果没有泛型，对于集合类的使用需要程序员记住每个集合中元素的类型。当您在java1.4种创建了一个集合，您知道您放入到集合中的对象的类型，但是编译器不知道。您必须小心地往其中加入一个合适类型的元素，当需要从集合中获取元素时，您必须显式的写强制类型转换以将他们从Object转换为他们真是的类型。考察下边的java1.4的代码。<br /><br /><pre class="overflow" title="pre code">public static void main(String[] args) {<br />    // This list is intended to hold only strings.<br />    // The compiler doesn't know that so we have to remember ourselves.<br />    List wordlist = new ArrayList();  <br /><br />    // Oops! We added a String[] instead of a String.<br />    // The compiler doesn't know that this is an error.<br />    wordlist.add(args);<br /><br />    // Since List can hold arbitrary <a class="bluekey" href="http://www.yesky.com/key/4694/159694.html" target="_blank">objects</a>, the get() method returns<br />    // Object.  Since the list is intended to hold strings, we cast the<br />    // return value to String but get a ClassCastException because of<br />    // the error above.<br />    String word = (String)wordlist.get(0);<br />}</pre>　　泛型类型解决了这段代码中的显示的类型安全问题。Java.util中的List或是其他集合类已经使用泛型重写过了。就像前面提到的， List被重新定义为一个list，它中间的元素类型被一个类型可变的名称为E的占位符描述。Add()方法被重新定义为期望一个类型为E的参数，用于替换以前的Object，get()方法被重新定义为返回一个E，替换了以前的Object。<br /><br />　　在java5.0中，当我们申明一个List或者创建一个ArrayList的实例的时候，我们需要在泛型类型的名字后面紧跟一对“&lt;&gt;”，尖括号中写入我们需要的实际的类型。比如，一个保持String的List应该写成“List&lt;String&gt;”。需要注意的是，这非常象给一个方法传一个参数，区别是我们使用类型而不是值，同时使用尖括号而不是圆括号<br /><br />　　Java.util的集合类中的元素必须是对象化的，他们不能是基本类型。泛型的引入并没有改变这点。泛型不能使用基本类型：我们不能这样来申明——Set&lt;char&gt;或者List&lt;int&gt;。记住，无论如何，java5.0中的自动打包和自动解包特性使得使用Set&lt;Character&gt;或者List&lt;Integer&gt;和直接使用char和int值一样方便。（查看第二章以了解更多关于自动打包和自动解包的细节）。<br /><br />　　在Java5.0中，上面的例子将被重写为如下方式：<br /><br /><pre class="overflow" title="pre code">public static void main(String[] args) {<br />    // This list can only hold String objects<br />    List&lt;String&gt; wordlist = new ArrayList&lt;String&gt;();<br /><br />    // args is a String[], not String, so the compiler won't let us do this<br />    wordlist.add(args);  // Compilation error!<br /><br />    // We can do this, though.  <br />    // Notice the use of the new for/in looping statement<br />    for(String arg : args) wordlist.add(arg);<br /><br />    // No cast is required.  List&lt;String&gt;.get() returns a String.<br />    String word = wordlist.get(0);<br />}</pre>　　值得注意的是代码量其实并没有比原来那个没有泛型的例子少多少。使用“(String)”这样的类型转换被替换成了类型参数“&lt;String&gt;”。 不同的是类型参数需要且仅需要声明一次，而list能够被使用任何多次，不需要类型转换。在更长点的例子代码中，这一点将更加明显。即使在那些看上去泛型语法比非泛型语法要冗长的例子里，使用泛型依然是非常有价值的——额外的类型信息允许编译器在您的代码里执行更强的错误检查。以前只能在运行起才能发现的错误现在能够在编译时就被发现。此外，以前为了处理类型转换的异常，我们需要添加额外的代码行。如果没有泛型，那么当发生类型转换异常的时候，一个ClassCastException异常就会被从实际代码中抛出。<br /><br />　　就像一个方法可以使用任意数量的参数一样，类允许使用多个类型变量。接口Java.util.Map就是一个例子。一个Map体现了从一个key的对象到一个value的对象的映射关系。接口Map申明了一个类型变量来描述key的类型而另一个类型变量来描述value的类型。举个例子来说，假设您希望做一个String对象到Integer对象的映射关系：<br /><br /><pre class="overflow" title="pre code">public static void main(String[] args) {<br />    // A map from strings to their position in the args[] array<br />    Map&lt;String,Integer&gt; map = new HashMap&lt;String,Integer&gt;();<br /><br />    // Note that we use autoboxing to wrap i in an Integer object.<br />    for(int i=0; i &lt; args.length; i++) map.put(args[i], i);  <br /><br />    // <a class="bluekey" href="http://www.yesky.com/key/278/160278.html" target="_blank">Find</a> the array <a class="bluekey" href="http://www.yesky.com/key/1318/161318.html" target="_blank">index</a> of a word.  Note no cast is required!<br />    Integer position = map.get("hello");<br /><br />    // We can also rely on autounboxing to convert directly to an int,<br />    // but this throws a NullPointerException if the key does not exist <br />    // in the map<br />    int pos = map.get("world");<br />}</pre>　　象List&lt;String&gt;这个一个参数类型其本身也是也一个类型，也能够被用于当作其他类型的一个类型变量值。您可能会看到这样的代码：<br /><br /><pre class="overflow" title="pre code">// Look at all those nested angle brackets!<br />Map&lt;String, List&lt;List&lt;int[]&gt;&gt;&gt; map = getWeirdMap();<br /><br />// The compiler knows all the types and we can write expressions<br />// like this without casting.  We might still get NullPointerException<br />// or ArrayIndexOutOfBounds at runtime, of course.<br />int value = map.get(key).get(0).get(0)[0];<br /><br />// Here's how we break that expression down step by step.<br />List&lt;List&lt;int[]&gt;&gt; listOfLists = map.get(key);<br />List&lt;int[]&gt; listOfIntArrays = listOfLists.get(0);<br />int[] array = listOfIntArrays.get(0);<br />int element = array[0];</pre>　　在上面的代码里，java.util.List&lt;E&gt;和java.util.Map&lt;K,V&gt;的get()方法返回一个类型为E的list元素或者一个类型为V的map元素。注意，无论如何，泛型类型能够更精密的使用他们的变量。在本书中的参考章节查看List&lt;E&gt;，您将会看到它的iterator( )方法被声明为返回一个Iterator&lt;E&gt;。这意味着，这个方法返回一个跟list的实际的参数类型一样的一个参数类型的实例。为了具体的说明这点，下面的例子提供了不使用get(0)方法来获取一个List&lt;String&gt;的第一个元素的方法<br /><br /><pre class="overflow" title="pre code">List&lt;String&gt; words = // ...initialized elsewhere...<br />Iterator&lt;String&gt; iterator = words.iterator();<br />String firstword = iterator.next();<br /></pre>。<br /><br /><br /><br /><br />　　理解泛型类型<br />　本段将对泛型类型的使用细节做进一步的探讨，以尝试说明下列问题：<br /><br />　　不带类型参数的使用泛型的后果<br /><br />　　参数化类型的体系<br /><br />　　一个关于编译期泛型类型的类型安全的漏洞和一个用于确保运行期类型安全的补丁<br /><br />　　为什么参数化类型的数组不是类型安全的<br /><br />　　未经处理的类型和不被检查的警告<br /><br />　　即使被重写的Java集合类带来了泛型的好处，在使用他们的时候您也不被要求说明类型变量。一个不带类型变量的泛型类型被认为是一个未经处理的类型（raw type）。这样，5.0版本以前的java代码仍然能够运行：您显式的编写所有类型转换就像您已经这样写的一样，您可能会被一些来自编译器的麻烦所困扰。查看下列存储不同类型的对象到一个未经处理的List：<br /><br /><pre class="overflow" title="pre code">List l = new ArrayList();<br />l.add("hello");  <br />l.add(new Integer(123));<br />Object o = l.get(0);</pre>　　这段代码在java1.4下运行得很好。如果您用java5.0来编译它，javac编译了，但是会打印出这样的“抱怨”：<br /><br /><span style="COLOR: blue">Note: Test.java uses unchecked or unsafe operations.<br />Note: Recompile with -Xlint:unchecked for details.</span><br /><br />　　如果我们加入-Xlint参数后重新编译，我们会看到这些警告：<br /><br /><span style="COLOR: blue">Test.java:6: warning: [unchecked]<br />    unchecked call to add(E) as a member of the raw type java.util.List<br />        l.add("hello");  <br />         ^<br />Test.java:7: warning: [unchecked]<br />    unchecked call to add(E) as a member of the raw type java.util.List<br />        l.add(new Integer(123));</span><br />         ^<br /><br />　　编译在add()方法的调用上给出了警告，因为它不能够确信加入到list中的值具有正确的类型。它告诉我们说我们使用了一个未经处理的类型，它不能验证我们的代码是类型安全的。注意，get()方法的调用是没有问题的，因为能够被获得的元素已经安全的存在于list中了。<br /><br />　　如果您不想使用任何的java5.0的新特性，您可以简单的通过带-source1.4标记来编译他们，这样编译器就不会再“抱怨”了。如果您不能这样做，您可以忽略这些警告，通过使用一个<a href="mailto:“@SuppressWarnings(&quot;unchecked">“@SuppressWarnings("unchecked</a>")”注解（查看本章的4.3节）隐瞒这些警告信息或者升级您的代码，加入类型变量描述。[2]下列示例代码，编译的时候不再会有警告但仍然允许您往list中放入不同的类型的对象。<br /><br /><pre class="overflow" title="pre code">List&lt;Object&gt; l = new ArrayList&lt;Object&gt;();<br />l.add("hello");  <br />l.add(123);              // autoboxing<br />Object o = l.get(0);</pre><b><span style="FONT-SIZE: 16px">　　参数化类型的体系</span></b><br /><br />　　参数化类型有类型体系，就像一般的类型一样。这个体系基于对象的类型，而不是变量的类型。这里有些例子您可以尝试：<br /><br /><pre class="overflow" title="pre code">ArrayList&lt;Integer&gt; l = new ArrayList&lt;Integer&gt;();<br />List&lt;Integer&gt; m = l;                            // okay<br />Collection&lt;Integer&gt; n = l;                      // okay<br />ArrayList&lt;Number&gt; o = l;                        // error<br />Collection&lt;Object&gt; p = (Collection&lt;Object&gt;)l;   // error, even with cast</pre>　　一个List&lt;Integer&gt;是一个Collection&lt;Integer&gt;，但不是一个List&lt;Object&gt;。这句话不容易理解，如果您想理解为什么泛型这样做，这段值得看一下。考察这段代码：<br /><br /><pre class="overflow" title="pre code">List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br />li.add(123);<br /><br />// The line below will not compile.  But for the purposes of this<br />// thought-experiment, assume that it does compile and see how much<br />// trouble we get ourselves into.<br />List&lt;Object&gt; lo = li;  <br /><br />// Now we can retrieve elements of the list as Object instead of Integer<br />Object number = lo.get(0);<br /><br />// But what about this?<br />lo.add("hello world");<br /><br />// If the line above is allowed then the line below throws ClassCastException<br />Integer i = li.get(1);  // Can't cast a String to Integer!</pre>　　这就是为什么List&lt;Integer&gt;不是一个List&lt;Object&gt;的原因，虽然List&lt;Integer&gt;中所有的元素事实上是一个Object的实例。如果允许转换成List&lt;Object&gt;，那么转换后，理论上非整型的对象也将被允许添加到list中。<br /><br /><br /><b><span style="FONT-SIZE: 16px">　　运行时类型安全</span></b><br /><br />　　就像我们所见到的，一个List&lt;X&gt;不允许被转换为一个List&lt;Y&gt;，即使这个X能够被转换为Y。然而，一个List&lt;X&gt;能够被转换为一个List，这样您就可以通过继承的方法来做这样的事情。<br /><br />　　这种将参数化类型转换为非参数化类型的能力对于向下兼容是必要的，但是它会在泛型所带来的类型安全体系上凿个漏洞：<br /><br /><pre class="overflow" title="pre code">// Here's a basic parameterized list.<br />List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br /><br />// It is legal to assign a parameterized type to a nonparameterized variable<br />List l = li;   <br /><br />// This line is a bug, but it compiles and runs.<br />// The Java 5.0 compiler will issue an unchecked warning about it.<br />// If it appeared as part of a legacy class compiled with Java 1.4, however,<br />// then we'd never even get the warning.  <br />l.add("hello");<br /><br />// This line compiles without warning but throws ClassCastException at runtime.<br />// Note that the failure can occur far away from the actual bug.<br />Integer i = li.get(0);</pre>　　泛型仅提供了编译期的类型安全。如果您使用java5.0的编译器来编译您的代码并且没有得到任何警告，这些编译器的检查能够确保您的代码在运行期也是类型安全的。如果您获得了警告或者使用了像未经处理的类型那样修改您的集合的代码，那么您需要增加一些步骤来确保运行期的类型安全。您可以通过使用java.util.Collections中的checkedList()和checkedMap( )方法来做到这一步。这些方法将把您的集合打包成一个wrapper集合，从而在运行时检查确认只有正确类型的值能够被置入集合众。下面是一个能够补上类型安全漏洞的一个例子：<br /><br /><pre class="overflow" title="pre code">// Here's a basic parameterized list.<br />List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br /><br />// Wrap it for runtime type safety<br />List&lt;Integer&gt; cli = Collections.checkedList(li, Integer.class);<br /><br />// Now widen the checked list to the raw type<br />List l = cli;   <br /><br />// This line compiles but fails at runtime with a ClassCastException.<br />// The exception occurs exactly where the bug is, rather than far away<br />l.add("hello");</pre><b><span style="FONT-SIZE: 16px">　　参数化类型的数组</span></b><br /><br />　　在使用泛型类型的时候，数组需要特别的考虑。回忆一下，如果T是S的父类（或者接口），那么类型为S的数组S[]，同时又是类型为T的数组T[]。正因为如此，每次您存放一个对象到数组中时，Java解释器都必须进行检查以确保您放入的对象类型与要存放的数组所允许的类型是匹对的。例如，下列代码在运行期会检查失败，抛出一个ArrayStoreException异常：<br /><br /><pre class="overflow" title="pre code">String[] words = new String[10];<br />Object[] objs = words;<br />objs[0] = 1;  // 1 autoboxed to an Integer, throws ArrayStoreException</pre>　　虽然编译时obj是一个Object[]，但是在运行时它是一个String[]，它不允许被用于存放一个Integer。<br /><br />　　当我们使用泛型类型的时候，仅仅依靠运行时的数组存放异常检查是不够的，因为一个运行时进行的检查并不能够获取编译时的类型参数信息。查看下列代码：<br /><br /><pre class="overflow" title="pre code">List&lt;String&gt;[] wordlists = new ArrayList&lt;String&gt;[10];<br />ArrayList&lt;Integer&gt; ali = new ArrayList&lt;Integer&gt;();<br />ali.add(123);<br />Object[] objs = wordlists;<br />objs[0] = ali;                       // No ArrayStoreException<br />String s = wordlists[0].get(0);      // ClassCastException!</pre>　　如果上面的代码被允许，那么运行时的数组存储检查将会成功：没有编译时的类型参数，代码简单地存储一个ArrayList到一个ArrayList[]数组，非常正确。既然编译器不能阻止您通过这个方法来战胜类型安全，那么它转而阻止您创建一个参数化类型的数组。所以上述情节永远不会发生，编译器在第一行就开始拒绝编译了。<br /><br />　　注意这并不是一个在使用数组时使用泛型的全部的约束，这仅仅是一个创建一个参数化类型数组的约束。我们将在学习如何写泛型方法时再来讨论这个话题。<br /><br /><b><span style="FONT-SIZE: 16px">　　类型参数通配符</span></b><br /><br />　　假设我们需要写一个方法来显示一个List中的元素。[3]在以前，我们只需要象这样写段代码：<br /><br /><pre class="overflow" title="pre code">public static void printList(PrintWriter out, List list) {<br />    for(int i=0, n=list.size(); i &lt; n; i++) {<br />        if (i &gt; 0) out.print(", ");<br />        out.print(list.get(i).toString());<br />    }<br />}<br /><br /><br /><br />在Java5.0中，List是一个泛型类型，如果我们试图编译这个方法，我们将会得到unchecked警告。为了解决这些警告，您可能需要这样来修改这个方法：<br /><br /></pre><pre class="overflow" title="pre code">public static void printList(PrintWriter out, List&lt;Object&gt; list) {<br />    for(int i=0, n=list.size(); i &lt; n; i++) {<br />        if (i &gt; 0) out.print(", ");<br />        out.print(list.get(i).toString());<br />    }<br />}</pre>　　这段代码能够编译通过同时不会有警告，但是它并不是非常地有效，因为只有那些被声明为List&lt;Object&gt;的list才会被允许使用这个方法。还记得么，类似于List&lt;String&gt;和List&lt;Integer&gt;这样的List并不能被转型为List&lt;Object&gt;。事实上我们需要一个类型安全的printList()方法，它能够接受我们传入的任何List，而不关心它被参数化为什么。解决办法是使用类型参数通配符。方法可以被修改成这样：<br /><br /><pre class="overflow" title="pre code">public static void printList(PrintWriter out, List&lt;?&gt; list) {<br />    for(int i=0, n=list.size(); i &lt; n; i++) {<br />        if (i &gt; 0) out.print(", ");<br />        Object o = list.get(i);<br />        out.print(o.toString());<br />    }<br />}</pre>　　这个版本的方法能够被编译过，没有警告，而且能够在任何我们希望使用的地方使用。通配符“？”表示一个未知类型，类型List&lt;?&gt;被读作“List of unknown”<br />作为一般原则，如果类型是泛型的，同时您并不知道或者并不关心值的类型，您应该使用“?”通配符来代替一个未经处理的类型。未经处理的类型被允许仅是为了向下兼容，而且应该只能够被允许出现在老的代码中。注意，无论如何，您不能在调用构造器时使用通配符。下面的代码是非法的：<br /><br />List&lt;?&gt; l = new ArrayList&lt;?&gt;();<br /><br />　　创建一个不知道类型的List是毫无道理的。如果您创建了它，那么您必须知道它将保持的元素是什么类型的。您可以在随后的方法中不关心元素类型而去遍历这里list，但是您需要在您创建它的时候描述元素的类型。如果你确实需要一个List来保持任何类型，那么您只能这么写：<br /><br /><pre class="overflow" title="pre code">List&lt;Object&gt; l = new ArrayList&lt;Object&gt;();</pre>　　从上面的printList()例子中，必须要搞清楚List&lt;？&gt;既不是List&lt;Object&gt;也不是一个未经处理的List。一个使用通配符的List&lt;?&gt;有两个重要的特性。第一，考察类似于get()的方法，他们被声明返回一个值，这个值的类型是类型参数中指定的。在这个例子中，类型是“unknown”，所以这些方法返回一个Object。既然我们期望的是调用这个object的toString()方法，程序能够很好的满足我们的意愿。<br /><br />　　第二，考察List的类似add()的方法，他们被声明为接受一个参数，这个参数被类型参数所定义。出人意料的是，当类型参数是未确定的，编译器不允许您调用任何有不确定参数类型的方法——因为它不能确认您传入了一个恰当的值。一个List(？)实际上是只读的——既然编译器不允许我们调用类似于add(),set(),addAll()这类的方法。<br /><br /><b><span style="FONT-SIZE: 16px">　　界定通配符</span></b><br /><br />　　让我们在我们原来的例子上作些小小的稍微复杂一点的改动。假设我们希望写一个sumList()方法来计算list中Number类型的值的合计。在以前，我们使用未经处理的List，但是我们不想放弃类型安全，同时不得不处理来自编译器的unchecked警告。或者我们可以使用List&lt;Number&gt;，那样的话我们就不能调用List&lt;Integer&gt;、List&lt;Double&gt;中的方法了，而事实上我们需要调用。如果我们使用通配符，那么我们实际上不能得到我们期望的类型安全，我们不能确定我们的方法被什么样的List所调用，Number？还是Number的子类？甚至，String?这样的一个方法也许会被写成这样：<br /><br /><pre class="overflow" title="pre code">public static double sumList(List&lt;?&gt; list) {<br />    double total = 0.0;<br />    for(Object o : list) {<br />        Number n = (Number) o;  // A cast is required and may fail<br />        total += n.doubleValue();<br />    }<br />    return total;<br />}</pre>　　要修改这个方法让它变得真正的类型安全，我们需要使用界定通配符（bounded wildcard），能够确保List的类型参数是未知的，但又是Number或者Number的子类。下面的代码才是我们想要的：<br /><br /><pre class="overflow" title="pre code">public static double sumList(List&lt;? extends Number&gt; list) {<br />    double total = 0.0;<br />    for(Number n : list) total += n.doubleValue();<br />    return total;<br />}</pre>　　类型List&lt;? extends Number&gt;可以被理解为“Number未知子类的List”。理解这点非常重要，在这段文字中，Number被认为是其自身的子类。<br /><br />　　注意，这样的话，那些类型转换已经不再需要了。我们并不知道list中元素的具体类型，但是我们知道他们能够向上转型为Number，因此我们可以把他们从list中把他们当作一个Number对象取出。使用一个for/in循环能够稍微封装一下从list中取出元素的过程。普遍性的原则是当您使用一个界定通配符时，类似于List中的get()方法的那些方法将返回一个类型为上界的值。因此如果我们在for/in循环中调用list.get()，我们将得到一个Number。在前一节说到使用通配符时类似于list.add()这种方法中的限制依然有效：举个例子来说，如果编译器允许我们调用这类方法，我们就可以将一个Integer放到一个声明为仅保持Short值的list中去。<br /><br />　　同样可行的是使用下界通配符，不同的是用super替换extends。这个技巧在被调用的方法上有一点不同的作用。在实际应用中，下界通配符要比上界通配符用得少。我们将在后面的章节里讨论这个问题。 <br /><br /><img src ="http://www.blogjava.net/xixidabao/aggbug/47063.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-19 15:17 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/19/47063.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java程序如何穿透带有密码验证的代理</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/10/45558.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Wed, 10 May 2006 14:57:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/10/45558.html</guid><description><![CDATA[java  sample codes  that access outer internet through proxy which require username and password <br /><br />use inner instead of extends is the most different <br /><br />网上也有一些文章但是大多数涉及带有授权验证的proxy都有问题，<br />主要问题就是出在对 Authenticator.setDefault的使用，以及base64编码的问题上<br />代码是最没有二义性的文档，实现原理不再解释，请看代码去体会。<br />如果转载请注明代码出处。<br /><br />chimae@cnjsp.org<br /><br />package org.chimae.net;<br /><br />import java.io.BufferedReader;<br />import java.io.IOException;<br />import java.io.InputStream;<br />import java.io.InputStreamReader;<br />import java.net.Authenticator;<br />import java.net.HttpURLConnection;<br />import java.net.PasswordAuthentication;<br />import java.net.URL;<br /><br />/**<br /> * @author chimae@cnjsp.org<br /> */<br />public class ProxyConnTest {<br />    <br />    public static void initProxy(String host, int port, final String username,<br />            final String password) {<br />        Authenticator.setDefault(new Authenticator() {<br />            protected PasswordAuthentication getPasswordAuthentication() {<br />                return new PasswordAuthentication(username,<br />                        new String(password).toCharArray());<br />            }<br />        });<br /><br />    <br />        System.setProperty(\"http.proxyType\", \"4\");<br />        System.setProperty(\"http.proxyPort\", Integer.toString(port));<br />        System.setProperty(\"http.proxyHost\", host);<br />        System.setProperty(\"http.proxySet\", \"true\");<br />    }<br />    <br />    <br />    public static void main(String[] args) throws IOException {<br />        String url = \"http://java.sun.com/\";<br />        String proxy = \"yourProxy\";<br />        int port =8080;<br />        String username =\"username\";<br />        String password =\"password\";<br />        String curLine = \"\";<br />        String content = \"\";        <br />        URL server = new URL(url);<br />        initProxy(proxy,port,username,password);<br />        HttpURLConnection connection = (HttpURLConnection)server.openConnection();<br />        connection.connect();<br />        InputStream is = connection.getInputStream();<br />        BufferedReader reader = new BufferedReader(new InputStreamReader(is));<br />        while ((curLine = reader.readLine()) != null) {<br />                content += curLine;<br />            }<br /><br />        System.out.println(\"content= \" + content);<br />        is.close();<br />    }<br /><br />}<br /><br /><img src ="http://www.blogjava.net/xixidabao/aggbug/45558.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-10 22:57 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/10/45558.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ruby，Java的劲敌</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/08/44933.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 07 May 2006 16:18:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/08/44933.html</guid><description><![CDATA[
		<p>   <strong> Bruce Tate：一石激起千层浪</strong></p>
		<p>    Bruce Tate并不是作为一个局外者写就《超越Java》这边书的。他的顾问公司专注于Java 持久化框架和轻量级开发方法，同时他也是这些流行的Java图书的作者， Spring： A Developer's Notebook， Better， Faster， Lighter Java， 以及 Bitter Java.</p>
		<p>    1，在《超越Java》中你花费了大量的时间在Ruby上面，看起来是它像在你说那些将超越Java竞争者中出类拔萃。你觉得是什么使Ruby比 PHP，Python这类语言优越？</p>
		<p>    这些都是好语言，但是都有一些缺点。对大型应用，PHP和Perl不能连续地产生可读的代码。Lisp，Python和Smalltalk这些就缺少了伟大语言好像应该拥有的催化剂。Ruby是一种好语言，和催化剂（Rails）提供了引人注目得新价值（以效率的角度）以及还在飞速地增长。Ruby不一定是最好的语言，但是它将是我所见过最有可能的。Ruby不大可能在委员会那里超过Java.它很有可能首先在一个更小但是却重要的环境中取得好成绩。这个环境也就是一个有web UI大的胖关系数据库。</p>
		<p>    2，是否Rails就意味着Ruby？其他语言包括Java难道就不能实现同样的思想？</p>
		<p>    如今，Rails就是超过象Netscape之类语言的催化剂，具有Java一样的功能，可通过网络实现应用的传送。但是我认为Rails很有可能仅仅是Ruby元编程框架浪潮的第一波。</p>
		<p>    3，你的书中很多都基于典型的“将一个web接口连接到数据库”场景，Ruby的成功案例看上去也仅仅是一两个开发人员的小项目。但是你也承认了Java的重量级企业框架对一些项目的价值（即大型系统上的大型应用）。什么情况下一个项目对于RoR来说过于大的呢？如果一个RoR在那方面的特性发展缓慢呢？</p>
		<p>    有Ruby和小团队你可以做很多事情。基础代码几乎都是一个人写就的，但却关乎整个公司的生计。在一些主要的公司开始进行认真的尝试之前，我们不知道你可以利用ruby或者rails到什么程度。其中一个最吸引我的事情是经济的规模，更小的规模。万一生产力的数字是真实的呢？万一确实可以得到5X的增长？那么你可以在一个部门内划分工作，将工作划分给团队中的一个。交流将很少会成为问题。管理和疏忽也很少会成为问题了。我们都知道对于一间公司增长， tipping points意味着什么。因为增加沟通和管理的级别会产生很多的障碍， 所以一间公司增长要超过1，5，10，40，甚至100倍是很困难的。但是，在这一点上， Ruby on Rails的可扩展性是非常的好。</p>
		<p>    4，你是否看到Java开发人员转向Ruby吗，还是Ruby将会给新一代的开发人员采用？</p>
		<p>    我觉得两者都有可能。有开发人员不能容忍学习servlets， Spring， XML， Hibernate， Struts 然后还要学习一些 UI 粘合的框架。在Rails中，他们将会完全给释放出来。同时也有Java开发人员已经在寻找更加优势的方法，他们发现了Ruby on Rails.接受了Rails的Java梦想家们的数目是令人惊愕的，他们有Thought Works，James Duncan Davidson，Stuart Halloway 更有 David Geary.</p>
		<p>    5，难道Java本身就不能做一些事情来维持它的杰出地位？如果过于复杂和膨胀，什么可以阻止开发人员倒退到jdk 1.4？</p>
		<p>    Java将会继续处于顶峰，并在企业应用上保持良好的表现，但是时间不会停滞不前。在某种意味上它终将会给替代。我们将需要一个更高级别的抽象。我认为我们最好的希望就是在JVM上做充足的投入，更好地支持动态语言， 拥抱新的事物，对于旧有的java代码，则最好是保留保守的态度。</p>
		<p>    6，我们应该期望Ruby在其他领域引起轰动？如果对于开发web应用它是如此不错，假如Ruby有的可以使用的合适的UI框架，会不会在桌面应用也实用呢？</p>
		<p>    现在说什么还为时过早。如今，尽管Ruby是有催化作用（Rails）的语言，但是它仅仅是一个候选。以后将会发生什么？我想谁也不知道。</p>
		<p> <strong>   James Duncan Davidson：尝试新事务</strong></p>
		<p>    如果你使用Tomcat或者Ant（认真地说，什么Java开发人员什么使用过？）那么你就熟悉了James Duncan Davidson的工作了。在Sun，他致力把这些项目开源并且把他们捐献给Apache基金会。并且他也编写了Servlet API的最初两个版本，还有处理XML的Java API.离开Sun之后，他做起了Mac OS 的X开发。编写《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》</p>
		<p>    1，上一次我们见到你的时候，你还是那个《Mac desktop apps in Cocoa》家伙。而现在，我在你的blog上看到你已经深深地陷入了Rails.那是什么回事？</p>
		<p>    我当时穷的要命和急切地需要钱。那时我刚刚买了一幢新房，并且抵押付款期限就快到期了――噢，等会，你想我认真点吗？好吧，事实是我和我的几个朋友已经一直在想一起工作一段时间了。当恰当的时机到的时候，我们给项目做了技术评估，Rails成了首选。那时我还没用过Rails或者Ruby.但是我是不会让小小的需要学习阻碍我去做那个项目的。今年我已经学习了三种，可能四种语言了。我不再相信一种语言可以做任何事了。如果我需要学习一些新知识去一些事情，我将全力以赴去学好它。</p>
		<p>    2，你对Rails有什么看法？</p>
		<p>    主要是简单性。完成事情的容易程度。我做的那个应用的第一个项目原来是一个基于Java的web应用。每个人都知道一定会有一种更好，更快，更容易的方法的。Ruby一直都是一种好语言――并且是一种有趣的语言――因此建立于它之上的这个框架，它应得到关注。</p>
		<p>    3，Ruby的晦涩和Rails的新颖对客户来说会不会是一个问题？</p>
		<p>    不全是。如今事实上恰恰相反。有太多潜在的工作， 缺并没有足够的人在真正地开发Ruby on Rails应用。</p>
		<p>    4，为什么Ruby会如此特殊？难道Rails就不能在其他语言中实现？难道它就不能给Java实现？</p>
		<p>    很少有其他语言可以完成Rails，或者像Rails那样的。Java不在他们之列。Rails从Ruby中获取了一些妙不可言的东西，尝试用另一种语言复制它不仅是对Rails所做的是一个浪费，对其他语言来说也是一个浪费。但是它的概念一定会在其他非常动态的，动态类型语言中得到很好的应用。</p>
		<p>    确实，我很兴奋的看到其他项目正实现一些从Rails衍生的主意到其他平台中。例如作为一个Python里的Rails版本，Django得到了一些固定的发展。但是，实际上它是Python自己的庞然大物，它如何成长将会非常有趣。</p>
		<p>    现在，我已经说过了你不能用Java来实现Rails.但却并不意味着你不能用Java做一些同样优秀的东西。Java的力量可以以一种有趣的，神奇方式应用到一种全新的框架上。只是还没人做那些事情。每个人都对J2EE这个糕点趋之若骛，以致于没人以一种更加激烈，更加动态的方式来重新考虑问题。尽管有人提出一个基于Java的杀手级的框架可以与Rails做同样多工作， 它一定也不能做的象Rails一样。</p>
		<p>    5，具有良好设计的Java应用能够很好地支持特性的扩展――设计好你的类和包，那么你的心情将舒畅好长的一段时间。能否有团队编写出一个真正大型的Ruby应用？它是否具可维护性？或者还是RoR只能小打小闹？</p>
		<p>    设计良好的应用无论是以何种语言编写的都能够很好地支持特性的扩展。糟糕的设计无论是何种语言就不能了。同时也有了如何才是大型应用的定义的问题。我用Ruby写的第一个rails应用部署到生产也不够5，000行代码，但是我之前用其他语言编写的同样大小的应用却达到了50，000行代码，所以如何定义大型是个问题。</p>
		<p>    有团队可以编写一个可以支持大量特性，运行良好，时间上具备可维护性的Ruby on rails应用吗？是的，毫无疑问。在使用了Ruby on Rails一段时间后，我将有信心用Rails解决任何尺寸的web应用问题。但是，那是因为我在它上面花费了一些时间，认识到编写一个具有良好设计的应用是有可能的。</p>
		<p>    也就是说，很有可能现在正有几十个垃圾的Ruby on rails应用在编写中。几百或者几千个都有可能。如果你不知道你正在做什么，你将会编写一个垃圾的应用。</p>
		<p>    6，那么我们回到了web应用，你可以在桌面上使用ruby，或者我们是否一直要用C#，面向对象C还是OS服务商支持的语言编写UI？</p>
		<p>    嗯，我的生活的一部分就是回到web应用。它对我来说是一个很好的还环境，因为自从1994年开始我就一直在做基于web的工作。但是现在我将开发基于桌面的应用。而且人们对桌面应用的需求还很大。我可不想要一个网络的office.你也不想把一些象Aperture的东西建造成一个web应用吧。</p>
		<p>    你现在可以使用Ruby去建造一个引人注目的桌面应用吗？不，相关的工具包还不存在。但是如果存在了恰当的工具包――这是有可能的。那就没有什么东西可以阻止它成为一个好的桌面应用语言了。那是说，我已经发现利用平台的最好的方法就是尽量的本地化-贴近平台，不管它是一个操作系统或者还是一个web应用框架。当我在Mac上的桌面工作，我需要写面向对象C和Cocoa.当我用Rails的web工作，那意味着使用Ruby.而对于操作系统方面的工作，我需要用到C和shell.在这个讨论中不会只有唯一的答案。</p>
		<p>    我认为这就是最近对Ruby on Rails关注和屏弃以有色Java眼镜看待世界的真正胜利。Ruby并不会成为下一个Java，完全不。而是Ruby on Rails将会帮助打破了这样的一个观点—— “只有一个正确的方法”，不是的。解决问题的方法有千百条。真正的，他们中没有一个是明显的胜者。只有解决方案有优势的位置。</p>
		<p>    我想就像我们在其中工作，吃饭和居住的建筑物一样。一些建筑物最好是用水泥和钢筋筑造。其他的是用砌筑。还有其他的最好是用木材，并且那样做是有理由的。没有人会跳起来说“所有的建筑物一定要用砖头筑造！”，那样太愚蠢了！同样的道理，不是所以的应用都应该要用Ruby on Rails或者Django或者J2EE或者Perl来编写的。对于任何一个特定的工作都有大量的工具。还有新的工具等待去发掘呢。诀窍就是决定最优秀的那个。</p>
		<p>    让我们从夸夸其谈回到你的问题：在web应用的范畴，很容易出现一个新的框架的，因为你并不是与视频卡，GUI和应用在上面跑的整个系统之类打交道。除非是你愿意开发一个自己的框架，你必须面对选择使用哪个框架的选择。在桌面上也是同样的道理。你可以创建你自己的框架，做任何你想要的做的，但是该建议却不比你自己为web创建一个新框架容易。</p>
		<p> <strong>   Robert Cooper：带上利器</strong></p>
		<p>    ONJava的博客Robert Cooper最近在他的blog上撰写了“It's the End of the World as We Know It."”来回应一些“Java时代末日”的言论。Cooper是亚特兰大地区致力于企业集成和web/web服务应用的J2EE开发人员，同时也是资讯和娱乐站点screaming-penguin.com的经营者。</p>
		<p>    1，你曾经说过“长期以来‘企业级’Java一直未能逃脱的一个很悲哀的事实是500个应用才需要‘企业级’的功能。”为什么Java开发人员采用了比他们实际需要更加复杂的框架？</p>
		<p>    好的，有一些因素导致了这样现象。一个是“buzzword compliance”。 你想使用你“应该”要使用的东西。我记得在99年一些大项目采用了entity bean作为数据模型，但是我们很快发现了性能是如此的恐怖以致于我们最终又转到了手写的DAO层。</p>
		<p>    最近对javax.persistence的修改，一定程度也表明了，EJB的失败一直都是缺乏不同级别的支持。理想的情况是，如果我仅需要基本的，简单的ORM-类型功能，我就能够很快的得到。如果我想深入真正复杂的东西，给我一个“更深层”的有分布式事务的视图。然而，尽管在那样高的层次上，在EJB1.1/1.2的世界里，看看你需要多少行代码1）从JNDI获得Home存根，2）做一个find by，3）做改变，4）提交事务。对于一般的应用，答案没有理由只应独一无‘二’。然而更新颖的Java框架（阅读：Spring + Hibernate）使你获得了那一个‘二’，但是你也要做一大堆的配置。那样，很多方面， 混淆了你的代码。大量的因素促成我我的演说“拥有一个有效的默认配置/操作”，但是那是不同的故事了。</p>
		<p>    2，你一向不屑把Ruby on Rails看作是technorati中的后起之秀。你是根本就不想接受还是只是厌恶这种夸夸其谈呢？</p>
		<p>    并不是我真的这样不屑。Rails在很多重要的方面来说非常优秀。事实上，如果PHP是那要死的飞行意粉怪，并且要给Ruby替代，我想那将是一个大进步。然而，尽管Ruby确实扫除了过去的错误，它仍然缺乏Java那么多的功能，但是Ruby为快速开发提供了一个引人注目新的开发模型。你可能反对，这仅仅是时间问题，假以时日，它一定可以的。然而，我对Ruby/Rails有一些敌意， 是因为我一些一直都在希望java能够拥有的特性，一直长期希望J2EE能够拥有简单性。</p>
		<p>    3，那么是什么促使你继续留在Java阵营，你看中它的什么呢？</p>
		<p>    按照我日常的工作，在400上没有必要使用Ruby调用PCML/RPG程序。同样，那些大量的java拥有的 “企业级”特性很重要，更不用说它是一个统一的打包和部署的框架。</p>
		<p>    4，你说“然而，Java像是变成了无所不包的了，它不是‘web的语言’，也不是桌面应用的‘一等公民’。”Java是否应该放弃一些野心，专注在一个更小应用空间集合里？如果那样，放弃哪个？</p>
		<p>    你也知道，我在我的站点上和一个绅士有很长的讨论。他指出了Java在J2ME世界的成功，TME/TiVo，置顶盒——或许是下一代DVD的混战。这些对于开发来说都是有效的领域，但是我认为如果Java变成了这样的一个系统，那将是一个损失。</p>
		<p>    使我恼怒的是Java发明的“applet”，你看看Flash（加上Flex/Laszlo），它的“Cool”（快速的用户体验）和“强大的”（我免费的得到数据绑定/SOAP/XML-RPC等等）使applets无地自容。“强大的”缺不需要JRE的事实立马扼杀了applet的生存空间，如果有人能以接近数目的代码行数向我展示Laszlo Dashboard demo，我可能已经在那个方面得到了一个核心发展。Cool是需要很大的代价的。</p>
		<p>    5，Java越来越多的复杂性，越来越多的竞争框架，这是你之前批评很多次的。我们用JDK 1.2的语义编码，手工编写servlet是不是更好呢？</p>
		<p>    我认为的复杂性是很难管理。例如，如果你从一个VB背景开始关注使用Swing，那是非常令人难受的。当你需要做一些技巧性的东西，没有一个“简单”接口可以Cast成更加“高级”的接口。坦白的说，最近出现的一个有用的东西就是JAX-P了。在我头脑中，还有一些东西jre没有的，但是却是必须存在的。Swing存在有多久了？然而还没有东西可以给你像VB5的数据绑定表格控制的功能。</p>
		<p>    我想JDK 1.5的提升是非常显著的。当我谈到“降低复杂性”，我真正指的是A）对于一件事情， 给予更多标准的途径来完成，因此如果我真正需要一些不同的特性，我仅仅需要一个外部的框架。B）设计更加友好的API――认真的说，看看JavaMail的JavaDoc，看看研究出如何发送一个HTML格式的email要花费你多长时间。C）增加更加通用的功能到核心运行环境，提供一个可以分别与基于Flash，RIA和桌面领域的Avalon/Sparkle相比拟的风格和性能。同样的，我记得以前天真地从VB/VC++转到Java世界，想道“天哪，本来一直就应该是这样的。”几年前，我不能说我看到任何增加到Java的东西都是和我有同样的想法（除了将要来临的JAX-WS API）。看看Rails，你会有同样的感觉。看看Flex你也有同样的感觉。看看Avalon你也有同样的感觉。不是我不喜欢Ruby，只是看上去Java不再可以与时俱进让我很沮丧。</p>
		<p>    <strong>Bill Venners：发行人的观点</strong></p>
		<p>    Artima是很多Java开发人员高度关注的站点。长久以来它的发行人Bill Venners是一个Java著作者和顾问。同时也是一个JavaWorld的专栏作家，Inside the Java Virtual Machine的作者。所以，当我们注意到Artima上的Ruby内容，我们必须找出背后的故事。</p>
		<p>    1，Artima在很多人眼中一直都是作为一个Java站点，但是你刚创建一个一个新的Ruby版面， Artima当今很多的特色文章都是关于Ruby的，是什么促使了这种改变？</p>
		<p>    没有改变。Artima曾经是一个清一色的Java站点，但是几年前我们扩展了更一般开发焦点，开始涵盖其他语言。例如，我们开始在“Python Buzz”集成Python Blogs，在“The C++ Source”刊发C++文章。我们创建了Ruby Code &amp; Style简报来作为Ruby社区通过高质量，编辑的文章分享信息的地方。</p>
		<p>    2，你是否认为你的Ruby报道是作为一种趋势，或者服务已作出改变的开发人员？</p>
		<p>    我们创建Ruby简报仅仅是为了服务Ruby社区。我不知道是不是有一个趋势，我也没有看到很多Java开发人员转到Ruby.人们并不只是仅仅需要用一种语言编程。我想掌握一种系统语言是有好处的，例如Java或者C++，和一种脚本语言，例如Ruby或者Python，而且能够用两者工作。那样的话你就可以使用你手中最好的工具来工作了。</p>
		<p>    3，你的最初少数Ruby文章几乎没有涉及Rails.你是否认为Rails背后有一个大的Ruby故事？你还知道有什么东西使用了Ruby？</p>
		<p>    除了知道Rails在市场上很有卖点，我对Rails了解的不多。Rails商人一遍又一遍传递了这样的一个信息，就是Rails能够助你很快的创建web应用。每个人都很清楚的收到了这个信息。我认为这是一个非常好营销工作。我也相信这个信息，但是快速的创建一个web应用不仅仅是人们所关心的。有时人们也关系与数据库的集成，应用服务器的集群，在这种情形，其他工具可能比Rails更有效率。就Ruby而言，我认为它是一种适合脚本和创建系统的多用途的编程语言，与Python同种类别。</p>
		<p>    4，即使在Rails以前，对比于其他“敏捷”语言，人们都谈论到Ruby独特的吸引Java开发人员。你认为Ruby有什么特别之处呢？为什么它对于Java移民这么好？</p>
		<p>    我不相信将会有很多Java移民或者Ruby尤其适合Java程序员。现在大肆宣称围绕着Ruby，或许是因为Rails的买卖，所以或许你印象中的移民就是来自于那些宣称的印象。Ruby是一种好的语言，但是Java也是，Python也是。</p>
		<p>    5，你是否认为我们将会看到很多Java开发人员开始学习Ruby或者转到Ruby，或者我们将看到一个新一代直接跳过Java而用Ruby代之？</p>
		<p>    Java不会离我们而去。在Artima，我们选择了Java作为新的体系架构，而不是Ruby，或者Python，就是因为它是一个成熟的拥有免费和商用的大量工具和API的生态系统。相对于Java，是的，当使用Ruby或者Python编程的时候是有一些速度的提升，但是有了现代的像IntelliJ，Eclipse和NetBeans的 Java IDE，你可以在Java里走的更快。但是用Ruby编程是很惬意的，同时，如果有人可以从Ruby中找到他们的职业生涯，那么请全力以赴去实现。</p>
		<p>    <strong>结语</strong></p>
		<p>    是否Ruby将横扫Java？不仅仅是虔诚的Ruby狂热者在预言这个场景。开发人员的需要观点， 就像Venners提出的“手上对工作最优的工具”。 至关重要的是，开发人员必须对正确理解和使用这些工具负责。也就不难看出Coopper对于EJB 1.0的大肆宣称的记忆和Davidson的预言“如今很有可能有很多垃圾的Ruby on Rails应用在编写中”的联系了。无视技术，让市场的浪潮冲走是很危险的。不仅如此，很多人正在告诉我们使用Ruby会有相当大的效率提升，它确实是一个理想的工具，因此我们应该给予一定的关注。</p>
		<p>    作者感谢Bruce Tate， James Duncan Davidson， Robert Cooper， 和 Bill Venners.感谢占用了他们的时间与ONJava的读者分享他们的思想。</p>
		<p>    Chris Adamson是ONJava和Java.net的编辑，专攻Java，Mac Os X和多媒体开发的亚特兰大地区的顾问。</p>
		<p>
				<span style="COLOR: red">　<font color="#000000">　版权声明：任何获得Matrix授权的网站，转载时请务必保留以下作者信息和链接</font></span>
				<br />
				<font color="#000000">　　原文:</font>
				<a href="http://www.onjava.com/pub/a/onjava/2005/11/16/ruby-the-rival.html" target="_new">
						<font color="#000000">http://www.onjava.com/pub/a/onjava/2005/11/16/ruby-the-rival.html</font>
				</a>
				<br />
				<font color="#000000">　　译文:</font>
				<a href="http://www.matrix.org.cn/resource/article/44/44288_Ruby+Java.html" target="_new">
						<font color="#000000">http://www.matrix.org.cn/resource/article/44/44288_Ruby+Java.html</font>
				</a>
		</p>
		<div>
		</div>
		<br />
		<br />
		<br />
<img src ="http://www.blogjava.net/xixidabao/aggbug/44933.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-08 00:18 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/08/44933.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java学习之——java面试题集</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/07/44929.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 07 May 2006 15:53:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/07/44929.html</guid><description><![CDATA[
		<p>　　基础知识：1.C++或Java中的异常处理机制的简单原理和应用。</p>
		<p>　　当JAVA程序违反了JAVA的语义规则时，JAVA虚拟机就会将发生的错误表示为一个异常。违反语义规则包括2种情况。一种是JAVA类库内置的语义检查。例如数组下标越界，会引发IndexOutOfBoundsException；访问null的对象时会引发NullPointerException.另一种情况就是JAVA允许程序员扩展这种语义检查，程序员可以创建自己的异常，并自由选择在何时用throw关键字引发异常。所有的异常都是java.lang.Thowable的子类。</p>
		<p>　　2. Java的接口和C++的虚类的相同和不同处。</p>
		<p>　　由于Java不支持多继承，而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性，现有的单继承机制就不能满足要求。与继承相比，接口有更高的灵活性，因为接口中没有任何实现代码。当一个类实现了接口以后，该类要实现接口里面所有的方法和属性，并且接口里面的属性在默认状态下面都是public static，所有方法默认情况下是public.一个类可以实现多个接口。</p>
		<p>　　3. 垃圾回收的优点和原理。并考虑2种回收机制。</p>
		<p>　　Java语言中一个显著的特点就是引入了垃圾回收机制，使c++程序员最头疼的内存管理的问题迎刃而解，它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制，Java中的对象不再有“作用域”的概念，只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露，有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行，不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收，程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收，增量垃圾回收。</p>
		<p>　　4. 请说出你所知道的线程同步的方法。</p>
		<p>　　wait（）：使一个线程处于等待状态，并且释放所持有的对象的lock.</p>
		<p>　　sleep（）：使一个正在运行的线程处于睡眠状态，是一个静态方法，调用此方法要捕捉InterruptedException异常。</p>
		<p>　　notify（）：唤醒一个处于等待状态的线程，注意的是在调用此方法的时候，并不能确切的唤醒某一个等待状态的线程，而是由JVM确定唤醒哪个线程，而且不是按优先级。</p>
		<p>　　Allnotity（）：唤醒所有处入等待状态的线程，注意并不是给所有唤醒线程一个对象的锁，而是让它们竞争。</p>
		<p>　　5. 请讲一讲析构函数和虚函数的用法和作用。</p>
		<p>　　6. Error与Exception有什么区别？</p>
		<p>　　Error表示系统级的错误和程序不必处理的异常，</p>
		<p>　　Exception表示需要捕捉或者需要程序进行处理的异常。</p>
		<p>　　7. 在java中一个类被声明为final类型，表示了什么意思？</p>
		<p>　　表示该类不能被继承，是顶级类。</p>
		<p>　　8. 描述一下你最常用的编程风格。</p>
		<p>　　9. heap和stack有什么区别。</p>
		<p>　　栈是一种线形集合，其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。</p>
		<p>　　堆是栈的一个组成元素</p>
		<p>　　10. 如果系统要使用超大整数（超过long长度范围），请你设计一个数据结构来存储这种超大型数字以及设计一种算法来实现超大整数加法运算）。</p>
		<p>　　public class B{</p>
		<p>　　int[] ArrOne = new ArrOne[1000]；</p>
		<p>　　String intString=""；</p>
		<p>　　public int[] Arr（String s）</p>
		<p>　　{</p>
		<p>　　intString = s；</p>
		<p>　　for（int i=0；i {</p>
		<p>　　11. 如果要设计一个图形系统，请你设计基本的图形元件（Point，Line，Rectangle，Triangle）的简单实现</p>
		<p>　　12，谈谈final， finally， finalize的区别。</p>
		<p>　　final？修饰符（关键字）如果一个类被声明为final，意味着它不能再派生出新的子类，不能作为父类被继承。因此一个类不能既被声明为 abstract的，又被声明为final的。将变量或方法声明为final，可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值，而在以后的引用中只能读取，不可修改。被声明为final的方法也同样只能使用，不能重载。</p>
		<p>　　finally？再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常，那么相匹配的 catch 子句就会执行，然后控制就会进入 finally 块（如果有的话）。</p>
		<p>　　finalize？方法名。Java 技术允许使用 finalize（） 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的，因此所有的类都继承了它。子类覆盖 finalize（） 方法以整理系统资源或者执行其他清理工作。finalize（） 方法是在垃圾收集器删除对象之前对这个对象调用的。</p>
		<p>　　13，Anonymous Inner Class （匿名内部类） 是否可以extends（继承）其它类，是否可以implements（实现）interface（接口）？</p>
		<p>　　匿名的内部类是没有名字的内部类。不能extends（继承） 其它类，但一个内部类可以作为一个接口，由另一个内部类实现。</p>
		<p>　　14，Static Nested Class 和 Inner Class的不同，说得越多越好（面试题有的很笼统）。</p>
		<p>　　Nested Class （一般是C++的说法），Inner Class （一般是JAVA的说法）。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。具体可见http： //www.frontfree.net/articles/services/view.ASP？id=704&amp;page=1</p>
		<p>　　注： 静态内部类（Inner Class）意味着1创建一个static内部类的对象，不需要一个外部类对象，2不能从一个static内部类的一个对象访问一个外部类对象</p>
		<p>　　第四，&amp;和&amp;&amp;的区别。</p>
		<p>　　&amp;是位运算符。&amp;&amp;是布尔逻辑运算符。</p>
		<p>　　15，HashMap和Hashtable的区别。</p>
		<p>　　都属于Map接口的类，实现了将惟一键映射到特定的值上。</p>
		<p>　　HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。</p>
		<p>　　Hashtable 类似于 HashMap，但是不允许 null 键和 null 值。它也比 HashMap 慢，因为它是同步的。</p>
		<p>　　16，Collection 和 Collections的区别。</p>
		<p>　　Collections是个java.util下的类，它包含有各种有关集合操作的静态方法。</p>
		<p>　　Collection是个java.util下的接口，它是各种集合结构的父接口。</p>
		<p>　　17，什么时候用assert.</p>
		<p>　　断言是一个包含布尔表达式的语句，在执行这个语句时假定该表达式为 true.如果表达式计算为 false，那么系统会报告一个 Assertionerror.它用于调试目的：</p>
		<p>　　assert（a &gt; 0）； // throws an Assertionerror if a &lt;= 0</p>
		<p>　　断言可以有两种形式：</p>
		<p>　　assert Expression1 ；</p>
		<p>　　assert Expression1 ： Expression2 ；</p>
		<p>　　Expression1 应该总是产生一个布尔值。</p>
		<p>　　Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试信息的 String 消息。</p>
		<p>　　断言在默认情况下是禁用的。要在编译时启用断言，需要使用 source 1.4 标记：</p>
		<p>　　javac -source 1.4 Test.java</p>
		<p>　　要在运行时启用断言，可使用 -enableassertions 或者 -ea 标记。</p>
		<p>　　要在运行时选择禁用断言，可使用 -da 或者 -disableassertions 标记。</p>
		<p>　　要系统类中启用断言，可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。</p>
		<p>　　可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过，断言不应该用于验证传递给公有方法的参数，因为不管是否启用了断言，公有方法都必须检查其参数。不过，既可以在公有方法中，也可以在非公有方法中利用断言测试后置条件。另外，断言不应该以任何方式改变程序的状态。</p>
		<p>　　18，GC是什么？ 为什么要有GC？ （基础）。</p>
		<p>　　GC是垃圾收集器。Java 程序员不用担心内存管理，因为垃圾收集器会自动进行管理。要请求垃圾收集，可以调用下面的方法之一：</p>
		<p>　　System.gc（）</p>
		<p>　　Runtime.getRuntime（）。gc（）</p>
		<p>　　19，String s = new String（"xyz"）；创建了几个String Object？</p>
		<p>　　两个对象，一个是“xyx”，一个是指向“xyx”的引用对象s.</p>
		<p>　　20，Math.round（11.5）等於多少？ Math.round（-11.5）等於多少？</p>
		<p>　　Math.round（11.5）返回（long）12，Math.round（-11.5）返回（long）-11；</p>
		<p>　　21，short s1 = 1； s1 = s1 + 1；有什么错？ short s1 = 1； s1 += 1；有什么错？</p>
		<p>　　short s1 = 1； s1 = s1 + 1；有错，s1是short型，s1+1是int型，不能显式转化为short型。可修改为s1 =（short）（s1 + 1） .short s1 = 1； s1 += 1正确。</p>
		<p>　　22，sleep（） 和 wait（） 有什么区别？ 搞线程的最爱</p>
		<p>　　sleep（）方法是使线程停止一段时间的方法。在sleep 时间间隔期满后，线程不一定立即恢复执行。这是因为在那个时刻，其它线程可能正在运行而且没有被调度为放弃执行，除非（a）“醒来”的线程具有更高的优先级 （b）正在运行的线程因为其它原因而阻塞。</p>
		<p>　　wait（）是线程交互时，如果线程对一个同步对象x 发出一个wait（）调用，该线程会暂停执行，被调对象进入等待状态，直到被唤醒或等待时间到。</p>
		<p>　　23，Java有没有goto？</p>
		<p>　　Goto？java中的保留字，现在没有在java中使用。</p>
		<p>　　24，数组有没有length（）这个方法？ String有没有length（）这个方法？</p>
		<p>　　数组没有length（）这个方法，有length的属性。</p>
		<p>　　String有有length（）这个方法。</p>
		<p>　　25，Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型？</p>
		<p>　　方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现，重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数，我们说该方法被重写 （Overriding）。子类的对象使用这个方法时，将调用子类中的定义，对它而言，父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法，它们或有不同的参数个数或有不同的参数类型，则称为方法的重载（Overloading）。Overloaded的方法是可以改变返回值的类型。</p>
		<p>　　26，Set里的元素是不能重复的，那么用什么方法来区分重复与否呢？ 是用==还是equals（）？ 它们有何区别？</p>
		<p>　　Set里的元素是不能重复的，那么用iterator（）方法来区分重复与否。equals（）是判读两个Set是否相等。</p>
		<p>　　equals（）和==方法决定引用值是否指向同一对象equals（）在类中被覆盖，为的是当两个分离的对象的内容和类型相配的话，返回真值。</p>
		<div>
		</div>
		<br />
		<br />
		<br />igInt（） 
<p></p><img src ="http://www.blogjava.net/xixidabao/aggbug/44929.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-07 23:53 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/07/44929.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java认证考试心得：顺利通过SCJP测试全接触 </title><link>http://www.blogjava.net/xixidabao/archive/2006/05/04/44488.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Wed, 03 May 2006 16:18:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/04/44488.html</guid><description><![CDATA[
		<br />　　Java以其平台无关性、面向对象、支持多线程等优点成为越来越多的程序开发人员的新宠，Java技术以其独特的优势在越来越多的领域得以使用和发展。作为程序开发人员，为了检验对Java掌握的程度，参加Sun公司的Java认证考试是比较好的一个方法。两周前，我以83%成绩通过了SCJP1.4的认证考试，有一些体会想与大家分享。<br />　　<br />　　想顺利通过考试，可以从以下几个方面着手：<br />　　<br />　　1.参加培训或自学Java经典课程。如果有C语言和面向对象的基础，只需自学SL275课程以及JDK1.4 API，否则应该参加相应的培训班进行学习。我对C语言和面向对象都有一些基础，同时又参加了SL275+Weblogic+JSP课程的学习。<br />　　<br />　　2.确定考试版本，明确考查的知识点及知识点的考查难度。目前SCJP考试有两个版本1.2和1.4，两个版本的比较如下表：<br />　　
<center><img src="/imgfiles/2004.5.17.11.7.55.1.jpg" /></center><br />　　
<center>表1</center><br />　　其中选择题有两种形式：(1)给出一段代码让选择其运行结果；(2)给出关于某基本概念的一些描述语句让选择正确的答案。填空题一般是给出一段正确的代码，不提供选择项，而让你填写上运行结果。<br />　　<br />　　两个版本考查的知识点最大的差别在于310-025包含I/O、AWT，且对各个知识点的考查难度相对较容易，而310-035中去掉了I/O和AWT部分的内容，但增加了对Assertion的考查，同时对Wrapper classes、Collections、hashcode( )和equals( )考查的难度有所加大。所以要根据自己的情况选择合适的版本。我在复习时觉得I/O这一部分的内容较多且不好记忆，就选择了310-035，这个版本较难，也是对自己的一个挑战。<br />　　<br />　　3.根据考查的知识点做练习题。由于认证考试是以试题的形式来考查对知识点的掌握情况，所以多做题是通过考试的最有效方法之一，通过对SL275课程系统的学习，掌握了有关知识点的基础知识以后，必须通过做题来测试自己对知识点的掌握情况，书中写的有关知识点的内容比较概括，有时自己觉得已经掌握了这部分内容，但做题时可能会出现各式各样的错误，比如在声明main( )方法时应为public static void main( String args[ ] ){……}，如果在声明时漏写了static，则在编译时可以通过，而运行时会提示出错信息；如果main( )中的参数写string args[ ]，则在编译时会提示出错，在考试时有类似的题目，故意设一些陷阱，一不小心看似简单的问题却有可能失分。对于给出一段程序代码要求选择其运行结果的题目，最好亲手调试并分析其结果。<br />　　<br />　　对考查Wrapper classes、Collections、hashcode( )和equals( )方法的题目要认真查看API文档(Sun官方网站提供了下载的链接，也可以在Sun网站上在线查看)并做好笔记供以后查阅。通过做题巩固知识点、适应考试题型、找出容易出错的地方，同时对所学知识点也起到了查漏补缺的作用。<br />　　<br />　　4.下载相应的模拟环境进行实战训练，或者在线测试。知识点和题型都掌握以后，还不要急于考试，应该从网上下载一个模拟考试环境的软件进行实战演练。我下载了Jcertify5.0，它需在JDK1.3环境下安装运行，该软件可以设置考查的范围进行practice或者test，是一个很不错的模拟软件，里面的题目与实际考试题目难易程度相当。<br />　　<br />　　通过该软件可以检验你在规定的时间内(120分钟)完成题目的情况及各个知识点掌握的程度，这样就可以根据测验结果有针对性的复习掌握的不太好的知识点，反复测验、练习，直到自己满意为止。<br />　　<br />　　5.考前给自己足够的信心。通过做练习和模拟软件测验，对各个知识点都比较有把握以后，其实这时参加考试已经没有问题了，要相信自己的实力，给自己加油，此时切不可再做较难的题目，我当时就犯了这样的大忌，听说310-035的考试比较难，目前通过的人数不多，我怕考试时的题目比我做的练习题难，在考试的前一天，又做了一套题目，共43题，通过率为仅为42%，这个结果令我很失望，也丧失了自信心，晚上做梦一直考试且考试结果很糟糕，第二天醒来头昏脑胀，本来预约好的考试不得不往后推迟，这是沉痛的教训。<br />　　<br />　　6.预约、考试。知名的IT认证厂商在一些大城市都有指定的考试中心，Sun认证也不例外，考试前三天到Sun指定的考试中心报名，由他们帮你注册，除了考试做题，其他的事情你尽管交给考试中心去做就行了。<br />　　<br />　　7.考试注意事项。按预约的时间提前到达考试中心，熟悉考试环境，稳定情绪，考试中心在考场里设置有监视器，你的一举一动都会被录制下来，所以考试来不得半点虚假，尽早打消“打小抄”的念头。考务人员帮你进入考试系统后，开始进行考试，考试分三个阶段：<br />　　<br />　　(1)正式考试前的问卷调查，给定时间为15分，有10来个问题，给出四个选项供你选择，主要调查你对SCJP要考查的知识点的掌握情况，如对数组、类的定义是精通、是了解但需要别人帮助还是不懂等，这部分内容的回答不影响你的考试成绩，如果你读懂问题的意思了，你可以根据自己的实际情况进行选择，否则你尽管随意选一个，一路Next下去，直到所有的问题做完；<br />　　<br />　　(2)正式考试，共120分钟，选择题在题面中注明了正确答案的数目(如：choose two)，按Next做下一题，单击exhibit按钮查看题目提供的代码；<br />　　<br />　　(3)题目做完后的问卷调查，同样也不影响你的考试结果。问卷结束后紧张、激动的时刻就要到了，此时你可以查看考试结果，考试系统列出你对各个知识点答题情况的一览表，如果你的成绩超过指定的分数底线，则Result即为Pass。<br />　　<br />　　拿到考试结果通知单的一刻是令人兴奋的，考后的总体感觉是SCJP认证并不像自己想象的那么难，只要你做好了充分的准备，拿到SCJP认证证书不成问题。 <img src ="http://www.blogjava.net/xixidabao/aggbug/44488.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-04 00:18 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/04/44488.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA程序员面试32问</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/04/44487.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Wed, 03 May 2006 16:10:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/04/44487.html</guid><description><![CDATA[
		<p>第一，谈谈final, finally, finalize的区别。 <br />final 修饰符（关键字）如果一个类被声明为final，意味着它不能再派生出新的子类，不能作为父类被继承。因此一个类不能既被声明为 abstract的，又被声明为final的。将变量或方法声明为final，可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值，而在以后的引用中只能读取，不可修改。被声明为final的方法也同样只能使用，不能重载 <br />finally 再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常，那么相匹配的 catch 子句就会执行，然后控制就会进入 finally 块（如果有的话）。 <br />finalize?方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的，因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。 </p>
		<p>第二，Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类，是否可以implements(实现)interface(接口)? <br />匿名的内部类是没有名字的内部类。不能extends(继承) 其它类，但一个内部类可以作为一个接口，由另一个内部类实现。 </p>
		<p>第三，Static Nested Class 和 Inner Class的不同，说得越多越好(面试题有的很笼统)。 <br />Nested Class （一般是C++的说法），Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。具体可见http: //www.frontfree.net/articles/services/view.asp?id=704&amp;page=1 <br />注： 静态内部类（Inner Class）意味着1创建一个static内部类的对象，不需要一个外部类对象，2不能从一个static内部类的一个对象访问一个外部类对象 </p>
		<p>第四，&amp;和&amp;&amp;的区别。 <br />&amp;是位运算符。&amp;&amp;是布尔逻辑运算符。 </p>
		<p>第五，HashMap和Hashtable的区别。 <br />都属于Map接口的类，实现了将惟一键映射到特定的值上。 <br />HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。 <br />Hashtable 类似于 HashMap，但是不允许 null 键和 null 值。它也比 HashMap 慢，因为它是同步的。 </p>
		<p>第六，Collection 和 Collections的区别。 <br />Collections是个java.util下的类，它包含有各种有关集合操作的静态方法。 <br />Collection是个java.util下的接口，它是各种集合结构的父接口。 </p>
		<p>第七，什么时候用assert。 <br />断言是一个包含布尔表达式的语句，在执行这个语句时假定该表达式为 true。如果表达式计算为 false，那么系统会报告一个 AssertionError。它用于调试目的： <br />assert(a &gt; 0); // throws an AssertionError if a &lt;= 0 <br />断言可以有两种形式： <br />assert Expression1 ; <br />assert Expression1 : Expression2 ; <br />Expression1 应该总是产生一个布尔值。 <br />Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试信息的 String 消息。 <br />断言在默认情况下是禁用的。要在编译时启用断言，需要使用 source 1.4 标记： <br />javac -source 1.4 Test.java <br />要在运行时启用断言，可使用 -enableassertions 或者 -ea 标记。 <br />要在运行时选择禁用断言，可使用 -da 或者 -disableassertions 标记。 <br />要系统类中启用断言，可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。 <br />可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过，断言不应该用于验证传递给公有方法的参数，因为不管是否启用了断言，公有方法都必须检查其参数。不过，既可以在公有方法中，也可以在非公有方法中利用断言测试后置条件。另外，断言不应该以任何方式改变程序的状态。 </p>
		<p>第八，GC是什么? 为什么要有GC? (基础)。 <br />GC是垃圾收集器。Java 程序员不用担心内存管理，因为垃圾收集器会自动进行管理。要请求垃圾收集，可以调用下面的方法之一： <br />System.gc() <br />Runtime.getRuntime().gc() </p>
		<p>第九，String s = new String("xyz");创建了几个String Object? <br />两个对象，一个是“xyx”,一个是指向“xyx”的引用对象s。 </p>
		<p>第十，Math.round(11.5)等於多少? Math.round(-11.5)等於多少? <br />Math.round(11.5)返回（long）12，Math.round(-11.5)返回（long）-11; <br />第二十一，abstract的method是否可同时是static,是否可同时是native，是否可同时是synchronized? <br />都不能 </p>
		<p>第二十二，接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? <br />接口可以继承接口。抽象类可以实现(implements)接口，抽象类是否可继承实体类，但前提是实体类必须有明确的构造函数。 </p>
		<p>第二十三，启动一个线程是用run()还是start()? <br />启动一个线程是调用start()方法，使线程所代表的虚拟处理机处于可运行状态，这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 </p>
		<p>第二十四，构造器Constructor是否可被override? <br />构造器Constructor不能被继承，因此不能重写Overriding，但可以被重载Overloading。 </p>
		<p>第二十五，是否可以继承String类? <br />String类是final类故不可以继承。 </p>
		<p>第二十六，当一个线程进入一个对象的一个synchronized方法后，其它线程是否可进入此对象的其它方法? <br />不能，一个对象的一个synchronized方法只能由一个线程访问。 </p>
		<p>第二十七，try {}里有一个return语句，那么紧跟在这个try后的finally {}里的code会不会被执行，什么时候被执行，在return前还是后? <br />会执行，在return前执行。 </p>
		<p>第二十八，编程题: 用最有效率的方法算出2乘以8等於几? <br />有C背景的程序员特别喜欢问这种问题。 </p>
		<p>2 &lt;&lt; 3 </p>
		<p>第二十九，两个对象值相同(x.equals(y) == true)，但却可有不同的hash code，这句话对不对? <br />不对，有相同的hash code。 </p>
		<p>第三十，当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并可返回变化后的结果，那么这里到底是值传递还是引用传递? <br />是值传递。Java 编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方法中时，参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变，但对象的引用是永远不会改变的。 </p>
		<p>第三十一，swtich是否能作用在byte上，是否能作用在long上，是否能作用在String上? <br />switch（expr1）中，expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。 </p>
		<p>第三十二，编程题: 写一个Singleton出来。 <br />Singleton模式主要作用是保证在Java应用程序中，一个类Class只有一个实例存在。 <br />一般Singleton模式通常有几种种形式: <br />第一种形式: 定义一个类，它的构造函数为private的，它有一个static的private的该类变量，在类初始化时实例话，通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 <br />public class Singleton { <br />　　private Singleton(){} <br />　　//在自己内部定义自己一个实例，是不是很奇怪？ <br />　　//注意这是private 只供内部调用 <br />　　private static Singleton instance = new Singleton(); <br />　　//这里提供了一个供外部访问本class的静态方法，可以直接访问　　 <br />　　public static Singleton getInstance() { <br />　　　　return instance; 　　 <br />　　 } <br />} <br />第二种形式: <br />public class Singleton { <br />　　private static Singleton instance = null; <br />　　public static synchronized Singleton getInstance() { <br />　　//这个方法比上面有所改进，不用每次都进行生成对象，只是第一次　　　 　 <br />　　//使用时生成实例，提高了效率！ <br />　　if (instance==null) <br />　　　　instance＝new Singleton(); <br />return instance; 　　} <br />} </p>
		<br />
<img src ="http://www.blogjava.net/xixidabao/aggbug/44487.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-04 00:10 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/04/44487.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>