<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>程序员实验室 &#187; Java</title>
	<atom:link href="http://www.prglab.com/blog/p/category/java/feed" rel="self" type="application/rss+xml" />
	<link>http://www.prglab.com/blog</link>
	<description>多读书，读好书</description>
	<lastBuildDate>Fri, 25 Nov 2011 04:20:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.4</generator>
		<item>
		<title>[JAVA]怎样验证XML文件是否符合schema定义</title>
		<link>http://www.prglab.com/blog/p/78</link>
		<comments>http://www.prglab.com/blog/p/78#comments</comments>
		<pubDate>Mon, 05 May 2008 18:21:34 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.prglab.com/blog/p/78</guid>
		<description><![CDATA[两种方法： 1。 使用SAX解析： /*** * @param xmlPath - XML文件路径 * @param schemaPath - Schema文件路径 */ public void saxValidation(String xmlPath, String schemaPath){ try { SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); saxParserFactory.setNamespaceAware(true); saxParserFactory.setValidating(true); SAXParser saxParser = saxParserFactory.newSAXParser(); saxParser.setProperty(&#34;http://java.sun.com/xml/jaxp/properties/schemaLanguage&#34;, &#34;http://www.w3.org/2001/XMLSchema&#34;); saxParser.setProperty(&#34;http://java.sun.com/xml/jaxp/properties/schemaSource&#34;, schemaPath); DefaultHandler handler = new DefaultHandler(); saxParser.parse(xmlPath, handler); } catch(SAXException exc) { exc.printStackTrace(); } } 2。 使用DOM解析： /*** * [...]]]></description>
			<content:encoded><![CDATA[<p>两种方法：</p>
<p>1。 使用SAX解析：</p>
<pre class="brush: java">
/***
* @param xmlPath - XML文件路径
* @param schemaPath - Schema文件路径
*/
public void saxValidation(String xmlPath, String schemaPath){
try {
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setValidating(true);
SAXParser saxParser = saxParserFactory.newSAXParser();
saxParser.setProperty(&quot;http://java.sun.com/xml/jaxp/properties/schemaLanguage&quot;, &quot;http://www.w3.org/2001/XMLSchema&quot;);
saxParser.setProperty(&quot;http://java.sun.com/xml/jaxp/properties/schemaSource&quot;, schemaPath);
DefaultHandler handler = new DefaultHandler();
saxParser.parse(xmlPath, handler);
} catch(SAXException exc) {
exc.printStackTrace();
}
}
</pre>
<p>2。 使用DOM解析：</p>
<pre class="brush: java">
/***
* @param xmlPath - XML文件路径
* @param schemaPath - Schema文件路径
*/
public void domValidation(String xmlPath, String schemaPath){
try {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
docBuilderFactory.setValidating(true);
docBuilderFactory.setAttribute(&quot;http://java.sun.com/xml/jaxp/properties/schemaLanguage&quot;, &quot;http://www.w3.org/2001/XMLSchema&quot;);
docBuilderFactory.setAttribute(&quot;http://java.sun.com/xml/jaxp/properties/schemaSource&quot;, schemaPath);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(xmlPath);
} catch(DOMException exc) {
exc.printStackTrace();
}
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/78/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用JAVA将彩色图片变为1bit黑白图片</title>
		<link>http://www.prglab.com/blog/p/73</link>
		<comments>http://www.prglab.com/blog/p/73#comments</comments>
		<pubDate>Thu, 01 May 2008 21:05:31 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.prglab.com/blog/p/73</guid>
		<description><![CDATA[最近做的project需要涉及到对扫描图片的文字识别，需要将扫描得到的彩色图片变为1bit的黑白图片。 搜索到很多解决方案，基本的归纳为两个步骤，首先要将彩色图片变为8bit灰度图片(grayscale image)，然后再进一步变为1bit的单色图片(1bit monochrome)。 方案一：使用ColorConvert和Dithering操作 1、代码 目前JAVA最新的图片处理包应该是JAI(Java Advanced Imaging)，在JAI API的FAQ页面上，找到了下面的方法来完成这两步工作： 将彩色图片转换为8bit灰度图片(使用ColorConvert颜色转换操作) public RenderedImage convertTo8BitGray(RenderedImage colorImage){ ParameterBlock pb = new ParameterBlock(); pb.addSource(colorImage); ColorModel cm = new ComponentColorModel( ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[]{8}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); pb.add(cm); RenderedImage grayImage = JAI.create(&#34;ColorConvert&#34;, pb); return grayImage; } 将8bit灰度图片转换为1bit黑白图片 (使用errordiffusion或ordereddither操作) public RenderedImage applyDithering(RenderedImage grayImage, boolean isErrorDiffusion){ // Load the ParameterBlock for [...]]]></description>
			<content:encoded><![CDATA[<p>最近做的project需要涉及到对扫描图片的文字识别，需要将扫描得到的彩色图片变为1bit的黑白图片。</p>
<p>搜索到很多解决方案，基本的归纳为两个步骤，首先要将彩色图片变为8bit灰度图片(grayscale image)，然后再进一步变为1bit的单色图片(1bit monochrome)。</p>
<h3>方案一：使用ColorConvert和Dithering操作</h3>
<h4>1、代码</h4>
<p>目前JAVA最新的图片处理包应该是JAI(Java Advanced Imaging)，在<a href="http://java.sun.com/products/java-media/jai/forDevelopers/jaifaq.html#1bit" target="_blank">JAI API的FAQ页面</a>上，找到了下面的方法来完成这两步工作：</p>
<table style="border-color: #aaaaaa; border-width: 1px; height: 237px;" border="0" width="566">
<tbody>
<tr>
<td style="border: 1px solid #cccccc; background-color: #eeeeee">将彩色图片转换为8bit灰度图片(使用ColorConvert颜色转换操作)</td>
</tr>
<tr>
<td style="border: 1px solid #cccccc">
<pre class="brush: java">
public RenderedImage convertTo8BitGray(RenderedImage colorImage){
ParameterBlock pb = new ParameterBlock();
pb.addSource(colorImage);
ColorModel cm = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_GRAY),
new int[]{8},
false,
false,
Transparency.OPAQUE,
DataBuffer.TYPE_BYTE);
pb.add(cm);
RenderedImage grayImage = JAI.create(&quot;ColorConvert&quot;, pb);
return grayImage;
}</pre>
</td>
</tr>
</tbody>
</table>
<table style="border-color: #aaaaaa; border-width: 1px; height: 40px;" border="0" width="566">
<tbody>
<tr>
<td style="border: 1px solid #cccccc; background-color: #eeeeee">将8bit灰度图片转换为1bit黑白图片 (使用errordiffusion或ordereddither操作)</td>
</tr>
<tr>
<td style="border: 1px solid #cccccc">
<pre class="brush: java">
public  RenderedImage applyDithering(RenderedImage grayImage, boolean isErrorDiffusion){
// Load the ParameterBlock for the dithering operation
// and set the operation name.
ParameterBlock pb = new ParameterBlock();
pb.addSource(grayImage);
String opName = null;
if(isErrorDiffusion) {
opName = &quot;errordiffusion&quot;;
LookupTableJAI lut = new LookupTableJAI(new byte[] {(byte)0x00, (byte)0xff});
pb.add(lut);
pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG);
} else {
opName = &quot;ordereddither&quot;;
ColorCube cube = ColorCube.createColorCube(DataBuffer.TYPE_BYTE,
0, new int[] {2}); //尝试改变2为其它值，可以得到不同效果
pb.add(cube);
pb.add(KernelJAI.DITHER_MASK_441);
}
// Create a layout containing an IndexColorModel which maps
// zero to zero and unity to 255.
ImageLayout layout = new ImageLayout();
byte[] map = new byte[] {(byte)0x00, (byte)0xff};
ColorModel cm = new IndexColorModel(1, 2, map, map, map);
layout.setColorModel(cm);
// Create a hint containing the layout.
RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
// Dither the image.
bwImage = JAI.create(opName, pb, hints);
return bwImage;
}
</pre>
</td>
</tr>
</tbody>
</table>
<h4>2、问题</h4>
<p>这段sample code在很多地方都被提到过，我不知道别人是否能够让它顺利执行，但是在我的机器上是不行的。</p>
<p>问题主要出在第一个convertTo8BitGray()函数里的ColorConvert操作上。在执行完ColorConvert之后，图片的ColorModel的numberOfComponents变为1。我的理解是因为变为灰度图片，只有一个色了。</p>
<p>然而不知为什么，图片的SampleModel的numberOfBands却仍然保持了3，这就造成了SampleModel和ColorModel 不兼容，所以程序总是抛出<span style="color: #ff0000;">java.lang.IllegalArgumentException: The specified ColorModel is incompatible with the image SampleModel&#8230;</span>的错误。</p>
<p><span id="more-73"></span></p>
<h3>方案二：使用重画到灰度图片上的方法</h3>
<p>后来我在Code Beach看到了<a href="http://blog.codebeach.com/2008/03/convert-color-image-to-gray-scale-image.html" target="_blank">这篇文章</a>，试用了里面的第二个方法 &#8212; 将彩色图片画到一个灰度图片上的方法，终于可以成功将彩色图片转化为8bit的灰度图片。代码如下：</p>
<pre class="brush: java">
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
Graphics g = image.getGraphics();
g.drawImage(colorImage, 0, 0, null);
g.dispose();
</pre>
<p>转换的效果如下图所示：</p>
<table style="height: 22px;" border="0" width="24">
<tbody>
<tr>
<td>转换前：彩色图片<br />
<a title="rose.jpg" rel="attachment wp-att-74" href="http://www.prglab.com/blog/p/73/rosejpg"><br />
<img src="http://www.prglab.com/blog/wp-content/uploads/rose.jpg" alt="rose.jpg" width="250" /></a></td>
<td>转换后：灰度图片<br />
<a title="rose_gray.jpg" rel="attachment wp-att-77" href="http://www.prglab.com/blog/p/73/rose_grayjpg"><br />
<img src="http://www.prglab.com/blog/wp-content/uploads/rose_gray.jpg" alt="rose_gray.jpg" width="250" /></a></td>
</tr>
</tbody>
</table>
<p><!--more--></p>
<h3>使用Binarize将8bit灰度图片转换为1bit黑白图片</h3>
<p>将彩色图片转换为灰度图片后，仍然可以用前面程序例子中的applyDithering()方法来将8bit灰度图片进一步转换为1bit黑白图片，得到的效果比较象报纸上的黑白图片，保留了很多灰度的层次。<br />
为了能够更进一步得到版画效果的图片，可以使用Binarize操作。改变Binarize操作的域值(threshold)也可以得到不同效果。<br />
<strong>代码：</strong></p>
<pre class="brush: java">
/***
* Binarize image (convert image to 1 bit black and white)
* 输入图片必须为灰度图片，否则会出错。
*/
public RenderedImage applyBinarize(RenderedImage grayImage) {
// Generate a histogram.
Histogram histogram =
(Histogram)JAI.create(&quot;histogram&quot;, grayImage).getProperty(&quot;histogram&quot;);
// Get a threshold equal to the median.
double[] threshold = histogram.getPTileThreshold(0.4); //改变域值可以得到不同效果
// Binarize the image.
RenderedImage bwImage =
JAI.create(&quot;binarize&quot;, grayImage, new Double(threshold[0]));
return bwImage;
}//function applyBinarize
</pre>
<p><strong>效果：</strong></p>
<table border="0">
<tbody>
<tr>
<td>使用Dithering的效果</td>
<td>使用Binarizing的效果</td>
</tr>
<tr>
<td><a title="rose_dithering.jpg" rel="attachment wp-att-76" href="http://www.prglab.com/blog/p/73/rose_ditheringjpg"><img src="http://www.prglab.com/blog/wp-content/uploads/rose_dithering.jpg" alt="rose_dithering.jpg" width="250" /></a></td>
<td><a title="rose_bw.jpg" rel="attachment wp-att-75" href="http://www.prglab.com/blog/p/73/rose_bwjpg"><img src="http://www.prglab.com/blog/wp-content/uploads/rose_bw.jpg" alt="rose_bw.jpg" width="250" /></a></td>
</tr>
</tbody>
</table>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/73/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从Java到C++ &#8212; 对比Java与C++编程的不同</title>
		<link>http://www.prglab.com/blog/p/48</link>
		<comments>http://www.prglab.com/blog/p/48#comments</comments>
		<pubDate>Wed, 15 Aug 2007 03:40:15 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://prglab.com/blog/p/48</guid>
		<description><![CDATA[原作：Cay Horstmann 英文原文 翻译：Aqua  prglab.com 注：很多程序员包括本人在内都是先学会的Java，然后才学的C++，其实C++与Java有很多相似和互通之处，有相当的Java知识可以对应转化到C++概念，从而帮助我们快速上手。这篇文章介绍的内容就对从Java向C++转变很有帮助，所以翻译推荐给有同样需要的朋友。翻译中加入了本人的理解，不完全是全文照搬。有不明或异议，请参考原文，或留言讨论。还是那句话，推荐多读原版资料。 学习怎样从一种编程语言转移到另一种编程语言是今日的程序员必须面对的现实，还好，C++和Java有很多共同的特点，所以从Java转到C++就容易许多。C++比Java复杂很多，本文并没打算涵盖所有C++的功能。但是如果你能够熟练掌握本文中的所有知识点，也足够有效的使用C++了。 这里我们只讲Java与C++的不同之处。像流程控制(if, while, for)这些在C++与Java中完全一样的内容这里就不讲了。 本文是基于ANSI C++标准的，一些老的C++ 编译器可能不支持这里讲到的一些重要功能。要使用这些编译器，你需要更多学习C++中从C继承来的部分。那些都超出了本文的范畴，也就不介绍了。 1. 数据类型和变量 C++ 中的变量类型与Java很相似。像Java一样，C++ 有int 和 double 类型。但是这些数字类型的取值范围是依赖于机器的。比如在16位系统上，例如运行DOS 或Windows 3.x的PC机上，int 是双字节(2-byte)的，取值范围比Java的4-byte的int 要小很多。在这些机器上，如果 int 不够用的话，你需要使用长整型long。 C++ 有 short 和 unsigned 类型来更有效的存储数字。（我认为所谓有效是指更高的空间利用率。） 最好是尽量避免使用这些类型除非是空间利用的有效性对你的系统真的非常重要。 在C++中布尔型用 bool 表示，而不像在Java中用boolean。 C++ 中字符串类型用 string 表示。它与Java中的 String 类型非常相似，但是，还是要逐一以下几点不同之处： 1. C++ 字符串存储ASCII 码字符，而不是标准码Unicode 字符 2. C++ 字符串是可以被修改的，而Java字符串的内容是不可修改的(immutable)。 3. 取子字符串的操作在 C++ 中叫做 [...]]]></description>
			<content:encoded><![CDATA[<p>原作：<a href="http://www.horstmann.com/" target="_blank">Cay Horstmann</a>      <a href="http://www.horstmann.com/ccj2/ccjapp3.html" target="_blank">英文原文</a></p>
<p>翻译：Aqua  <a href="http://prglab.com/blog/p/48" title="全文链接">prglab.com</a></p>
<blockquote><p>注：很多程序员包括本人在内都是先学会的Java，然后才学的C++，其实C++与Java有很多相似和互通之处，有相当的Java知识可以对应转化到C++概念，从而帮助我们快速上手。这篇文章介绍的内容就对从Java向C++转变很有帮助，所以翻译推荐给有同样需要的朋友。翻译中加入了本人的理解，不完全是全文照搬。有不明或异议，请参考原文，或留言讨论。还是那句话，推荐多读原版资料。</p></blockquote>
<p>学习怎样从一种编程语言转移到另一种编程语言是今日的程序员必须面对的现实，还好，C++和Java有很多共同的特点，所以从Java转到C++就容易许多。C++比Java复杂很多，本文并没打算涵盖所有C++的功能。但是如果你能够熟练掌握本文中的所有知识点，也足够有效的使用C++了。<span id="more-48"></span></p>
<p>这里我们只讲Java与C++的不同之处。像流程控制(if, while, for)这些在C++与Java中完全一样的内容这里就不讲了。</p>
<p>本文是基于ANSI C++标准的，一些老的C++ 编译器可能不支持这里讲到的一些重要功能。要使用这些编译器，你需要更多学习C++中从C继承来的部分。那些都超出了本文的范畴，也就不介绍了。</p>
<h2>1. 数据类型和变量</h2>
<p>C++ 中的变量类型与Java很相似。像Java一样，C++ 有<tt>int</tt> 和 <tt>double</tt> 类型。但是这些数字类型的取值范围是依赖于机器的。比如在16位系统上，例如运行DOS 或Windows 3.x的PC机上，<tt>int</tt> 是双字节(2-byte)的，取值范围比Java的4-byte的<tt>int</tt> 要小很多。在这些机器上，如果 int 不够用的话，你需要使用长整型long。</p>
<p>C++ 有 <tt>short</tt> 和 <tt>unsigned</tt> 类型来更有效的存储数字。（我认为所谓有效是指更高的空间利用率。） 最好是尽量避免使用这些类型除非是空间利用的有效性对你的系统真的非常重要。</p>
<p>在C++中布尔型用 <tt>bool</tt> 表示，而不像在Java中用boolean。</p>
<p>C++ 中字符串类型用 <tt>string</tt> 表示。它与Java中的 <tt>String</tt> 类型非常相似，但是，还是要逐一以下几点不同之处：</p>
<p>1. C++ 字符串存储ASCII 码字符，而不是标准码Unicode 字符</p>
<p>2. C++ 字符串是可以被修改的，而Java字符串的内容是不可修改的(immutable)。</p>
<p>3. 取子字符串的操作在 C++ 中叫做 <tt>substr</tt>，这个命令<tt>s.substr(i, n)</tt> 从字符串s中取得从位置 i 开始长度为n的子字符串。</p>
<p>4. 在C++中，你只能够将字符串与其它字符串对象相串联(concatenate)，而不能够与任意的对象相串联。</p>
<p>5. C++中可以直接使用关系操作符 <tt>==、 !=、 &lt;、 &lt;=、       &gt;、 &gt;= </tt>来进行字符串比较，其中后面四个操作符是按字母顺序进行比较的。 这比Java中使用函数equals和compareTo来比较要方便很多。</p>
<h2>2. 变量和常量</h2>
<p>在C++中，本地变量的定义看起来与Java中相同，例如：</p>
<p><tt>int n = 5;</tt></p>
<p>实际上这正是C++和Java的一个重要不同之处。C++编译器不对本地变量进行初始化检验，所以在C++中很容易忘记初始化一个变量，这种情况下，变量的值该变量所占内存区域中刚好当前存在随机值。这显然是很容易产生程序出错的地方。</p>
<p>与Java一样， C++中类可以有数据域和静态变量。不同的是，C++中变量可以在函数甚至是类的外面定义，这些所谓的全局变量可以在程序的任何函数中被访问，因而不易被很好的管理。所C++中应该尽量避免使用全局变量。</p>
<p>在C++中，常量可以在任何地方被定义（记得在Java中，常量必须是类的静态数据static data)。 C++ 使用关键字 <tt>const</tt> 来定义常量，而Java中是 <tt>final</tt>。例如：</p>
<p><tt>const int DAYS_PER_YEAR = 365;</tt></p>
<h2>3. 类</h2>
<p>C++ 中对类的定义与Java有些不同，这里是一个例子：一个C++ 版本的 <tt>Point</tt> 类:</p>
<p><tt>class Point /* C++ */</tt></p>
<p><tt>{</tt></p>
<p><tt>    public:</tt></p>
<p><tt>        Point();</tt></p>
<p><tt>        Point(double xval, double yval);</tt></p>
<p><tt>        void move(double dx, double dy);</tt></p>
<p><tt>        double getX() const;</tt></p>
<p><tt>          double getY() const;</tt></p>
<p><tt>    private:</tt></p>
<p><tt>        double x;</tt></p>
<p><tt>        double y;</tt></p>
<p><tt>};</tt></p>
<p>这里几点重要的不同是：</p>
<p>1. C++的类定义中分为公共和私有部分，分别以关键字 <tt>public</tt> 和 <tt>private</tt>开始。而在Java中，每一个元素都必须标明 <tt>public</tt> 或 <tt>private</tt>。</p>
<p>2. C++中类的定义只包含函数的声明，真正的实现另外单独列出。</p>
<p>3. 访问函数(accessor methods)标有关键字 <tt>const</tt> ，表明这个函数不会改变本对象的元素值。</p>
<p>4. 类定义的结尾处有分号</p>
<p>类中函数的实现跟在类的定义之后。因为函数是在类外面定义的，所以每一个函数的名字前面要加类名称作为前缀，并使用操作符双冒号::来分割类的名称和函数的名称。不改变隐含参数值（即当前对象的值）的访问函数用       <tt>const</tt>标明。如下所示是上面类定义中的函数的实现：</p>
<p><tt>Point::Point() { x = 0; y = 0; }</tt></p>
<p><tt>void Point::move(double dx, double dy) </tt></p>
<p><tt>{</tt></p>
<p><tt>  x = x + dx;</tt></p>
<p><tt>  y = y + dy;</tt></p>
<p><tt>}</tt></p>
<p><tt>double Point::getX() const</tt></p>
<p><tt>{  </tt></p>
<p><tt>    return x;</tt></p>
<p><tt>}</tt></p>
<h2>4. 对象</h2>
<p>Java 与 C++ 最主要的不同在于对象变量的使用。在 C++中，对象变量存储的是真正的对象的值，而不是对象引用(<a href="http://www.prglab.com/blog/p/28" title="关于reference的文章">reference</a>)。注意在C++中构造一个对象的时候是不使用关键字new的，只需要在变量的名字后面直接赋予构造函数的参数就可以了，例如：</p>
<p><tt>Point p(1, 2); /* 构造对象 p */</tt></p>
<p>如果不跟参数赋值，则使用默认构造函数，例如：</p>
<p><tt>Time now; /* 默认使用构造函数 Time::Time() */</tt></p>
<p>这一点与Java很不同。在Java中，这个命令仅仅生成一个没有初始化的reference，而在C++中，它生成一个实际的对象。</p>
<p>当一个对象被赋给另一个对象变量的时候，实际的值将被拷贝。而在Java中，拷贝一个对象变量只不过是建立了另外一个指向对象的reference。拷贝一个C++的对象就像在Java中调用clone这个函数一样，而修改拷贝的值不会改变原对象的值。例如：</p>
<p><tt>Point q = p; /* 拷贝p到q */</tt></p>
<p><tt>q.move(1, 1); /* 移动q而p不动，即q的值变了，而p的不变*/</tt></p>
<p>多数情况下，C++中这种对象直接对值操作的特性使用起来很方便，但是也有些时候不尽如人意：</p>
<p>1. 当需要一个函数中修改一个对象的值，必须记住要使用按引用调用call       by reference (参见下面函数部分)</p>
<p>2. 两个对象变量不能指向同一个对象实体。如果你要在C++中实现这种效果，必须使用指针pointer（参见下面指针部分）</p>
<p>3. 一个对象变量只能存储一种特定的类型的值，如果你想要使用一个变量来存储不同子类的对象的值（多态ploymorphism)，则需要使用指针。</p>
<p>4. 如果你想在C++中使用一个变量来或者指向null或者指向一个实际的对象，则需要使用指针</p>
<h2>5. 函数</h2>
<p>在Java中，每一个函数必须或者是对象函数(instance method)，或者是静态函数(static function)或称类函数。C++同样支持对象函数和静态函数（类函数），但同时C++也允许定义不属于任何类的函数，这些函数叫做全局函数<em>（global functions）</em>。</p>
<p>特别的是，每一个C++ 程序都从一个叫做 <tt>main</tt>的全局函数开始执行：</p>
<p><tt>int main()</tt></p>
<p><tt>{ . . .</tt></p>
<p><tt>}</tt></p>
<p>还有另外一个格式的main函数可以用来捕捉命令行参数，类似于Java的main函数，但是它要求关于C格式的数组和字符串的知识，这里就不介绍了。</p>
<p>按照习惯，通常如果程序执行成功， <tt>main</tt> 函数返回0，否则返回非零整数。</p>
<p>同Java一样，函数参数是通过值传递的(passed by value)。在Java中，函数无论如何都是可以修改对象的值的。然而在C++中，因为对象直接存储的是实际的值，而不是指向值的reference，也就是说传入函数的是一个实际值的拷贝，因此也就无法修改原来对象的值。</p>
<p>所以，C++ 有两种参数传递机制，同Java一样的按值调用(<em>call by value) </em>，以及按地址调用(<em>call by reference</em>)。当一个参数是按reference传递时，函数可以修改其原始值。Call by reference 的参数前面有一个地址号 <tt>&amp;</tt> 跟在参数类型的后面，例如：</p>
<p><tt>void raiseSalary(Employee&amp; e, double by)</tt></p>
<p><tt>{ . . .</tt></p>
<p><tt>}</tt></p>
<p>下面是一个典型的利用call by reference的函数，在Java中是无法实现这样的功能的。</p>
<p><tt>void swap(int&amp; a, int&amp; b)</tt></p>
<p><tt>{  int temp = a;</tt></p>
<p><tt>  a = b;</tt></p>
<p><tt>  b = temp;</tt></p>
<p><tt>}</tt></p>
<p>如果使用 <tt>swap(x, y)</tt>来调用这个函数，则reference参数 <tt>a</tt> 和 <tt>b</tt> 指向原实际参数<tt>x</tt> 和 <tt>y</tt>的位置，而不是它们的值的拷贝，因此这个函数可以实现实际交换这两个参数的值。</p>
<p>在 C++中，每当需要实现修改原参数的值时你就可以使用按地址调用 call by reference 。</p>
<h2>6. 向量Vector</h2>
<p>C++ 的向量结构结合了Java中数组和向量两者的优点。一个C++ 的向量可以方便的被访问，其容量又可以动态的增长。如果 <tt>T</tt> 是任意类型，则 <tt>vector&lt;T&gt;</tt>       是一个元素为 <tt>T</tt> 类型的动态数组。下面的语句</p>
<p><tt>vector&lt;int&gt; a;</tt></p>
<p>产生一个初始为空的向量。而语句</p>
<p><tt>vector&lt;int&gt; a(100);</tt></p>
<p>生成一个初始有100个元素的向量。你可以使用<tt>push_back</tt> 函数来添加元素：</p>
<p><tt>a.push_back(n);</tt></p>
<p>调用 <tt>a.pop_back()</tt> 从<tt>a</tt>中取出最后一个元素（操作后这个元素被从a中删掉)，       使用函数<tt>size</tt> 可以得到当前a中的元素个数。</p>
<p>你还可以通过我们熟悉的 <tt>[]</tt> 操作符来访问向量中元素，例如：</p>
<p><tt>for (i = 0; i &lt; a.size(); i++) {<br />
</tt></p>
<p><tt>  sum = sum + a[i];</tt></p>
<p><tt>}</tt></p>
<p>同Java中一样，数组索引必须为 0 和 <tt>a.size() - 1</tt>之间的值。但是与Java不同的是，C++中没有runtime的索引号合法性检验。试图访问非法的索引位置可能造成非常严重的出错。</p>
<p>就像所有其它 C++ 对象一样，向量也是值。如果你将一个向量赋值给另外一个向量变量，所有的元素都会被拷贝过去。</p>
<p><tt>vector&lt;int&gt; b = a; /* 所有的元素都被拷贝了 */</tt></p>
<p>对比Java中的情况，在Java中，一个数组变量是一个指向数组的reference。拷贝这个变量仅仅产生另外一个指向同一数组的reference，而不会拷贝每一个元素的值。</p>
<p>正因如此，如果一个C++函数要实现修改向量的值，必须使用reference参数：</p>
<p><tt>void sort(vector&lt;int&gt;&amp; a)</tt></p>
<p><tt>{ . . .</tt></p>
<p><tt>}</tt></p>
<h2>7. 输入和输出</h2>
<p>在C++中，标准的输入输出流用对象       <tt>cin</tt> 和 <tt>cout</tt> 表示。我们使用 <tt>&lt;&lt;</tt>       操作符写输出，例如：</p>
<p><tt>cout &lt;&lt; "Hello, World!";</tt></p>
<p>也可以连着输出多项内容，例如：</p>
<p><tt>cout &lt;&lt; "The answer is " &lt;&lt; x &lt;&lt; "\n";</tt></p>
<p>我们使用 <tt>&gt;&gt;</tt>       操作符来读入一个数字或单词，例如：</p>
<p><tt>double x;</tt></p>
<p><tt>cout &lt;&lt; "Please enter x: ";</tt></p>
<p><tt>cin &gt;&gt; x;</tt></p>
<p><tt>string fname;</tt></p>
<p><tt>cout &lt;&lt; "Please enter your first name: ";</tt></p>
<p><tt>cin &gt;&gt; fname;</tt></p>
<p>函数<tt>getline</tt> 可以读入整行的输入，例如：</p>
<p><tt>string inputLine;</tt></p>
<p><tt>getline(cin, inputLine);</tt></p>
<p>如果到达输入的结尾，或者一个数字无法被正确的读入，这个流对象会被设置为 failed 状态，我们可以使用函数 <tt>fail</tt> 来检验这个状态，例如：</p>
<p><tt>int n;</tt></p>
<p><tt>cin &gt;&gt; n;</tt></p>
<p><tt>if (cin.fail()) cout &lt;&lt; "Bad input";</tt></p>
<p>一旦一个流的状态被设为failed，我们是很难重置它的状态的，所以如果你的程序需要处理错误输入的情况，应该使用函数 <tt>getline</tt> 然后人工处理得到的输入数据。</p>
<h2>8. 指针pointer</h2>
<p>我们已经知道在C++中，对象变量直接存储的是对象的值。这是与Java不同的，在Java中对象变量存储的是一个地址，该地址指向对象值实际存储的地方。有时在C++中也需要实现这样的布置，这就用到了指针pointer。在 C++中，一个指向对象的变量叫做指针。如果T是一种数据类型，则 <tt>T*</tt> 是指向这种数据类型的指针。 这里重点介绍C++与Java的不同，要详细了解C++中指针的使用，请参考本站另一篇文章“<strong><a href="http://www.prglab.com/blog/p/28" rel="bookmark" title="Permanent Link to C++中Reference与指针（Pointer）的使用对比">C++中Reference与指针（Pointer）的使用对比</a></strong>”。</p>
<p>就像 Java中一样，一个指针变量可以被初始化为空值 <tt>NULL</tt>，另外一个指针变量的值，或者一个调用new生成的新对象：</p>
<p><tt>Employee* p = NULL;</tt></p>
<p><tt>Employee* q = new Employee("Hacker, Harry", 35000);</tt></p>
<p><tt>Employee* r = q;</tt></p>
<p>实际上在C++中还有第四种可能，那就是指针可以被初始化为另外一个对象的地址，这需要使用地址操作符 <tt>&amp;</tt> ：</p>
<p><tt>Employee boss("Morris, Melinda", 83000);</tt></p>
<p><tt>Employee* s = &amp;boss;</tt></p>
<p>这实际上并不是什么好主意。保险的做法还是应该直接让指针指向使用 <tt>new</tt>生成的新对象。</p>
<p>到目前为止，C++ 指针看起来非常像 Java 的对象变量。然而，这里有一个很重要的语法的不同。我们必须使用星号操作符 <tt>*</tt> 来访问指针指向的对象。如果 <tt>p</tt> 是一个指向<tt>Employee</tt>对象的指针，则 <tt>*p</tt> 才代表了这个对象：</p>
<p><tt>Employee* p = . . .;</tt></p>
<p><tt>Employee boss = <strong>*p</strong>;</tt></p>
<p>当我们需要执行对象的函数或访问对象的一个数据域时，也需要使用 <tt>*p</tt> ：</p>
<p><tt>(*p).setSalary(91000);</tt></p>
<p>*p外面的括号是必需的，因为 <tt>.</tt> 操作符比 * 操作符有更高的优先级。C的设计者觉得这种写法很难看，所以他们提供了另外一种替代的写法，使用 -&gt; 操作符来实现 <tt>*</tt> 和 <tt>.</tt> 操作符的组合功能。表达式</p>
<p><tt>p-&gt;setSalary(91000);</tt></p>
<p>可以调用对象*p的函数 <tt>setSalary</tt> 。你可以简单的记住 <tt>.</tt> 操作符是在对象上使用的，-&gt; 操作符是在指针上使用的。</p>
<p>如果你不初始化一个指针，或者如果一个指针为空值 NULL 或指向的对象不再存在，则在它上面使用 <tt>*</tt>       或 <tt>-&gt;</tt> 操作符就会出错。 不幸的是 C++ runtime 系统并不检查这个出错。如果你范了这个错误，你的程序可能会行为古怪或死机。</p>
<p>而在Java中，这些错误是不会发生的。所有的reference都必须初始化，所有的对象只要仍有reference指向它就不会被从内存中清除，因此你也不会有一个指向已被删除的对象的reference。Java的runtime      系统会检查reference是否为空，并在遇到空指针时抛出一个null pointer的例外(exception)。</p>
<p>C++ 和 Java还有一个显著的不同，就是 Java 有<em>垃圾回收</em>功能，能够自动回收被废弃的对象。而在C++中，需要程序员自己管理内存分配回收。</p>
<p>C++中当对象变量超出范围时可以自动被回收。但是使用new生成的对象必须用delete操作符手动删除，例如：</p>
<p><tt>Employee* p = new Employee("Hacker, Harry", 38000);</tt></p>
<p><tt>. . .</tt></p>
<p><tt>delete p; /* 不在需要这个对象 */</tt></p>
<p>如果你忘记删除一个对象，那么你的程序有可能最终用光所有内存。这就是我们常说的内存泄漏 (<em>memory leak</em>)。更重要的是，如果你如果删除了一个对象，然后又继续使用它，你可能覆盖不属于你的数据。如果你刚巧覆盖了用于处理内存回收的数据域，那么内存分配机制就可能运转失常而造成更严重的错误，而且很难诊断和修复。因此，在C++中最好尽量少用指针。</p>
<h2>9. 继承</h2>
<p>C++和Java中继承的基本语法是很相似的。在C++中，使用 <tt>: public</tt> 代替Java中的<tt>extends</tt> 来表示继承关系 。 (C++ 也支持私有继承的概念，但是不太有用。)</p>
<p>默认情况下，C++中的函数不是动态绑定的。如果你需要某个函数实现动态绑定，需要使用<tt>virtual</tt>声明它为虚函数，例如：</p>
<p><tt>class Manager : public Employee</tt></p>
<p><tt>{ </tt></p>
<p><tt>public:</tt></p>
<p><tt>   Manager(string name, double salary, string dept);</tt></p>
<p><tt>   <strong>virtual</strong> void print() const;</tt></p>
<p><tt>private:</tt></p>
<p><tt>   string department;</tt></p>
<p><tt>};</tt></p>
<p>同Java一样，构造函数中调用父类的构造函数有特殊的语法。 Java使用关键字 <tt>super</tt>。C++中必须在子类的构造函数体外调用父类的构造函数。下面是一个例子：</p>
<p><tt>Manager::Manager(string name, double salary, string dept)</tt></p>
<p><tt><strong>: Employee(name, salary) </strong>/* 调用父类的构造函数 */</tt></p>
<p><tt>{  department = dept;</tt></p>
<p><tt>}</tt></p>
<p>Java 中在子类函数中调用父类的函数时也使用关键字 <tt>super</tt> 。而在C++中是使用父类的名称加上操作符       <tt>::</tt>表示，例如：</p>
<p><tt>void Manager::print() const</tt></p>
<p><tt>{  <strong>Employee::print(); </strong>/* 调用父类的函数       */</tt></p>
<p><tt>   cout &lt;&lt; department &lt;&lt; "\n";</tt></p>
<p><tt>}</tt></p>
<p>一个 C++ 对象变量只能存储特定类型的对象值。要想在C++中实现多态(polymorphism)，必须使用指针。一个 <tt>T*</tt> 指针可以指向类型为 <tt>T</tt> 或 <tt>T</tt> 的任意子类的对象，例如：</p>
<p><tt>Employee* e = new Manager("Morris, Melinda", 83000, "Finance");</tt></p>
<p>你可以将父类和不同子类的对象混合收集到一个元素均为指针的向量中，然后调用动态绑定的函数，如下所示：</p>
<p><tt>vector&lt;Employee*&gt; staff;</tt></p>
<p><tt>. . .</tt></p>
<p><tt>for (i = 0; i &lt; staff.size(); i++)</tt></p>
<p><tt>   staff[i]-&gt;print();</tt></p>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/48/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>推荐一本Java处理XML的书</title>
		<link>http://www.prglab.com/blog/p/46</link>
		<comments>http://www.prglab.com/blog/p/46#comments</comments>
		<pubDate>Wed, 01 Aug 2007 02:32:05 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://prglab.com/blog/p/46</guid>
		<description><![CDATA[XML是现在程序员必备的技能了，有次面试就被问到有没有写过处理XML的程序，用的是什么。记得以前最常用的DOM parser是Apache的Xcerces，在Java 1.4以后，Java标准中有了自己的处理XML的API，叫做JAXP。 Elliotte Rusty Harold的这本书Processing XML with Java详细介绍了如何使用Java编程语言处理XML文档，是一本实用而且综合的指南与教程。书中简要概述了 XML基础，包括XML语法、DTD、模式、有效性，样式单和XML协议XML-RPC、SOAP与RSS。本书的核心内容是深入介绍了Java编程人员用Java生成与操纵XML文档时所用的关键XML API，包括SAX、DOM（文档对象模型）和JDOM。此外，还介绍了这些核心API的许多重要补充，包括XPath，XSLT，TrAX与JAXP，是所有需要使用XML的Java编程人员的宝贵参考资料。 现在这本书的英文版全文已经在线发布在XML的资源网站Cafe con Leche。本书也有中文译本，名为《Java语言与XML处理教程:SAX,DOM,JDOM,JAXP与TrAX指南》，以前卓越可以找到，现在好像找不到了。]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.cafeconleche.org/images/xmljavamediumcover.jpg" border="0" alt="" hspace="10" vspace="10" width="120" height="150" align="left" />XML是现在程序员必备的技能了，有次面试就被问到有没有写过处理XML的程序，用的是什么。记得以前最常用的DOM parser是Apache的<a href="http://xerces.apache.org/" target="_blank">Xcerces</a>，在Java 1.4以后，Java标准中有了自己的处理XML的API，叫做<a href="http://java.sun.com/webservices/jaxp/" target="_blank">JAXP</a>。</p>
<p>Elliotte Rusty Harold的这本书Processing XML with Java详细介绍了如何使用Java编程语言处理XML文档，是一本实用而且综合的指南与教程。书中简要概述了 XML基础，包括XML语法、DTD、模式、有效性，样式单和XML协议XML-RPC、SOAP与RSS。本书的核心内容是深入介绍了Java编程人员用Java生成与操纵XML文档时所用的关键XML API，包括SAX、DOM（文档对象模型）和JDOM。此外，还介绍了这些核心API的许多重要补充，包括XPath，XSLT，TrAX与JAXP，是所有需要使用XML的Java编程人员的宝贵参考资料。</p>
<p>现在这本书的<a href="http://www.cafeconleche.org/books/xmljava/" target="_blank">英文版全文</a>已经在线发布在XML的资源网站<a href="http://www.cafeconleche.org/" target="_blank">Cafe con Leche</a>。本书也有中文译本，名为《Java语言与XML处理教程:SAX,DOM,JDOM,JAXP与TrAX指南》，以前卓越可以找到，现在好像找不到了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/46/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[节选]新一代专家系统工具——基于Java的Jess（转载）</title>
		<link>http://www.prglab.com/blog/p/29</link>
		<comments>http://www.prglab.com/blog/p/29#comments</comments>
		<pubDate>Wed, 23 May 2007 03:02:38 +0000</pubDate>
		<dc:creator>Aqua</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://prglab.com/blog/p/29</guid>
		<description><![CDATA[转自：http://www.360doc.com/showWeb/0/1/42844.aspx 近10 年来，随着计算机技术和人工智能技术的飞速发展，尤其是网络技 术的进步，专家系统也有了新的重大发展，出现了以Java为核心技术的专家系统开发工具。由美国Sandia实验室推出的专家系统外壳Jess（Java expert system shell）就是其中的一个出色代表，本文将介绍它的来龙去脉和主要特点。 从LISP到CLIPS 专家系统工具，即专家系统语言，是一种比LISP或C语言层次更高的语言，它提供一个推理机去 执行该语言的语句。早期的专家系统工具大都由LISP开发， 20世纪80年代中至90年代初是LISP的黄金时期。但随着LISP的广泛应用，其问题也逐渐暴露。一是LISP的运行速度。直到1989年，LISP 应用程序只有在用LISP编写的操作系统上才具有较好的运行效率，所以，一些计算机公司专门设计了运行人工智能语言程序的专用机器，但这使每次软件的更新 或升级都要付出巨大的代价。二是LISP的嵌入性。当要解决一个非常复杂的问题时，LISP显得极其无能为力。这两点限制了LISP的发展，它只被使用在 某些特定的应用领域。 CLIPS（C Language Integrated Production System）正是为解决这些问题而出现的。它于1984年由美国航空航天局约翰逊空间中心（NASA’s Johnson Space Center）推出，意在克服LISP移植性差、开发工具和硬件成本高、嵌入性低的缺点。CLIPS是一个基于Rete算法的前向推理语言，用标准C语言 编写，目前最新的版本为6.10。它具有高移植性、高扩展性、强大的知识表达能力和编程方式以及低成本等特点。一经推出，立即受到欢迎，被广泛应用于政 府、工业和学术界，有力地推动了专家系统技术在各领域及各种运行环境下的应用。目前，CLIPS是一个自由软件，主要由原来在NASA工作的设计人员维 护。它提供了一个新闻讨论组，可作为从事CLIPS的开发人员交换信息的场所。此外，在NASA的组织下还成立了一个CLIPS用户协会，以推动 CLIPS的开发与应用。更详细的信息可访问它的官方网站http: //www.ghg.net/clips/CLIPS.html。 由于CLIPS的强大功能与良好性能，以及NASA对该软件采取的开放政策，CLIPS 在美国国内外都有众多用户，他们应用CLIPS开发了许多实用的专家系统，并根据各自的需要对CLIPS进行扩充和改造，如加拿大研究委员会在CLIPS 中加入了模糊推理功能，推出了FuzzyCLIPS，我们也成功地在CLIPS中加入了图形功能，推出了图形化专家系统工具GEST（Graphical Expert System Shell）。 新一代工具Jess Jess是1995年由美国Sandia国家 实验室分布式系统计算组成员Ernest J. Friedman-Hill用Java实现的一个经过扩充的CLIPS版本。它以CLIPS的设计原理为基础进行编写，除继承了CLIPS的优点外，还具 有许多自己独特的特征，如支持正向和逆向推理，可以在系统运行环境下直接调用Java的类库等，这些特点将专家系统的开发过程同功能强大的Java语言结 合起来，使采用Jess语言开发的专家系统具有良好的移植性、嵌入性，可以方便地应用到网络上的不同机器中。另外，Java多线程机制使Jess可以与其 他应用程序并发执行，同步机制保证了对共享数据的正确操作，通过使用不同的线程完成特定的行为，就可以很容易地实现网络上的实时交互行为。目前，Jess 被广泛用于学术、工业、商业等领域，是一个有着广阔发展前景的专家系统开发平台。 1. Jess的基本组成和知识表示 同 大多数专家系统工具一样，Jess的核心也是由事实库、规则库、推理机三大部分组成，并采用产生式规则作为基本的知识表达模式。在Jess中，事实包括简 单事实和对象事实。简单事实就是一个描述事物的断言，而对象事实除此之外还封装了方法，可以接受外界信息改变自身的特征。这一概念本身并不是Jess首次 提出的，ART-IM、CLIPS都支持这一概念。但Jess表达对象事实的方法确实别具一格：它用Java而非系统本身的语言来定义对象。在CLIPS 6.0中，对象事实通过系统本身的语句defclass和make-instance来定义，但在Jess中，类的定义由Java语言书写，编译通过后即 可动态地加入系统中。用Java虚拟机编译通过后，通过defclass命令将该类加入系统，它就可以执行类似于CLIPS中对类的各种操作，如生成它的 一个实例、调用它的方法等。由此可见，Jess可以方便地调用Java中的类库，使用Java中的各种数据结构和方法，从而具备其他系统不可比拟的优良的 嵌入能力。 Jess通过模式匹配语言对事实进行操作。在Jess中，模式匹配操作符的类型有很多，从可以同任意事实进行匹配的单一操作符到 只能同满足特定约束值的事实进行匹配的复杂操作符。特别要指出的是，Jess中有“unique”条件元素，它告诉系统同该模式匹配的事实是惟一的。这 样，当模式发现一条事实同它匹配后，就会停止对事实库的检索，在实际应用中，这可以将系统的性能提高20％～30％，而CLIPS系统不支持这一条件元 [...]]]></description>
			<content:encoded><![CDATA[<p align="left">转自：<a href="http://www.360doc.com/showWeb/0/1/42844.aspx" target="_blank">http://www.360doc.com/showWeb/0/1/42844.aspx</a></p>
<p align="left">近10 年来，随着计算机技术和人工智能技术的飞速发展，尤其是网络技 术的进步，专家系统也有了新的重大发展，出现了以Java为核心技术的专家系统开发工具。由美国Sandia实验室推出的专家系统外壳Jess（Java expert system shell）就是其中的一个出色代表，本文将介绍它的来龙去脉和主要特点。<span id="more-29"></span></p>
<p><strong>从LISP到CLIPS</strong></p>
<p>专家系统工具，即专家系统语言，是一种比LISP或C语言层次更高的语言，它提供一个推理机去 执行该语言的语句。早期的专家系统工具大都由LISP开发， 20世纪80年代中至90年代初是LISP的黄金时期。但随着LISP的广泛应用，其问题也逐渐暴露。一是LISP的运行速度。直到1989年，LISP 应用程序只有在用LISP编写的操作系统上才具有较好的运行效率，所以，一些计算机公司专门设计了运行人工智能语言程序的专用机器，但这使每次软件的更新 或升级都要付出巨大的代价。二是LISP的嵌入性。当要解决一个非常复杂的问题时，LISP显得极其无能为力。这两点限制了LISP的发展，它只被使用在 某些特定的应用领域。</p>
<p>CLIPS（C Language Integrated Production System）正是为解决这些问题而出现的。它于1984年由美国航空航天局约翰逊空间中心（NASA’s Johnson Space Center）推出，意在克服LISP移植性差、开发工具和硬件成本高、嵌入性低的缺点。CLIPS是一个基于Rete算法的前向推理语言，用标准C语言 编写，目前最新的版本为6.10。它具有高移植性、高扩展性、强大的知识表达能力和编程方式以及低成本等特点。一经推出，立即受到欢迎，被广泛应用于政 府、工业和学术界，有力地推动了专家系统技术在各领域及各种运行环境下的应用。目前，CLIPS是一个自由软件，主要由原来在NASA工作的设计人员维 护。它提供了一个新闻讨论组，可作为从事CLIPS的开发人员交换信息的场所。此外，在NASA的组织下还成立了一个CLIPS用户协会，以推动 CLIPS的开发与应用。更详细的信息可访问它的官方网站<a href="http://prglab.com/blog/wp-admin/http:%20//www.ghg.net/clips/CLIPS.html" target="_blank">http: //www.ghg.net/clips/CLIPS.html</a>。 由于CLIPS的强大功能与良好性能，以及NASA对该软件采取的开放政策，CLIPS 在美国国内外都有众多用户，他们应用CLIPS开发了许多实用的专家系统，并根据各自的需要对CLIPS进行扩充和改造，如加拿大研究委员会在CLIPS 中加入了模糊推理功能，推出了FuzzyCLIPS，我们也成功地在CLIPS中加入了图形功能，推出了图形化专家系统工具GEST（Graphical Expert System Shell）。</p>
<p><strong>新一代工具Jess</strong></p>
<p>Jess是1995年由美国Sandia国家 实验室分布式系统计算组成员Ernest J. Friedman-Hill用Java实现的一个经过扩充的CLIPS版本。它以CLIPS的设计原理为基础进行编写，除继承了CLIPS的优点外，还具 有许多自己独特的特征，如支持正向和逆向推理，可以在系统运行环境下直接调用Java的类库等，这些特点将专家系统的开发过程同功能强大的Java语言结 合起来，使采用Jess语言开发的专家系统具有良好的移植性、嵌入性，可以方便地应用到网络上的不同机器中。另外，Java多线程机制使Jess可以与其 他应用程序并发执行，同步机制保证了对共享数据的正确操作，通过使用不同的线程完成特定的行为，就可以很容易地实现网络上的实时交互行为。目前，Jess 被广泛用于学术、工业、商业等领域，是一个有着广阔发展前景的专家系统开发平台。</p>
<p><strong>1. Jess的基本组成和知识表示</strong></p>
<p>同 大多数专家系统工具一样，Jess的核心也是由事实库、规则库、推理机三大部分组成，并采用产生式规则作为基本的知识表达模式。在Jess中，事实包括简 单事实和对象事实。简单事实就是一个描述事物的断言，而对象事实除此之外还封装了方法，可以接受外界信息改变自身的特征。这一概念本身并不是Jess首次 提出的，ART-IM、CLIPS都支持这一概念。但Jess表达对象事实的方法确实别具一格：它用Java而非系统本身的语言来定义对象。在CLIPS 6.0中，对象事实通过系统本身的语句defclass和make-instance来定义，但在Jess中，类的定义由Java语言书写，编译通过后即 可动态地加入系统中。用Java虚拟机编译通过后，通过defclass命令将该类加入系统，它就可以执行类似于CLIPS中对类的各种操作，如生成它的 一个实例、调用它的方法等。由此可见，Jess可以方便地调用Java中的类库，使用Java中的各种数据结构和方法，从而具备其他系统不可比拟的优良的 嵌入能力。</p>
<p>Jess通过模式匹配语言对事实进行操作。在Jess中，模式匹配操作符的类型有很多，从可以同任意事实进行匹配的单一操作符到 只能同满足特定约束值的事实进行匹配的复杂操作符。特别要指出的是，Jess中有“unique”条件元素，它告诉系统同该模式匹配的事实是惟一的。这 样，当模式发现一条事实同它匹配后，就会停止对事实库的检索，在实际应用中，这可以将系统的性能提高20％～30％，而CLIPS系统不支持这一条件元 素。在Jess中，规则的表达形式沿用了CLIPS的语法结构，通过对规则前件和后件的限定，它可以支持内容丰富的模式匹配语言。另外，Jess支持面向 过程的编程方式，它提供了一些语句来控制规则后件的操作流程，如使用if…then…else和while…do…语句，这样它就能很有效地利用面向过程 编程的优势。总之，Jess的这些特性使系统拥有很强的知识表示能力。</p>
<p><strong>2．Jess的推理机制</strong></p>
<p>Jess 支持前向和逆向推理两种方式，前向推理同CLIPS的原理相同，逆向推理则是Jess不同于CLIPS的一个显著特征。在Jess的逆向推理中，规则仍采 用if…then…结构，但是在逆向推理时，推理引擎执行的是前件没有得到满足的规则，这种行为常常被称为目标寻找。显然，Jess同时支持前向和逆向推 理的特点使其推理能力得到了加强。推理的效率很大程度上依赖于匹配算法的效率。Jess通过实现Rete匹配算法来提供非常高效的前向和逆向推理。 Rete算法利用了专家系统中时间冗余性和结构相似性这两个特点，有效地减少了用于匹配操作的次数。因此，当系统的性能是由匹配算法的质量决定时， Jess的优点将更为明显。应当指出的是，Rete算法是一个以空间换取时间的算法，所以，应用Jess时应当考虑内存的消耗。</p>
<p><strong>3．开发环境和文档支持</strong><br />
Jess提供了一个交互式的、命令行的开发环境，但也可以使用文本编辑器编辑代码，然后再通过系统命令以批处理的方式载入到系统中。同时它还提供了一个简 单的图形开发环境。类似于CLIPS，Jess非常重视系统调试工具和错误跟踪这两方面。它的调试命令可以帮助使用者监视事实库、知识库中的内容，以及议 程表中被激活规则的情况，也可以监视某一规则前件的部分匹配的情况。在错误跟踪方面，Jess中的JessException类提供了丰富的错误信息，包 含了对出错问题的解释和堆栈跟踪表中的信息，如出错子函数的名字、相关的语句、出错的行数等，可以很好地帮助开发人员发现错误原因。</p>
<p>Jess提供了一个非常完备的电子文档，它可以帮助开发人员很快查询到需要的信息，将Jess嵌入到Java应用程序中。由于Jess在很大程度上是CLIPS的克隆版本，感兴趣的读者可参考CLIPS用户手册和参考手册。</p>
<p><strong>4．Jess的进一步扩充——FuzzyJ Toolkit</strong></p>
<p>Jess 自从问世以来就没有停止过对功能的扩展，有许多研究技术人员不断地在Jess的基础上添加新的辅助功能。加拿大国家研究委员会信息技术研究所的 R.A.Orchard早在1995年就扩展了CLIPS的功能，将模糊推理的功能加入到CLIPS中，推出了模糊专家系统外壳工具 FuzzyCLIPS。后随着Jess的推出，R.A.Orchard将工作重心转移到Jess上来，将模糊推理的功能加入到Jess中，推出了 FuzzyJ Toolkit。FuzzyJ Toolkit本身就是一个模糊专家系统外壳，但只支持模糊前件和模糊后件这些基本的推理方式。它可以和Jess结合，形成功能更为强大的专家系统外壳 FuzzyJess，FuzzyJess不但可以表达精确事实、模糊事实、执行模糊推理，还具有Jess的强大功能。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.prglab.com/blog/p/29/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

