<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[小宇飞刀的BLOG]]></title> 
<link>http://vir.jxstnu.edu.cn/xieyunc/index.php</link> 
<description><![CDATA[小宇的网上家园 飞刀的技术博客]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[小宇飞刀的BLOG]]></copyright>
<item>
<link>http://vir.jxstnu.edu.cn/xieyunc/read.php?218</link>
<title><![CDATA[C++树的实现]]></title> 
<author>xieyunc &lt;xieyunc@jxstnu.cn&gt;</author>
<category><![CDATA[Java&amp;C/C++]]></category>
<pubDate>Thu, 05 Jun 2008 06:40:54 +0000</pubDate> 
<guid>http://vir.jxstnu.edu.cn/xieyunc/read.php?218</guid> 
<description>
<![CDATA[ 
	STL里面没有提供容器树的模板实现，自已写一个：<br/><strong>Tree.h</strong><br/><textarea name="code" class="c++" rows="15" cols="100">//tree.h 头文件
 
#include <list>
#include <algorithm>
using namespace std;
 
struct TreeNode; //定义一个结构体原型
classTree;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//定义一个类原型
classIterator; //定义一个类原型
typedef list<TreeNode*> List; //重命名一个节点链表......</textarea><br/>............<br/>
]]>
</description>
</item><item>
<link>http://vir.jxstnu.edu.cn/xieyunc/read.php?210</link>
<title><![CDATA[C/C++中的堆、栈和队列]]></title> 
<author>xieyunc &lt;xieyunc@jxstnu.cn&gt;</author>
<category><![CDATA[Java&amp;C/C++]]></category>
<pubDate>Thu, 22 May 2008 05:23:43 +0000</pubDate> 
<guid>http://vir.jxstnu.edu.cn/xieyunc/read.php?210</guid> 
<description>
<![CDATA[ 
	<strong>一、什么是堆栈</strong><br/>在计算机领域，堆栈是一个不容忽视的概念，但是很多人甚至是计算机专业的人也没有明确堆栈其实是两种数据结构。<br/>要点：<br/>堆：顺序随意&nbsp;&nbsp;&nbsp;&nbsp;栈：先进后出&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;队列：排队买东西(插入在队尾，删除在队头)<br/><br/><strong>二、堆和栈的区别</strong><br/>①、预备知识—程序的内存分配<br/>一个由c/C++编译的程序占用的内存分为以下几个部分：<br/>1、栈区（stack）— 由编译器自动分配释放 ，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。<br/>2、堆区（heap） — 一般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表。<br/>3、全局区（静态区）（static）—，全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态变量在一块区域， 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。<br/>4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 。<br/>5、程序代码区—存放函数体的二进制代码。<br/><br/>②、例子程序<br/>这是一个前辈写的，非常详细<br/><textarea name="code" class="c++" rows="15" cols="100">//main.cpp
int a = 0; //全局初始化区
char *p1; //全局未初始化区
int main()
&#123;
&nbsp;&nbsp;int b; //栈
&nbsp;&nbsp;char s[] = "abc"; //栈
&nbsp;&nbsp;char *p2; //栈
&nbsp;&nbsp;char *p3 = "123456"; //123456&#92;0在常量区，p3在栈上。
&nbsp;&nbsp;static int c =0； //全局（静态）初始化区
&nbsp;&nbsp;p1 = (char *)new(10);
&nbsp;&nbsp;p2 = (char *)new(20);
&#125;</textarea><br/>分配得来得10和20字节的区域就在堆区。<br/>&nbsp;&nbsp;strcpy(p1, "123456"); 123456&#92;0放在常量区，编译器可能会将它与p3所指向的"123456"优化成一个地方。<br/>&nbsp;&nbsp;<span style="color: #DC143C;">博主注：</span>p1,p2,p3本身在栈中（全局区或静态区其实也是在栈区，只不过是一个特殊的栈区而已）占用机器字长的空间，如32位机占4个字节的空间，64位机则占8个字节的空间，因为操作系统必须保证它可对任意内存地址进行寻址，函数名变量（为何叫它函数名变量呢？因为函数名就是该函数的入口地址，相当于一个标准的机器字节的长度的整型指针）也是如此。<br/><strong>三、堆和栈的理论知识详解</strong><br/>1.申请方式<br/>stack:<br/>&nbsp;&nbsp;由系统自动分配。 例如，声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间<br/>heap:<br/>&nbsp;&nbsp;需要程序员自己申请，并指明大小，如：<br/><textarea name="code" class="c++" rows="15" cols="100">&nbsp;&nbsp;//在c中malloc函数，如：
&nbsp;&nbsp;p1 = (char *)malloc(10);
&nbsp;&nbsp;//在C++中用new运算符，如：
&nbsp;&nbsp;p2 = (char *)new(10);</textarea><br/>&nbsp;&nbsp;但是必须明白，p1、p2本身是在栈中的，只是它们之中的内容是指向堆中新开辟空间的首地址。<br/><br/>2.申请后系统的响应<br/>栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。<br/>堆：首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的 delete语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。<br/><br/>3.申请大小的限制<br/>栈：在Windows下,栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在 WINDOWS下，栈的大小是2M（也有的说是1M，总之是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。<br/>堆：堆是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。<br/><br/>4.申请效率的比较<br/>栈由系统自动分配，速度较快。但程序员是无法控制的。<br/>堆是由new分配的内存，一般速度比较慢，而且容易产生内存碎片,不过用起来最方便.<br/>另外，在WINDOWS下，最好的方式是用VirtualAlloc分配内存，他不是在堆，也不是在栈,而是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活<br/><br/>5.堆和栈中的存储内容<br/>栈： 在函数调用时，第一个进栈的是主函数中函数调用后的下一条指令（函数调用语句的下一条可执行语句）的地址，然后是函数的各个参数，在大多数的C编译器中，参数是由右往左入栈的，然后是函数中的局部变量。<strong><span style="color: #00008B;">注意静态变量是不入栈的。</span></strong><br/>当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，程序由该点继续运行。<br/>堆：一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。<br/><br/>6.存取效率的比较<br/><textarea name="code" class="c++" rows="15" cols="100">char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";</textarea><br/>aaaaaaaaaaa是在运行时刻赋值的；<br/>而bbbbbbbbbbb是在编译时就确定的；<br/><br/>但是，在以后的存取中，在栈上的数组比指针所指向的字符串(例如堆)快。<br/>比如：<br/><textarea name="code" class="c++" rows="15" cols="100">#include ....
int main()
&#123;
&nbsp;&nbsp;char a = 1;
&nbsp;&nbsp;char c[] = "1234567890";
&nbsp;&nbsp;char *p ="1234567890";
&nbsp;&nbsp;a = c[1];
&nbsp;&nbsp;a = p[1];
&nbsp;&nbsp;return 0;
&#125;</textarea><br/>对应的汇编代码可能是：<br/>10: a = c[1];<br/>00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]<br/>0040106A 88 4D FC mov byte ptr [ebp-4],cl<br/><br/>11: a = p[1];<br/>0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]<br/>00401070 8A 42 01 mov al,byte ptr [edx+1]<br/>00401073 88 45 FC mov byte ptr [ebp-4],al<br/><br/>第一种在读取时直接就把字符串中的元素读到寄存器cl中，而第二种则要先把指针值读到edx中，在根据edx读取字符，显然慢了。<br/><br/>7.小结：<br/>堆和栈的区别可以用如下的比喻来看出：<br/>使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是快捷，但是自由度小。<br/>使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。<br/><br/>堆和栈的区别主要是：<br/>操作系统方面的堆和栈，如上面说的那些，不多说了。<br/>还有就是数据结构方面的堆和栈，这些都是不同的概念。这里的堆实际上指的就是（满足堆性质的）优先队列的一种数据结构，第1个元素有最高的优先权；栈实际上就是满足先进后出的性质的数学或数据结构。<br/>虽然堆栈，堆栈的说法是连起来叫，但是他们还是有很大区别的，连着叫只是由于历史的原因。<br/>
]]>
</description>
</item><item>
<link>http://vir.jxstnu.edu.cn/xieyunc/read.php?196</link>
<title><![CDATA[折半查找法的C++原型]]></title> 
<author>xieyunc &lt;xieyunc@jxstnu.cn&gt;</author>
<category><![CDATA[Java&amp;C/C++]]></category>
<pubDate>Thu, 24 Apr 2008 07:30:38 +0000</pubDate> 
<guid>http://vir.jxstnu.edu.cn/xieyunc/read.php?196</guid> 
<description>
<![CDATA[ 
	　　折半查找法也称为二分查找法，它充分利用了元素间的次序关系，采用分治策略，可在最坏的情况下用O(log n)完成搜索任务。<br/><br/><strong>【基本思想】</strong><br/><br/>　　将n个元素分成个数大致相同的两半，取a[n/2]与欲查找的x作比较，如果x=a[n/2]则找到x，算法终止。如果x<a[n/2]，则我们只要在数组a的左半部继续搜索x（这里假设数组元素呈升序排列）。如果x>a[n/2]，则我们只要在数组a的右半部继续搜索x。<br/><br/>　　二分搜索法的应用极其广泛，而且它的思想易于理解。第一个二分搜索算法早在1946 年就出现了，但是第一个完全正确的二分搜索算法直到1962年才出现。Bentley在他的著作《Writing Correct Programs》中写道，90%的计算机专家不能在2小时内写出完全正确的二分搜索算法。问题的关键在于准确地制定各次查找范围的边界以及终止条件的确定，正确地归纳奇偶数的各种情况，其实整理后可以发现它的具体算法是很直观的。<br/><br/><strong>C++描述</strong><br/>Download: BinarySearch.cpp<br/><textarea name="code" class="c++" rows="15" cols="100">
template<class Type> 
int BinarySearch(Type a[],const Type& x,int n) 
&#123; 
&nbsp;&nbsp;&nbsp;&nbsp;int left=0; 
&nbsp;&nbsp;&nbsp;&nbsp;int right=n-1; 
&nbsp;&nbsp;&nbsp;&nbsp;while(left<=right)&#123; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int middle=(left+right)/2; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (x==a[middle]) return middle; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (x>a[middle]) left=middle+1; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else right=middle-1; 
&nbsp;&nbsp;&nbsp;&nbsp;&#125; 
&nbsp;&nbsp;&nbsp;&nbsp;return -1; 
&#125;</textarea><br/><strong>递归实现（recuition）</strong>Download: binary_search_recuition.cpp<br/><textarea name="code" class="c++" rows="15" cols="100">
template<class Record, class Key>
int binary_search( Record * r, const int & low, const int & high, const Key & k )
&#123;
&nbsp;&nbsp;&nbsp;&nbsp;int mid = (low + high)/2;
&nbsp;&nbsp;&nbsp;&nbsp;if( low < high )
&nbsp;&nbsp;&nbsp;&nbsp;&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( k <= r[mid] )
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;binary_search( r, low, mid, k );&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;binary_search( r, mid+1, high, k );
&nbsp;&nbsp;&nbsp;&nbsp;&#125;
&nbsp;&nbsp;&nbsp;&nbsp;else if( low == high )
&nbsp;&nbsp;&nbsp;&nbsp;&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( k == r[mid] )
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return low;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;
&nbsp;&nbsp;&nbsp;&nbsp;&#125;
&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;
&#125;</textarea><br/><strong>迭代实现（iteration）</strong>Download: binary_search_iteration.cpp<br/><textarea name="code" class="c++" rows="15" cols="100">
template<typename Record, typename Key>
int binary_search( Record * r, const int & size, const Key & k )
&#123;
&nbsp;&nbsp;&nbsp;&nbsp;int low=0, high=size-1, mid;
&nbsp;&nbsp;&nbsp;&nbsp;while( low < high )
&nbsp;&nbsp;&nbsp;&nbsp;&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mid = (low + high) / 2;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( k > r[mid] )
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; low = mid + 1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; high = mid;
&nbsp;&nbsp;&nbsp;&nbsp;&#125;
&nbsp;&nbsp;&nbsp;&nbsp;if( low > high )
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;
&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&#123;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if( k == r[low] )
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return low;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;
&nbsp;&nbsp;&nbsp;&nbsp;&#125;
&#125;</textarea>
]]>
</description>
</item><item>
<link>http://vir.jxstnu.edu.cn/xieyunc/read.php?148</link>
<title><![CDATA[C++中的浮点数格式控制]]></title> 
<author>xieyunc &lt;xieyunc@jxstnu.cn&gt;</author>
<category><![CDATA[Java&amp;C/C++]]></category>
<pubDate>Tue, 30 Oct 2007 10:46:39 +0000</pubDate> 
<guid>http://vir.jxstnu.edu.cn/xieyunc/read.php?148</guid> 
<description>
<![CDATA[ 
	使用setprecision(n)可控制输出流显示浮点数的数字个数。C++默认的流输出数值有效位是6。 <br/>如果setprecision(n)与setiosflags(ios::fixed)合用，可以控制小数点右边的数字个数。setiosflags(ios::fixed)是用定点方式表示实数。<br/>如果与setiosnags(ios::scientific)合用， 可以控制指数表示法的小数位数。setiosflags(ios::scientific)是用指数方式表示实数。 <br/>例如，下面的代码分别用浮点、定点和指数方式表示一个实数： <br/><textarea name="code" class="C++" rows="15" cols="100">
#include <iostream>
#include <iomanip> //要用到格式控制符 

using namespace std;

int main() 
&#123; 
 &nbsp; &nbsp;double amount = 22.0/7; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//amount=3.1428571428571428571428571428571
 &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; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//运行结果为：
 &nbsp; &nbsp;cout <<amount <<endl; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //3.14286 &nbsp; 流的有效位数默认设置值为6
 &nbsp; &nbsp;cout <<setprecision(0) <<amount <<endl &nbsp; &nbsp; //3
 &nbsp; &nbsp;<<setprecision(1) <<amount <<endl &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//3 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;没有设置定点输出格式时setprecision(x)表示输出流中的浮点数的数字个数
 &nbsp; &nbsp;<<setprecision(2) <<amount <<endl &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//3.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 不包括负数和小数点
 &nbsp; &nbsp;<<setprecision(3) <<amount <<endl &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//3.14
 &nbsp; &nbsp;<<setprecision(4) <<amount <<endl; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //3.143
 &nbsp; &nbsp;
 &nbsp; &nbsp;cout <<setiosflags(ios::fixed); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //设置为浮点输出格式(即定点输出格式)
 &nbsp; &nbsp;cout <<setprecision(8) <<amount <<endl; &nbsp; &nbsp; //3.14285714 
 &nbsp; &nbsp;//上面二行也可写为：cout<<setprecision(8)<<fixed<<amount<<endl;
 &nbsp; &nbsp;
 &nbsp; &nbsp;cout <<setiosflags(ios::scientific); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //设置成指法输出格式
 &nbsp; &nbsp;cout <<amount <<endl; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //3.14285714e+00 
 &nbsp; &nbsp;
 &nbsp; &nbsp;cout <<setprecision(6); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //重新设置成原默认设置 
 &nbsp; &nbsp;
 &nbsp; &nbsp;return 0;
&#125;</textarea><br/>该程序在32位机器上运行通过。 <br/>在用浮点表示的输出中，setprecision(n)表示有效位数。 <br/>第1行输出数值之前没有设置有效位数，所以用流的有效位数默认设置值6：第2个输出设置了有效位数0，C++最小的有效位数为1，所以作为有效位数设置为1来看待：第3～6行输出按设置的有效位数输出。 <br/>在用定点表示的输出中，setprecision(n)表示小数位数。 <br/>第7行输出是与setiosflags(ios::fixed)合用。所以setprecision(8)设置的是小数点后面的位数，而非全部数字个数。 <br/>在用指数形式输出时，setprecision(n)表示小数位数。 <br/>第8行输出用setiosflags(ios::scientific)来表示指数表示的输出形式。其有效位数沿用上次的设置值8<br/>Tags - <a href="http://vir.jxstnu.edu.cn/xieyunc/tag.php?tag=setprecision" rel="tag">setprecision</a> , <a href="http://vir.jxstnu.edu.cn/xieyunc/tag.php?tag=fixed" rel="tag">fixed</a> , <a href="http://vir.jxstnu.edu.cn/xieyunc/tag.php?tag=setiosflags" rel="tag">setiosflags</a>
]]>
</description>
</item><item>
<link>http://vir.jxstnu.edu.cn/xieyunc/read.php?141</link>
<title><![CDATA[cygwin,在win中开发linux程序]]></title> 
<author>xieyunc &lt;xieyunc@jxstnu.cn&gt;</author>
<category><![CDATA[Java&amp;C/C++]]></category>
<pubDate>Sat, 29 Sep 2007 01:09:06 +0000</pubDate> 
<guid>http://vir.jxstnu.edu.cn/xieyunc/read.php?141</guid> 
<description>
<![CDATA[ 
	从windows到Linux -- 编程篇 -- cygwin,在win中开发linux程序<br/> &nbsp; 乾坤一笑[smileonce] &nbsp; &nbsp;smileonce@126.com &nbsp; 2004-7-23<br/> &nbsp; &nbsp;版权所有 转载请注明出处 &nbsp;CSdn.net/smileonce"><a href="http://blog.csdn.net/smileonce" target="_blank">http://blog.csdn.net/smileonce</a><br/><br/> &nbsp; &nbsp;很多用windows的朋友不习惯于用linux的开发环境。虽然很乐意尝试一下，但是往往怕linux系统<br/>打乱了自己的正常生活：1〉装linux系统把windows系统给搞坏了，导致自己无法正常生活；2>linux开<br/>发上手太难，写出第一个helloworld不亚于Java的难度，环境配置摸不着头脑。 对于此，我的看法是：<br/>路不管平还是陡，终归你要走的，如果你愿意投入到linux开发的社群中来，不会安装linux系统，不会配<br/>置工作环境是不能想象的。(事实上，确实要了解很多东西的原理，不然很难排错：诸如，硬盘引导器的引<br/>导原理、分区结构原理及linux分区结构和文件系统、环境变量的设置、种类繁多的压缩包安装包的解压安<br/>装方法、用户管理权限管理等常用命令、以至于驱动安装系统中文化等等异常琐碎的东东)。<br/><br/>本文试图跳过这个难走的步骤，启用一个win环境下的linux仿真器（和linux下面的命令行开发环境基本<br/>一致），用短短的20分钟的时间，教你做出一个纯正的linux下gcc编译的helloworld。就象是买点心前<br/>先尝尝味道，不也是一件很愉快的事情么？(注：cygwin事实上不仅有此模拟功能，它也是移植Unix<--><br/>win程序的一个很有效的工具，也有人用它来做嵌入式系统开发)<br/><br/>一、cygwin是什么？<br/> &nbsp; 这个问题你最好Google一下"cygwin的历史",或许能够获得更为详尽的答案。简而言之，cygwin是一<br/>个在windows平台上运行的linux模拟环境，是cygnus solutions公司开发的自由软件（该公司开发了<br/>很多好东西，著名的还有eCos，不过现已被Redhat收购）。插一句废话，很多朋友不明白linux和unix的<br/>区别和联系，在此也简要介绍一下。UNIX是一个注册商标，是要满足一大堆条件并且支付可观费用才能够<br/>被授权使用的一个操作系统;linux是unix的克隆版本，是由其创始人Linus和诸多世界知名的黑客手工打<br/>造的一个操作系统。为什么linux和unix之间有很多软件可以很轻松的移植？因为linux也满足POSIX规<br/>范,所以在运行机制上跟unix相近。 &nbsp; <br/> &nbsp; 以下引用网上的一段话（出处：ASPx"><a href="http://blog.csdn.net/glock18/archive/2004/07/10/38275.aspx" target="_blank">http://blog.csdn.net/glock18/archive/2004/07/10/38275.aspx</a>）， <br/>用于说明cygwin的工作机制：cygnus当初首先把gcc，gdb，gas等开发工具进行了改进，使他们能够生成<br/>并解释win32的目标文件。然后，他们要把这些工具移植到windows平台上去。一种方案是基于win32 api<br/>对这些工具的源代码进行大幅修改，这样做显然需要大量工作。因此，他们采取了一种不同的方法——他们写<br/>了一个共享库(就是cygwin dll)，把win32 api中没有的unix风格的调用（如fork,spawn,signals,<br/>select,sockets等）封装在里面，也就是说，他们基于win32 api写了一个unix系统库的模拟层。这样，<br/>只要把这些工具的源代码和这个共享库连接到一起，就可以使用unix主机上的交叉编译器来生成可以在<br/>windows平台上运行的工具集。以这些移植到windows平台上的开发工具为基础，cygnus又逐步把其他的<br/>工具（几乎不需要对源代码进行修改，只需要修改他们的配置脚本）软件移植到windows上来。这样，在<br/>windows平台上运行bash和开发工具、用户工具，感觉好像在unix上工作。关于cygwin实现的更详细描述，<br/>请参考<a href="http://cygwin.com/cygwin-ug-net/highlights.html" target="_blank">http://cygwin.com/cygwin-ug-net/highlights.html</a><br/>二、cygwin的安装。<br/> &nbsp;cygwin的安装文件很容易通过google找到。目前国内的网站上有“网络安装版”和"本地安装版"两种。<br/>标准的发行版应该是 &nbsp;网络安装版。两者并无大不同，下面介绍一下安装的过程。<br/><br/> &nbsp;step1. 下载后，点击安装文件(setup.exe)进行安装，第一个画面是GNU版权说明，点"下一步(N)—>"，<br/>进入安装模式选择画面。<br/><br/> &nbsp;step2. 安装模式有"Install from Internet"、"Download form Internet"、<br/>"Install from Local Directory" 三种。"Install form Internet"就是直接从internet上装，<br/>适用于网速较快的情况。如果你和我一样网速不是很快，或者说装过之后想把下载的安装文件保存起来，<br/>下次不再下载了直接安装，就应该选择"Download form Internet"，下载安装的文件（大约40M左右）。<br/>事实上，所谓的"本地安装版"，也是别人从网上下载全部文件后打的包(适用于中国国情嘛^_^)<br/> &nbsp;<br/> &nbsp;step3. 接下来是选择安装目的路径和安装源文件所在的路径，之后就进入了选择安装包所在的路径。<br/>注意了阿，这里可是重头戏。我第一安装的时候就是没有看清这一步，结果没有把gcc装进去，导致没法编<br/>译文件。<br/> &nbsp; + All &nbsp;Default<br/> &nbsp; &nbsp; &nbsp;+ Admin Default<br/> &nbsp; &nbsp; &nbsp; ....<br/> &nbsp; &nbsp; &nbsp;+ Devel &nbsp; Default<br/> &nbsp; &nbsp; &nbsp;+ Editors Default<br/> &nbsp; &nbsp; &nbsp;....<br/>如上图所示，你在这个TreeView的某个节点上双击，就可以改变它的状态，如Default、Install、<br/>Uninstall、Reinstall四种状态。默认的都是Default状态，很多工具的默认状态都是不安装。<br/>在这里我选择了在All上点Install，全部安装，以免后患。（全部安装大概不到200M的空间）<br/><br/> &nbsp;step4. 点下一步，安装成功。它会自动在你的桌面上建立一个快捷方式。<br/><br/> &nbsp;好了，下面就开始我的linux旅程了。双击cygwin的快捷方式进入系统。<br/>首先介绍几个简单的linux命令。<br/>pwd &nbsp; 显示当前的路径<br/>cd &nbsp; &nbsp;改变当前路径，无参数时进入对应用户的home目录<br/>ls &nbsp; &nbsp;列出当前目录下的文件。此命令有N多参数，比如ls -al<br/>ps &nbsp; &nbsp;列出当前系统进程<br/>kill &nbsp;杀死某个进程<br/>mkdir 建立目录<br/>rmdir 删除目录<br/>rm &nbsp; &nbsp;删除文件<br/>mv &nbsp; &nbsp;文件改名或目录改名<br/>man &nbsp; 联机帮助<br/>less &nbsp;显示文件的最末几行<br/><br/>由于linux下面的命令大多都有很多参数，可以组合使用。所以，每当你不会或者记不清楚改用那个参数，<br/>那个开关的时候，可以用man来查找，比如，我想查找ls怎么使用，可以键入<br/>$ &nbsp;man ls<br/>系统回显信息如下：<br/>LS(1) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FSF &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;LS(1)<br/>NAME<br/> &nbsp; &nbsp; &nbsp; ls - list directory contents<br/>SYNOPSIS<br/> &nbsp; &nbsp; &nbsp; ls [OPTION]... [FILE]...<br/>DESCRIPTION<br/> &nbsp; &nbsp; &nbsp; List information about the FILEs (the current directory by<br/> &nbsp; &nbsp; &nbsp; default). &nbsp;Sort entries alphabetically if none of -cftuSUX<br/> &nbsp; &nbsp; &nbsp; nor --sort.<br/> &nbsp; &nbsp; &nbsp; -a, --all<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do not hide entries starting with .<br/> &nbsp; &nbsp; &nbsp; -A, --almost-all<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do not list implied . and ..<br/> &nbsp; &nbsp; &nbsp; -b, --escape<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print octal escapes for nongraphic characters<br/> &nbsp; &nbsp; &nbsp; --block-size=SIZE<br/> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; use SIZE-byte blocks<br/>:<br/>很全是吧，嘿嘿。<br/>三、好了，多说无意，让我们来写一个hello world程序。<br/># cd <br/> &nbsp;进入了/home/administrator目录，我当前的登陆帐号是administrator<br/><br/># mkdir source<br/> &nbsp;建立一个叫做source的子目录<br/><br/># cd source<br/> &nbsp;进入 /home/administrator/source<br/><br/># vim hello.c<br/><br/> &nbsp; 启动vim编辑器，来编写程序。好了，现在有必要简要介绍一下vim。<br/>在linux界，有两大编辑器最有历史。其一是vi，其二是emacs。vi现在已经演化成了vim，比当前的vi<br/>更为强大。vim和emacs是两种截然不同的东西，vim强调用简洁的命令来完成功能，无论是查找、替换、<br/>正则表达式匹配、编译、链接、排错、函数间跳转等等等等都在命令行中完成，并且它把方向键也集成在<br/>了hjkl四个键之上，可以说，用了vim基本上可以不用鼠标了:p &nbsp;emacs则是在功能强大上做文章，版本<br/>控制、模拟多种编辑环境、对文本进行各种操作，可谓之一个强大的文本处理系统。emacs是用诸如<br/>Ctrl+Alt+K 之类的组合键来控制的；vim则是用se(set 的缩写)等简洁命令来控制的。<br/> &nbsp; 由于cygwin中只提供了vim(能不能自己装emacs我还没有试过)，我们就先体验一下vim吧:p<br/>vim加载文本文件后分为命令模式和插入模式两种。插入模式，顾名思义就是输入编辑文本；命令模式，则<br/>是输入各种控制命令，常用的有：<br/> &nbsp; i &nbsp;进入编辑模式<br/> &nbsp; h &nbsp;左移<br/> &nbsp; j &nbsp;下移<br/> &nbsp; k &nbsp;上移<br/> &nbsp; l &nbsp;右移<br/> &nbsp; w &nbsp;存盘<br/> &nbsp; q &nbsp;退出<br/> &nbsp; ！ 强调执行<br/><br/>有些命令是可以组合使用的，如果你修改了某个文件，想存盘退出，则可以使用wq；如果你想放弃存盘，<br/>直接退出则可以使用q! &nbsp;好了大家体验一下吧，记住：从编辑模式退回命令模式按"Esc"，再按":"，在输入指<br/>令，从命令模式进入编辑模式用i，初次使用vim肯定很难受，嘿嘿，不过用多了就习惯了，确实很省劲。<br/><br/>我们编辑hello.c文件，输入：<br/>#include <stdio.h><br/><br/>int main(void) &#123;<br/> &nbsp;printf( "Hello World!");<br/>&#125;<br/>然后，输入wq命令退到命令行。<br/>输入编译指令：<br/># gcc hello.c -o hello<br/>编译成功后可以看一下<br/># ls <br/>看到hello.exe了吧，嘿嘿。<br/>好，让我们运行看看。<br/># ./hello<br/>效果如何？不错吧？ :)<br/><br/>okey，现在玩一个C++的hello world，<br/># vim world.cpp<br/>输入：<br/>#include <iostream><br/>using namespace std;<br/><br/>void main() &#123;<br/> &nbsp;cout << "Hello World!";<br/>&#125;<br/>编译C++程序要用g++<br/># g++ world.cpp -o world<br/>运行一下哈，<br/># ./world<br/><br/>如何，效果不错吧？诸位都是高手，走到这步应该编些简单程序都不成问题了吧:p<br/>好了，关于如何编写makefile文件，如何用gdb下次再说了。<br/><br/><br/>Tags - <a href="http://vir.jxstnu.edu.cn/xieyunc/tag.php?tag=cygwin" rel="tag">cygwin</a> , <a href="http://vir.jxstnu.edu.cn/xieyunc/tag.php?tag=gun" rel="tag">gun</a> , <a href="http://vir.jxstnu.edu.cn/xieyunc/tag.php?tag=c%252B%252B" rel="tag">c++</a> , <a href="http://vir.jxstnu.edu.cn/xieyunc/tag.php?tag=gcc" rel="tag">gcc</a>
]]>
</description>
</item><item>
<link>http://vir.jxstnu.edu.cn/xieyunc/read.php?110</link>
<title><![CDATA[Java路径问题最终解决方案之一]]></title> 
<author>xieyunc &lt;xieyunc@jxstnu.cn&gt;</author>
<category><![CDATA[Java&amp;C/C++]]></category>
<pubDate>Tue, 02 Jan 2007 03:20:07 +0000</pubDate> 
<guid>http://vir.jxstnu.edu.cn/xieyunc/read.php?110</guid> 
<description>
<![CDATA[ 
	前言<br/><br/>　　Java的路径问题，非常难搞。最近的工作涉及到创建和读取文件的工作，这里我就给大家彻底得解决Java路径问题。<br/><br/>　　我编写了一个方法，比ClassLoader.getResource(String 相对路径)方法的能力更强。它可以接受“../”这样的参数，允许我们用相对路径来定位classpath外面的资源。这样，我们就可以使用相对于classpath的路径，定位所有位置的资源！<br/><br/>　　Java路径<br/><br/>　　Java中使用的路径，分为两种：绝对路径和相对路径。具体而言，又分为四种：<br/><br/>　　一、URI形式的绝对资源路径<br/><br/>　　如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b<br/><br/>　　URL是URI的特例。URL的前缀/协议，必须是Java认识的。URL可以打开资源，而URI则不行。<br/><br/>　　URL和URI对象可以互相转换，使用各自的toURI(),toURL()方法即可！<br/><br/>　　二、本地系统的绝对路径<br/><br/>　　D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b<br/><br/>　　Java.io包中的类，需要使用这种形式的参数。<br/><br/>　　但是，它们一般也提供了URI类型的参数，而URI类型的参数，接受的是URI样式的String。因此，通过URI转换，还是可以把URI样式的绝对路径用在java.io包中的类中。<br/><br/>　　三、相对于classpath的相对路径<br/><br/>　　如：相对于<br/><br/>　　file:/D:/java/eclipse32/workspace/jbpmtest3/bin/这个路径的相对路径。其中，bin是本项目的classpath。所有的Java源文件编译后的.class文件复制到这个目录中。<br/><br/>　　四、相对于当前用户目录的相对路径<br/><br/>　　就是相对于System.getProperty("user.dir")返回的路径。<br/><br/>　　对于一般项目，这是项目的根路径。对于JavaEE服务器，这可能是服务器的某个路径。这个并没有统一的规范！<br/><br/>　　所以，绝对不要使用“相对于当前用户目录的相对路径”。然而：<br/><br/>　　默认情况下，java.io 包中的类总是根据当前用户目录来分析相对路径名。此目录由系统属性 user.dir 指定，通常是 Java 虚拟机的调用目录。<br/><br/>　　这就是说，在使用java.io包中的类时，最好不要使用相对路径。否则，虽然在J2SE应用程序中可能还算正常，但是到了J2EE程序中，一定会出问题！而且这个路径，在不同的服务器中都是不同的！<br/><br/>　　相对路径最佳实践<br/><br/>　　推荐使用相对于当前classpath的相对路径<br/><br/>　　因此，我们在使用相对路径时，应当使用相对于当前classpath的相对路径。<br/><br/>　　ClassLoader类的getResource(String name),getResourceAsStream(String name)等方法，使用相对于当前项目的classpath的相对路径来查找资源。<br/><br/>　　读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。<br/><br/>　　通过查看ClassLoader类及其相关类的源代码，我发现，它实际上还是使用了URI形式的绝对路径。通过得到当前classpath的URI形式的绝对路径，构建了相对路径的URI形式的绝对路径。（这个实际上是猜想，因为JDK内部调用了SUN的源代码，而这些代码不属于JDK，不是开源的。）<br/> 　　相对路径本质上还是绝对路径<br/><br/>　　因此，归根结底，Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法，都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径，从而找到资源的！<br/><br/>　　得到classpath和当前类的绝对路径的一些方法<br/><br/>　　下面是一些得到classpath和当前类的绝对路径的一些方法。你可能需要使用其中的一些方法来得到你需要的资源的绝对路径。<br/><br/>　　1.FileTest.class.getResource("")<br/><br/>　　得到的是当前类FileTest.class文件的URI目录。不包括自己！<br/><br/>　　如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/com/test/<br/><br/>　　2.FileTest.class.getResource("/")<br/><br/>　　得到的是当前的classpath的绝对URI路径。<br/><br/>　　如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br/><br/>　　3.Thread.currentThread().getContextClassLoader().getResource("")<br/><br/>　　得到的也是当前ClassPath的绝对URI路径。<br/><br/>　　如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br/><br/>　　4.FileTest.class.getClassLoader().getResource("")<br/><br/>　　得到的也是当前ClassPath的绝对URI路径。<br/><br/>　　如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br/><br/>　　5.ClassLoader.getSystemResource("")<br/><br/>　　得到的也是当前ClassPath的绝对URI路径。<br/><br/>　　如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br/><br/>　　我推荐使用Thread.currentThread().getContextClassLoader().getResource("")来得到当前的classpath的绝对路径的URI表示法。<br/><br/>　　Web应用程序中资源的寻址<br/><br/>　　上文中说过，当前用户目录，即相对于System.getProperty("user.dir")返回的路径。<br/><br/>　　对于JavaEE服务器，这可能是服务器的某个路径，这个并没有统一的规范！<br/><br/>　　而不是我们发布的Web应用程序的根目录！<br/><br/>　　这样，在Web应用程序中，我们绝对不能使用相对于当前用户目录的相对路径。<br/><br/>　　在Web应用程序中，我们一般通过ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。<br/><br/>　　这样，我们只需要提供相对于Web应用程序根目录的路径，就可以构建出定位资源的绝对路径。<br/><br/>　　这是我们开发Web应用程序时一般所采取的策略。<br/><br/>　　Web应用程序，可以作为Web应用程序进行发布和运行。但是，我们也常常会以JavaSE的方式来运行Web应用程序的某个类的main方法。或者，使用JUnit测试。这都需要使用JavaSE的方式来运行。<br/><br/>　　这样，我们就无法使用ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。<br/><br/>　　而JDK提供的ClassLoader类，它的getResource(String name),getResourceAsStream(String name)等方法，使用相对于当前项目的classpath的相对路径来查找资源。<br/><br/>　　读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。<br/><br/>　　它们都只能使用相对路径来读取classpath下的资源，无法定位到classpath外面的资源。<br/><br/>　　Classpath外配置文件读取问题<br/><br/>　　如，我们使用测试驱动开发的方法，开发Spring、Hibernate、iBatis等使用配置文件的Web应用程序，就会遇到问题。<br/><br/>　　尽管Spring自己提供了FileSystem（也就是相对于user,dir目录）来读取Web配置文件的方法，但是终究不是很方便。而且与Web程序中的代码使用方式不一致！<br/><br/>　　至于Hibernate，iBatis就更麻烦了！只有把配置文件移到classpath下，否则根本不可能使用测试驱动开发！<br/><br/>　　这怎么办？<br/><br/>　　通用的相对路径解决办法<br/><br/>　　面对这个问题，我决定编写一个助手类ClassLoaderUtil，提供一个便利方法[public static URL getExtendResource(String relativePath)]。在Web应用程序等一切Java程序中，需要定位classpath外的资源时，都使用这个助手类的便利方法，而不使用Web应用程序特有的ServletContext.getRealPath("/")方法来定位资源。<br/><br/>　　利用classpath的绝对路径，定位所有资源<br/><br/>　　这个便利方法的实现原理，就是“利用classpath的绝对路径，定位所有资源”。<br/><br/>　　ClassLoader类的getResource("")方法能够得到当前classpath的绝对路径，这是所有Java程序都拥有的能力，具有最大的适应性！<br/>而目前的JDK提供的ClassLoader类的getResource(String 相对路径)方法，只能接受一般的相对路径。这样，使用ClassLoader类的getResource(String 相对路径)方法就只能定位到classpath下的资源。<br/><br/>　　如果，它能够接受“../”这样的参数，允许我们用相对路径来定位classpath外面的资源，那么我们就可以定位位置的资源！<br/><br/>　　当然，我无法修改ClassLoader类的这个方法，于是，我编写了一个助手类ClassLoaderUtil类，提供了[public static URL getExtendResource(String relativePath)]这个方法。它能够接受带有“../”符号的相对路径，实现了自由寻找资源的功能。<br/><br/>　　通过相对classpath路径实现自由寻找资源的助手类的源代码：<br/><div class="code"><br/>import java.io.IOException;<br/>import java.io.InputStream;<br/>import java.net.MalformedURLException;<br/>import java.net.URL;<br/>import java.util.Properties;<br/><br/>import org.apache.commons.logging.Log;<br/>import org.apache.commons.logging.LogFactory;<br/><br/>/**<br/>*@author沈东良shendl_s@hotmail.com<br/>*Nov29,2006 10:34:34AM<br/>*用来加载类，classpath下的资源文件，属性文件等。<br/>*getExtendResource(StringrelativePath)方法，可以使用../符号来加载classpath外部的资源。<br/>*/<br/>publicclass ClassLoaderUtil &#123;<br/>　privatestatic Log log=LogFactory.getLog(ClassLoaderUtil.class);<br/>　/**<br/>　*Thread.currentThread().getContextClassLoader().getResource(&quot;&quot;)<br/>　*/<br/><br/>　/**<br/>　*加载Java类。 使用全限定类名<br/>　*@paramclassName<br/>　*@return<br/>　*/<br/>　publicstatic Class loadClass(String className) &#123;<br/>　　try &#123;<br/>　　　return getClassLoader().loadClass(className);<br/>　　&#125; catch (ClassNotFoundException e) &#123;<br/>　　　thrownew RuntimeException(&quot;class not found &#039;&quot;+className+&quot;&#039;&quot;, e);<br/>　　&#125;<br/>　&#125;<br/>　/**<br/>　*得到类加载器<br/>　*@return<br/>　*/<br/>　publicstatic ClassLoader getClassLoader() &#123;<br/>　　return ClassLoaderUtil.class.getClassLoader();<br/>　&#125;<br/>　/**<br/>　*提供相对于classpath的资源路径，返回文件的输入流<br/>　*@paramrelativePath必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源，需要使用　../来查找<br/>　*@return 文件输入流<br/>　*@throwsIOException<br/>　*@throwsMalformedURLException<br/>　*/<br/>　publicstatic InputStream getStream(String relativePath) throws MalformedURLException, IOException &#123;<br/>　　if(!relativePath.contains(&quot;../&quot;))&#123;<br/>　　　return getClassLoader().getResourceAsStream(relativePath);<br/>　　&#125;else&#123;<br/>　　　return ClassLoaderUtil.getStreamByExtendResource(relativePath);<br/>　　&#125;<br/>　&#125;<br/>　/**<br/>　*<br/>　*@paramurl<br/>　*@return<br/>　*@throwsIOException<br/>　*/<br/>　publicstatic InputStream getStream(URL url) throws IOException&#123;<br/>　　if(url!=null)&#123;<br/>　　　return url.openStream();<br/>　　&#125;else&#123;<br/>　　　returnnull;<br/>　　&#125;<br/>　&#125;<br/>　/**<br/>　*<br/>　*@paramrelativePath必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源，需要使用　../来查找<br/>　*@return<br/>　*@throwsMalformedURLException<br/>　*@throwsIOException<br/>　*/<br/>　publicstatic InputStream getStreamByExtendResource(String relativePath) throws MalformedURLException, IOException&#123;<br/>　　return ClassLoaderUtil.getStream(ClassLoaderUtil.getExtendResource(relativePath));<br/>　&#125;<br/><br/>　/**<br/>　*提供相对于classpath的资源路径，返回属性对象，它是一个散列表<br/>　*@paramresource<br/>　*@return<br/>　*/<br/>　publicstatic Properties getProperties(String resource) &#123;<br/>　　Properties properties = new Properties();<br/>　　try &#123;<br/>　　　properties.load(getStream(resource));<br/>　　&#125; catch (IOException e) &#123;<br/>　　　thrownew RuntimeException(&quot;couldn&#039;t load properties file &#039;&quot;+resource+&quot;&#039;&quot;, e);<br/>　　&#125;<br/>　　return properties;<br/>　&#125;<br/>　/**<br/>　*得到本Class所在的ClassLoader的Classpat的绝对路径。<br/>　*URL形式的<br/>　*@return<br/>　*/<br/>　publicstatic String getAbsolutePathOfClassLoaderClassPath()&#123;<br/>　　ClassLoaderUtil.log.info(ClassLoaderUtil.getClassLoader().getResource(&quot;&quot;).toString());<br/>　　return ClassLoaderUtil.getClassLoader().getResource(&quot;&quot;).toString();<br/>　&#125;<br/>　/**<br/>　*<br/>　*@paramrelativePath 必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源，需要使　用../来查找<br/>　*@return资源的绝对URL<br/>　*@throwsMalformedURLException<br/>　*/<br/>　publicstatic URL getExtendResource(String relativePath) throws MalformedURLException&#123;<br/>　　ClassLoaderUtil.log.info(&quot;传入的相对路径：&quot;+relativePath) ;<br/>　　//ClassLoaderUtil.log.info(Integer.valueOf(relativePath.indexOf(&quot;../&quot;))) ;<br/>　　if(!relativePath.contains(&quot;../&quot;))&#123;<br/>　　　return ClassLoaderUtil.getResource(relativePath);<br/>　　&#125;<br/>　　String classPathAbsolutePath=ClassLoaderUtil.getAbsolutePathOfClassLoaderClassPath();<br/>　　if(relativePath.substring(0, 1).equals(&quot;/&quot;))&#123;<br/>　　　relativePath=relativePath.substring(1);<br/>　　&#125;<br/>　　ClassLoaderUtil.log.info(Integer.valueOf(relativePath.lastIndexOf(&quot;../&quot;))) ;<br/>　　String wildcardString=relativePath.substring(0,relativePath.lastIndexOf(&quot;../&quot;)+3);<br/>　　relativePath=relativePath.substring(relativePath.lastIndexOf(&quot;../&quot;)+3);<br/>　　int containSum=ClassLoaderUtil.containSum(wildcardString, &quot;../&quot;);<br/>　　classPathAbsolutePath= ClassLoaderUtil.cutLastString(classPathAbsolutePath, &quot;/&quot;, containSum);<br/>　　String resourceAbsolutePath=classPathAbsolutePath+relativePath;<br/>　　ClassLoaderUtil.log.info(&quot;绝对路径：&quot;+resourceAbsolutePath) ;<br/>　　URL resourceAbsoluteURL=new URL(resourceAbsolutePath);<br/>　　return resourceAbsoluteURL;<br/>　&#125;<br/>　/**<br/>　*<br/>　*@paramsource<br/>　*@paramdest<br/>　*@return<br/>　*/<br/>　privatestaticint containSum(String source,String dest)&#123;<br/>　　int containSum=0;<br/>　　int destLength=dest.length();<br/>　　while(source.contains(dest))&#123;<br/>　　　containSum=containSum+1;<br/>　　　source=source.substring(destLength);<br/>　　&#125;<br/>　　return containSum;<br/>　&#125;<br/>　/**<br/>　*<br/>　*@paramsource<br/>　*@paramdest<br/>　*@paramnum<br/>　*@return<br/>　*/<br/>　privatestatic String cutLastString(String source,String dest,int num)&#123;<br/>　　// String cutSource=null;<br/>　　for(int i=0;i&lt;num;i++)&#123;<br/>　　　source=source.substring(0, source.lastIndexOf(dest, source.length()-2)+1);<br/>　　&#125;<br/>　　return source;<br/>　&#125;<br/>　/**<br/>　*<br/>　*@paramresource<br/>　*@return<br/>　*/<br/>　publicstatic URL getResource(String resource)&#123;<br/>　　ClassLoaderUtil.log.info(&quot;传入的相对于classpath的路径：&quot;+resource) ;<br/>　　return ClassLoaderUtil.getClassLoader().getResource(resource);<br/>　&#125;<br/>　/**<br/>　*@paramargs<br/>　*@throwsMalformedURLException<br/>　*/<br/>　publicstaticvoid main(String&#91;&#93; args) throws MalformedURLException &#123;<br/>　　//ClassLoaderUtil.getExtendResource(&quot;../spring/dao.xml&quot;);<br/>　　//ClassLoaderUtil.getExtendResource(&quot;../../../src/log4j.properties&quot;);<br/>　　ClassLoaderUtil.getExtendResource(&quot;log4j.properties&quot;);<br/>　　System.out.println(ClassLoaderUtil.getClassLoader().getResource(&quot;log4j.properties&quot;).toString());<br/>　&#125;<br/>&#125; <br/></div><br/>　　后记<br/><br/>　　ClassLoaderUtil类的public static URL getExtendResource(String relativePath)，虽然很简单，但是确实可以解决大问题。<br/><br/>　　不过这个方法还是比较简陋的。我还想在未来有空时，进一步增强它的能力。比如，增加Ant风格的匹配符。用**代表多个目录，*代表多个字符，？代表一个字符。达到Spring那样的能力，一次返回多个资源的URL，进一步方便大家开发。<br/><br/>　　总结：<br/><br/>　　1.尽量不要使用相对于System.getProperty("user.dir")当前用户目录的相对路径。这是一颗定时炸弹，随时可能要你的命。<br/><br/>　　2.尽量使用URI形式的绝对路径资源。它可以很容易的转变为URI,URL，File对象。<br/><br/>　　3.尽量使用相对classpath的相对路径。不要使用绝对路径。使用上面ClassLoaderUtil类的public static URL getExtendResource(String relativePath)方法已经能够使用相对于classpath的相对路径定位所有位置的资源。<br/><br/>　　4.绝对不要使用硬编码的绝对路径。因为，我们完全可以使用ClassLoader类的getResource("")方法得到当前classpath的绝对路径。<br/>使用硬编码的绝对路径是完全没有必要的！它一定会让你死的很难看！程序将无法移植！<br/><br/>　　如果你一定要指定一个绝对路径，那么使用配置文件，也比硬编码要好得多！ <br/><br/>　　当然，我还是推荐你使用程序得到classpath的绝对路径来拼资源的绝对路径！
]]>
</description>
</item><item>
<link>http://vir.jxstnu.edu.cn/xieyunc/read.php?109</link>
<title><![CDATA[利用Java技术编写桌面软件基础]]></title> 
<author>xieyunc &lt;xieyunc@jxstnu.cn&gt;</author>
<category><![CDATA[Java&amp;C/C++]]></category>
<pubDate>Tue, 02 Jan 2007 02:16:24 +0000</pubDate> 
<guid>http://vir.jxstnu.edu.cn/xieyunc/read.php?109</guid> 
<description>
<![CDATA[ 
	　　在学习Java编程语言的细节和语法时，我们会碰到这样一个问题：开发桌面应用软件需要使用哪些Java技术，应当引入哪些package？这一问题的答案取决于开发的应用软件类型和它的作用。 <br/><br/>　　这篇文章面向初学Java技术的开发人员，它描述了开发不同类型的桌面应用软件涉及的技术，以及何时决定使用它们。以后的文章将详细阐述如何使用这些技术，以及如何联合使用它们。首先，我们将开始在这篇文章中学习部分技术。 <br/><br/>　　要使用这篇文章中的任何技术，我们必须在计算机上安装Java平台。 <br/><br/>　　桌面应用软件的类型、外观、功能<br/><br/>　　在学习大量的Java技术前，我们必须搞清楚谁会使用我们的应用软件、他们如何使用它、它在个人电脑还是在网络上运行，以及应用软件采用什么样的外观或图形用户界面（GUI）？ <br/><br/>　　在开发应用软件前，考虑下面5个方面： <br/><br/>　　·应用软件的发布<br/><br/>　　·是否采用GUI<br/><br/>　　·应用软件的功能<br/><br/>　　·应用软件的部署<br/><br/>　　·其它Java技术<br/><br/>　　应用软件的发布<br/><br/>　　首先，我们必须确定我们的应用软件是否是分布式软件。分布式应用软件运行在多台计算机上，并通过一个网络通讯。一些分布式应用软件是二个独立的软件：后端服务器软件和前端客户端软件。后端软件运行在Solaris或Linux等共享系统上，管理磁盘、打印机、调制解调器等共享资源。后端软件包含有应用软件主要的处理能力；前端客户端软件运行在工作站或个人电脑上，它是用户在使用应用软件时能够看到的部分。前端客户端软件处理用户界面功能，例如接收键盘的输入、在显示屏上显示输出。 <br/><br/>　　分布式应用软件可以非常简单，只在一台客户端计算机和一台服务器上运行；也可以很复杂，在多台客户端和服务器上运行。非分布式应用软件在本地计算机上运行，无需访问网络。例如，我们可以编写一个只在本地机上运行的简单的计算器软件，我们也可以使这类软件成为分布式软件。 <br/><br/>　　通常，我们开发的应用软件都是分布式软件，在网络或互联网上运行，供许多计算机使用。 <br/><br/>　　要开发分布式应用软件，我们需要学习和使用Java Remote Method Invocation（Java RMI），远程Java对象的方法可以被其它Java虚拟机（JVM）调用。Java RMI利用对象序列化对参数进行序列化和反序列化，它不会截断类型，支持真正的面向对象的多态性。 <br/><br/>　　下面是实现Java RMI需要用到的一些package： <br/><br/>　　·java.rmi <br/>　　·java.rmi.activation <br/>　　·java.rmi.dgc <br/>　　·java.rmi.registry <br/>　　·java.rmi.server <br/><br/>　　注意，下面的package名字是以"javax"而非"java"开头的： <br/><br/>　　·javax.rmi <br/>　　·javax.rmi.CORBA <br/>　　·javax.rmi.ssl <br/><br/>　　是否采用GUI<br/><br/>　　应用软件通常包含有许多具有不同功能的微型软件。一些微型软件有图形用户界面（GUI）━━可能只是一个简单的窗口或对话框，许多微型软件没有GUI。但是，应用软件本身可能有一个主GUI━━包含有菜单、按钮、工具条、文本框、其它图形功能。GUI主要用于用户输入━━无论用户点击一个按钮或输入信息，GUI也可以向用户提供更多的信息。另外，GUI组件也可以向用户显示系统返回的信息。 <br/><br/>　　要创建GUI，我们需要使用Java Foundation Classes/Swing（JFC/Swing）和Abstract Window Toolkit（AWT）API。那些package中的许多类和界面使我们能够方便地创建按钮、check-box对象、文本框、其它组件，以及组织它们的组件。 <br/><br/>　　创建应用软件的框架和所有GUI组件最为简单的方式是使用NetBeans IDE等集成开发环境（IDE）。该IDE使我们能够拖放组件，由它为我们编写复杂的组件代码。学习使用它非常容易，能够为我们节约大量的时间。但是，所有开发人员必须理解JFC/Swing和AWT的工作原理，因此我们必须亲手进行足够的实践，理解这些概念。 <br/><br/>　　编写GUI软件使用的主要Package<br/><br/>　　面向AWT <br/><br/>　　·java.applet <br/>　　·java.awt <br/>　　·java.awt.color <br/>　　·java.awt.datatransfer <br/>　　·java.awt.dnd <br/>　　·java.awt.event <br/>　　·java.awt.font <br/>　　·java.awt.geom <br/>　　·java.awt.im <br/>　　·java.awt.im.spi <br/>　　·java.awt.image <br/>　　·java.awt.image.renderable <br/>　　·java.awt.print <br/><br/>　　面向JFC/Swing <br/><br/>　　·javax.swing <br/>　　·javax.swing.border <br/>　　·javax.swing.colorchooser <br/>　　·javax.swing.event <br/>　　·javax.swing.filechooser <br/>　　·javax.swing.plaf <br/>　　·javax.swing.plaf.basic <br/>　　·javax.swing.plaf.metal <br/>　　·javax.swing.plaf.multi <br/>　　·javax.swing.plaf.synth <br/>　　·javax.swing.table <br/>　　·javax.swing.text <br/>　　·javax.swing.text.html <br/>　　·javax.swing.text.html.parser <br/>　　·javax.swing.text.rtf <br/>　　·javax.swing.tree <br/>　　·javax.swing.undo <br/><br/>　　当然，我们无需引入所有这些package，只需引入我们使用的package即可。 如果我们使用NetBeans IDE，就会发现，在我们使用JFC/Swing或AWT组件时，它就会在代码中创建引入语句。仅仅从它们的名字中，我们就可以很好地理解这些package的作用。 <br/><br/>　　应用软件的功能<br/><br/>　　这篇文章不可能讨论我们在应用软件中使用的所有功能，但一些功能对于许多或大多数应用软件而言是通用的。 <br/><br/>　　要使GUI中的按钮、菜单、文本框完成一些功能，我们需要理解事件处理机制。事件处理程序（event handler）是一个类，包含有当用户点击一个按钮或选择一个菜单时会执行的指令。事件处理程序可以执行许多操作，应用软件可以在GUI中显示反馈信息、在文件或数据库中写入数据、进行数学运算、显示结果，或者完成一些简单的操作，例如打开一个对话框供用户输入更多的信息。 <br/><br/>　　读写数据<br/><br/>　　将用户输入的信息写入一个文件系统，以及在GUI中显示从文件中读取的数据，是应用软件中常见的操作。例如，用户可以在一个GUI表单中输入姓名、地址、电话号码，在服务器上注册软件。处理这些数据的一种方式是让应用软件将数据写入到主机或服务器的一个文件中。 <br/>Java.io package通过数据流、序列化、文件系统提供系统输入/输出（I/O）。一个I/O流代表着一个输入源或一个输出目标。流可以代表许多不同类型的源或目标，包括磁盘文件、设备、其它软件、内存数组。流支持许多不同种类的数据，包括字节、简单的数据类型、本地化字母、对象。一些流只是简单地传输数据，另外一些类则处理和转换数据。 <br/><br/>　　无论内部工作原理如何，对于使用它们的软件而言，所有的流都是相同的简单模式。一个流就是一个数据序列。在java.io package中被使用得比较多的二个类是FileInputStream和FileOutputStream，这二者都创建与文件相连的字节流。<br/><br/>　　存储数据的一种常用方法是使用数据库管理系统。要在软件中编写向数据库写数据、然后获得结果的软件代码，我们就需要学习使用JDBC API，JDBC API提供Java编程语言统一的数据访问方法，使我们能够访问几乎所有数据源━━从关系数据库到电子表格，再到普通文件。JDBC技术还提供了用于开发工具和其它接口的通用代码。 <br/><br/>　　JDBC API支持数据库访问的二层和三层模式<br/><br/>　　在二层模式中，Java applet或应用软件直接与数据源通讯。这种方法要求能够与应用软件访问的特定数据源直接通讯的JDBC驱动程序。 用户的命令被传输给数据库或其它数据源，这些命令的结果会被返送给用户。数据源可能位于用户通过网络连接的其它计算机上，这被称为是客户机-服务器配置：用户的计算机是客户机，托管有数据源的计算机则是服务器。而网络则可能是一家公司的内联网或互联网。 <br/><br/>　　在三层模式中，应用软件将命令发送给中间层，中间层再将命令发送给数据源。在处理命令后，数据源将结果返回给中间层，中间层会将结果返回给用户。管理信息系统（MIS）主管发现三层模式非常有吸引力，因为中间层使得对数据存取和更新的控制成为可能。三层模式的另一个好处是它能够简化应用软件的部署。例如，数据可以在数据库中被修改、在不影响用户体验的情况下增添新组件。最后，在许多情况下，三层架构具有性能优势，因为多重事务的效率会更高。 <br/><br/>　　在编写利用JDBC API存取数据库的软件时，我们需要使用下面的组件： <br/><br/>　　·java.sql package中的类和界面 <br/>　　·Java DB数据库等数据库管理系统<br/>　　·使用的数据库管理系统的驱动程序 <br/><br/>　　执行计算和管理数据<br/><br/>　　我们可能需要编写大量的代码，由于必须输入所有的Java代码，我们应当熟练使用java.lang package。 <br/><br/>　　我们应当理解java.lang package中的字符串，字符串在Java编程中被广泛使用。与StringBuilder类一样，String类用于创建和处理字符串。 <br/><br/>　　我们还应当熟练使用java.lang package中的Number类、及其子类，它使我们能够使用这些类的实例，而不是简单的数字类型。PrintStream和DecimalFormat类提供了"写"格式化的数字输出的方法。最后，Math类提供的数学函数补充了Java语言中的数学运算符，它提供了面向三角函数、指数函数等函数的方法。 <br/><br/>　　为了处理各种类型的数据，我们需要学习collections framework━━表示和处理collections的统一架构。Collection有时也被称作container，是将多个元素组合进一个单元的对象。Collections被用来存储、访问、处理、传输聚合数据。通常情况下，collection表示组成一个合乎自然规律的群体的数据项，例如一迭卡片、一个邮件文件夹、一个字母集、电话号码簿。 <br/><br/>　　图1显示的是collection framework，以及java.util package中常用的界面 <br/><p align="center"><a href="http://vir.jxstnu.edu.cn/xieyunc/attachment/1179634649_0.jpg" target="_blank"><img src="http://vir.jxstnu.edu.cn/xieyunc/attachment/1179634649_0.jpg" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>图1： Collection Framework 和java.util Package中的一些界面</p> <br/>　　核心的Collection界面是collections framework的基础。Set是一种特殊类型的 Collection，SortedSet是一种特殊类型的Set，等等。 需要注意的是，一个层包含有二棵截然不同的树。尽管使用方式与collection 相似，Map并非真正的Collection。Map界面提供有3个collection视图，使Map的内容能够被看作一系列的键、值的集合、键-值映射集合。 <br/><br/>　　并发<br/><br/>　　应用软件通常一次完成多个任务。例如，流式音频应用软件必须同时从网络上读取数字音频内容、解密、播放，并更新对用户显示的内容； 无论排版或更新显示内容的任务多么繁忙，字处理软件应当随时响应键盘和鼠标事件。这种能够同时完成数种任务的软件就是并发软件。 <br/><br/>　　Java.util.concurrent package提供基本的并发支持。 <br/><br/><br/>　　错误处理<br/><br/>　　Java语言使用异常处理错误和其它意外事件。异常是指在软件运行期间、破坏了软件指令正常流程的事件。当一个方法中出现错误时，方法会创建一个对象，将它提交给运行时间库系统。该对象被称作异常对象，包含有相关错误的信息，其中包括类型、错误发生时软件的状态。 创建一个异常对象，并将它提交给运行时间库系统就被称作是"抛出异常"。 <br/><br/>　　要在应用软件中处理异常，我们需要理解如何编写代码，使应用软件能够抛出或捕捉一个异常。<br/><br/>　　应用软件的部署<br/><br/>　　当希望应用软件在浏览器中运行，或应用软件与web网页内容紧密集成时，使用Java插件技术部署applet。如果希望应用软件在桌面上运行，就使用Java Web Start技术。 <br/><br/>　　Java Web Start技术要求应用软件被封装为Java Archive（JAR）文件。JAR文件格式使我们能够将多个文件整合到一个存档文件中。通常，一个JAR文件包含类文件、与applet和应用软件相关的辅助性资源文件。 <br/><br/>　　JAR文件采用了ZIP文件格式，因此我们能够用它完成无损数据压缩、存档、解压缩、文档解包等任务。这些都是JAR文件最常见的用途，通过只使用这些最基本的功能，我们就能够发现JAR文件的许多优点。 <br/><br/>　　如果想利用JAR文件格式提供的电子签名等先进功能，我们需要首先熟悉基本操作。要利用JAR文件完成基本任务，我们需要使用Java Development Kit（JDK）提供的JAR工具。<br/><br/>　　一旦应用软件被封装为JAR文件，我们就可以使用Java Web Start技术部署应用软件了。Java Web Start提供了点击一下鼠标启动全功能应用软件的能力。用户可以下载和启动应用软件━━例如一个完整的电子表格软件或互联网聊天客户端，而无需经过复杂的安装过程。 <br/><br/>　　使用Java Web Start软件，通过点击web网页中的一个链接，用户就能够启动基于Java技术的应用软件。该链接指向一个Java Network Launching Protocol（JNLP）文件，它指示Java Web Start软件下载、缓冲、运行应用软件。 <br/><br/>　　其它Java技术<br/><br/>　　这一部分将讨论我们应当考虑的其它Java技术。 <br/><br/>　　应用软件安全<br/><br/>　　大多数开发人员非常担心应用软件的安全性，Java技术有助于从多个方面解决这种担心。Java技术包含有大量的API、工具，以及常用安全算法、机制、协议的实现。Java平台安全性涉及许多领域，其中包括密码、公钥架构、安全通讯、认证、访问控制。Java安全技术为我们提供了完整的应用软件安全框架，也为用户或系统管理员提供了一系列安全地管理应用软件所需要的工具。 <br/><br/>　　安全Package和类过多，我们在这里无法一一列出，但一些优秀的资源使我们能够开始学习与Java应用软件的安全有关的知识。<br/><br/>　　全屏独占模式API<br/><br/>　　如果在应用软件中需要高性能图形━━例如游戏、幻灯演示等软件，我们就需要掌握全屏独占模式API。全屏独占模式是一个功能强大的新功能，它它我们能够"暂停"窗口系统，使应用软件能够直接向显示屏上写内容。 <br/><br/>　　国际化<br/><br/>　　国际化指的是一个设计应用软件的过程，在无需修改设计的情况下就能够使应用软件适用于多种语言和地区。 "internationalization"这个词有时也被缩写成"i18n"，因为这个单词的第一和最后一个字母之间有16个字母。为实现国际化，我们使用得最多的package是java.util。 <br/><br/>　　总结<br/><br/>　　数量众多的Java技术会使我们感到困惑，但这篇文章使我们能够了解开发桌面应用软件所需要的技术。如果想让我们的应用软件具有一定的功能，我们就会找到很好的Java技术帮助实现我们需要的功能。<br/><br/> <br/>
]]>
</description>
</item>
</channel>
</rss>