<?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>Neo | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/articles/author/neo/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Sat, 24 Mar 2012 13:57:38 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>神奇的CSS形状</title>
		<link>https://coolshell.cn/articles/6913.html</link>
					<comments>https://coolshell.cn/articles/6913.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Sat, 24 Mar 2012 12:35:41 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[趣味问题]]></category>
		<category><![CDATA[CSS]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6913</guid>

					<description><![CDATA[<p>【感谢 Neo 投递本文 – 微博帐号：@_锟_ 】 在StackOverflow上有这么一个问题，有位同学在http://css-tricks.com/exa...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6913.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6913.html">神奇的CSS形状</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>【<span style="color: #cc0000;">感谢 Neo 投递本文 – 微博帐号</span>：<a title="_锟_" href="http://weibo.com/gandalfthegrey" target="_blank">@_锟_</a> 】</p>
<p style="text-align: left;">在StackOverflow上有这么一个问题，有位同学在<a href="http://css-tricks.com/examples/ShapesOfCSS/">http://css-tricks.com/examples/ShapesOfCSS/ </a> 找到一些使用CSS做的形状，其中一位同学对下面的这个形状充满了疑问。</p>
<p style="text-align: left;">形状是：</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6914" src="https://coolshell.cn/wp-content/uploads/2012/03/a.png" alt="" width="103" height="102" /></p>
<p style="text-align: left;">代码是：</p>
<pre data-enlighter-language="css" class="EnlighterJSRAW">
#triangle-up {
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red;
}
</pre>
<p>这位同学就提问啦，为啥这么这么几句就能画出一个三角形呢？<br />
于是呢，有高人出现，这个高人图文并茂的解释了这个三角的成因</p>
<p><span id="more-6913"></span><br />
首先呢，我们需要了解HTML标记的Box Model（盒模型），这个例子中呢我们将content，padding都看作content。忽略掉margin。那么一个盒模型就是下图</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6915" src="https://coolshell.cn/wp-content/uploads/2012/03/b.png" alt="" width="340" height="266" srcset="https://coolshell.cn/wp-content/uploads/2012/03/b.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/b-300x234.png 300w" sizes="(max-width: 340px) 100vw, 340px" /></p>
<p>中间是内容，然后是4条边。每一条边都有宽度。<br />
根据上面CSS的定义，没有border-top（顶边）的情形下 ,我们的图形如下：</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6916" src="https://coolshell.cn/wp-content/uploads/2012/03/c.png" alt="" width="340" height="266" srcset="https://coolshell.cn/wp-content/uploads/2012/03/c.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/c-300x234.png 300w" sizes="(max-width: 340px) 100vw, 340px" /></p>
<p>width设置为0后 ，内容没有了就成为下图：</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6917" src="https://coolshell.cn/wp-content/uploads/2012/03/d.png" alt="" width="340" height="266" srcset="https://coolshell.cn/wp-content/uploads/2012/03/d.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/d-300x234.png 300w" sizes="(max-width: 340px) 100vw, 340px" /></p>
<p>height也设置为0，只有底边了。</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6918" src="https://coolshell.cn/wp-content/uploads/2012/03/e.png" alt="" width="200" height="122" /></p>
<p>然后两条边都是设置为透明，最后我们就得到了</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6919" src="https://coolshell.cn/wp-content/uploads/2012/03/f.png" alt="" width="200" height="122" /></p>
<p>这个属于奇技淫巧，但是也说明CSS的强大，没有做不到只有想不到。另外<a href="http://css-tricks.com/examples/ShapesOfCSS/">http://css-tricks.com/examples/ShapesOfCSS/ </a>还能找到很多其他的形状，感兴趣的同学可以自己去看。还有酷壳以前的这篇文章《<a title="CSS图形" href="https://coolshell.cn/articles/5164.html" target="_blank">CSS实现的各种形状</a>》<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif" alt="CSS 布局:40个教程、技巧、例子和最佳实践" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_title">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5164.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg" alt="CSS图形" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5164.html" class="wp_rp_title">CSS图形</a></li><li ><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="一些有意思的贴子和工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_title">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6913.html">神奇的CSS形状</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6913.html/feed</wfw:commentRss>
			<slash:comments>21</slash:comments>
		
		
			</item>
		<item>
		<title>CSS 布局:40个教程、技巧、例子和最佳实践</title>
		<link>https://coolshell.cn/articles/6840.html</link>
					<comments>https://coolshell.cn/articles/6840.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Mon, 19 Mar 2012 00:25:46 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6840</guid>

					<description><![CDATA[<p>【感谢 Neo 投递本文 – 微博帐号：_锟_ 】 前言： 布局是WEB开发一个重要的课题，进入XHTML/CSS后，使用TABLE布局的方式逐渐淡出，CSS布...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6840.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6840.html">CSS 布局:40个教程、技巧、例子和最佳实践</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>【<span style="color: #cc0000;">感谢 Neo 投递本文 – 微博帐号</span>：<a title="_锟_" href="http://weibo.com/gandalfthegrey" target="_blank">_锟_</a> 】</p>
<p><strong>前言：</strong> 布局是WEB开发一个重要的课题，进入XHTML/CSS后，使用TABLE布局的方式逐渐淡出，CSS布局以众多优点成为主流，本文将介绍40个基于CSS的web布局的资源和教程。文章的出处在<a href="http://www.noupe.com/css/css-layouts-40-tutorials-tips-demos-and-best-practices.html" target="_blank">http://www.noupe.com/css/css-layouts-40-tutorials-tips-demos-and-best-practices.html</a>。文中的不少的例子在一本经典的CSS书籍<a href="http://shop.oreilly.com/product/9780596802455.do">《CCS: The Missing Manual, 2nd Edition》</a>中都可以找到，据我所知，第二版在中国没有翻译出版。你可以从<a href="http://www.itpub.net/forum.php?mod=viewthread&amp;tid=1210179&amp;highlight=CSS%2Bthe%2Bmissing%2Bmanual">这里</a>下载英文版（不过需要注册个用户名）</p>
<p><strong>正文</strong><br />
<strong>基于CSS的布局</strong>能提供更灵活布局方式和更强的用户视觉体验。一些重要技巧和关键点可以帮助初学者理解CSS布局的基础和本质。这也是本文成文的原因 ——找到那些完美的布局，<strong>完全灵活的，等高栏</strong>和工作完美的布局。<br />
因此下面这个列表就是我们整理了网络上关于基于CSS布局的一些技巧，教程和最佳实践的列表。<br />
当然你也可能对下面这些和CSS相关的主题有兴趣：</p>
<p><a href="http://www.noupe.com/css/9-timeless-3-column-layout-techniques.html&gt;9 Timeless 3 Column Layout Techniques&lt;/a&gt;&lt;/ui&gt;&lt;br /&gt; &lt;ui&gt;&lt;a href=">The 7 CSS Hacks that we should use</a><br />
<a href="http://www.noupe.com/css/using-css-to-do-anything-50-creative-examples-and-tutorials.html">Using CSS to Do Anything: 50+ Creative Examples and Tutorials</a><br />
<a href="http://www.noupe.com/css/using-css-to-fix-anything-20-common-bugs-and-fixes.html">Using CSS to Fix Anything: 20+ Common Bugs and Fixes</a></p>
<p><span id="more-6840"></span></p>
<h4><strong><span style="color: #008000;">CSS 布局教程</span></strong></h4>
<p>1-<a href="http://woork.blogspot.com/2008/01/three-column-fixed-layout-structure.html">使用CSS完成三栏固定布局结构</a>&#8211; 这篇文章解释了如何实现一个基于的HTML/CSS来设计一个简单的带有基本要素（顶部的logo条，导航条，文本区，定义分类的中部栏，右边侧栏插入google的120X600的广告区）的固定三栏页面布局。</p>
<p><img decoding="async" loading="lazy" class="size-full wp-image-6843 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts.gif" alt="" width="450" height="297" /></p>
<p>2-<a href="http://woork.blogspot.com/2007/10/design-page-layout-using-css.html">使用CSS设计页面布局</a>&#8211; 如何使用CSS文件来为你的站点设计页面布局。<br />
<img decoding="async" loading="lazy" class="size-full wp-image-6844 aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts2.gif" alt="" width="450" height="200" /></p>
<p>3-<a href="http://css-tricks.com/how-to-create-a-horizontally-scrolling-site/">如何创建一个水平布局的站点</a>&#8211; 创建不同于常规的水平布局的站点技术（译者注：水平布局，客户体验也就仁者见仁了）</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6845" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts3.gif" alt="" width="450" height="190" /><br />
例子<a href="http://css-tricks.com/examples/HorzScrolling">查看这里</a> |<a href="http://css-tricks.com/examples/HorzScrolling.zip">下载</a></p>
<p>4-<a href="http://css-tricks.com/super-simple-two-column-layout/">超级简单的两栏布局</a>&#8211; 创建不同于常规的水平布局的站点技术（译者注：这里是原作者笔误吧和上面的内容一样）.</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6849" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts4.gif" alt="" width="450" height="100" /></p>
<p>例子<a href="http://css-tricks.com/examples/SuperSimpleTwoColumn">查看这里</a> <a href="http://css-tricks.com/examples/SuperSimpleTwoColumn.zip">下载</a></p>
<p>5-<a href="http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/">简单两栏CSS布局</a>&#8211; 这是一个创建简单两栏布局的教程。这种布局包含了一个标题区，一个水平导航条，主内容区，边侧栏，和页脚区。并且这个布局是水平居中的。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts6.gif" alt="" width="450" height="150" /></p>
<p>例子<a href="http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/finished.html">查看这里</a></p>
<p>6-<a href="http://dnevnikeklektika.com/en/the-holy-grail-layout-3-columns-and-a-lot-less-problems">圣杯布局(The holy grail layout)</a> – 3栏布局会有一些问题 ，这篇文章讨论了一种三栏布局——两栏固定宽度边侧栏加上一栏变宽中栏布局，保证了页面的良好结构和清晰。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts7.gif" alt="" width="450" height="362" /></p>
<p>例子<a href="http://dnevnikeklektika.com/css/3ColLayout/working.html">查看这里</a></p>
<p>7-<a href="http://www.simplebits.com/notebook/2004/09/08/centering.html">CSS居中101</a>&#8211; 如何使用CSS完成居中一个固定宽度的布局</p>
<p>使用CSS，通过下面两条规则完成对id为container的DIV所包含的内容居中</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;body&gt;
 &lt;div id=&quot;container&quot;&gt; ...entire layout goes here...
&lt;/div&gt;
&lt;/body&gt;
</pre>
<pre data-enlighter-language="css" class="EnlighterJSRAW">
body {
    text-align: center;
}
#container {
    margin: 0 auto;
    width: xxxpx;
    text-align: left;
}
</pre>
<p>8-<a href="http://www.subcide.com/tutorials/csslayout/index.aspx">从头创建CSS布局</a>&#8211; 这个指南通过创建一个全功能的 CSS布局来一步步教你入门CSS布局。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts9.gif" alt="" width="450" height="255" /></p>
<p>9-<a href="http://www.alistapart.com/articles/multicolumnlayouts/">非主流！多栏布局</a>&#8211; 多栏布局，等高栏（每一列的高度都相等），固定或变宽中央区，简洁标记，CSS 。(译者注：原文作者的图配的和上图一样)</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts9.gif" alt="" width="450" height="255" /></p>
<p>例子<a href="http://www.alistapart.com/d/multicolumnlayouts/3ColLiquid.html">查看这里</a></p>
<p>10- <a href="http://www.positioniseverything.net/articles/onetruelayout/">创建天下无双的CSS布局</a>&#8211; 高灵活性布局,等高栏，跨栏垂直摆放元素。本文告诉你通过何等手段完成这些目标，并使用它们创建天下无双的CSS布局（译者注:原文是One True Layout ，不知道怎么翻译，就天下无双吧。）</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts22.gif" alt="" width="450" height="180" /></p>
<p><a href="http://www.positioniseverything.net/articles/onetruelayout/examples">查看这里</a></p>
<p>11-<a href="http://nettuts.com/site-builds/from-psd-to-html-building-a-set-of-website-designs-step-by-step/">从PSD到HTML，手把手完成WEB设计</a>-从Photoshop到完整HTML，全过程手把手教会你。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts34.jpg" alt="" width="450" height="341" /></p>
<p>例子<a href="http://nettuts.s3.amazonaws.com/017_Creatif/Site/index.html">查看这里</a> | <a href="http://nettuts.s3.amazonaws.com/017_Creatif/Site_Download.zip">下载</a></p>
<p>12- <a href="http://tutorialblog.org/5-tips-for-coding-xhtmlcss-layouts/">5个XHTML/CSS技巧</a> &#8211; 5个CSS技巧帮助你完成从基于表格的布局到基于CSS的布局。</p>
<p>13-<a href="http://veerle.duoh.com/index.php/blog/comments/designing_a_css_based_template_part_i/">设计一个基于CSS的模板</a> &#8211; 这是一个教你创建基于CSS的模板页的基础教程。这个教程由下面几个部分构成：第一部分覆盖了在Photoshop CS*中的创建导航条按钮，第二部分：创建背景接下来的清单是标题和页面布局，最后的部分在XHTML和CSS中实现。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts35.jpg" alt="" width="450" height="250" /></p>
<p><a href="http://homepage.mac.com/vpieters/css_step2/step2_whooshes.mov.zip">下载</a></p>
<p>14-<a href="http://www.sitepoint.com/article/breaking-out-of-the-box">使用CSS布局跳出常规布局</a>&#8211; 如果你理解了基于表格布局的工作方式，你能通过合并或拆分表格创建你随心所欲的布局。就这个目标（同时支持灵活性和可维护性），CSS能够提供比基于表格更多地东西。Jina Bolton的教程解释如何达到这个目标。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts36.gif" alt="" width="450" height="250" /></p>
<p>15-<a href="http://www.webreference.com/authoring/style/sheets/layout/advanced/">高级CSS教程:手把手</a>&#8211; 这个教程的终极目标创建一个CSS布局，这个CSS布局精确地重组了原有使用table的WebReference.com的布局。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts29.gif" alt="" width="450" height="263" /></p>
<p>16-<a href="http://snook.ca/archives/html_and_css/six_keys_to_understanding_css_layouts/">了解CSS布局的6个关键要素</a>-本文讲述了6件基于CSS布局需要了解的事情：盒模型(Box Model)，浮动栏(Floated Columns) （译者注：float是WEB布局最重要的一个属性了）。使用Em来设置尺寸（Sizing Using Ems），图片替换（Image Replacement）,浮动导航和Sprintes。</p>
<p>17-<a href="http://wisdump.com/design/are-you-making-these-common-blog-layout-mistakes/">你会犯这些常见的博客布局错误吗？</a>-讨论4个博客布局中常见而且易修复的错误。</p>
<p>18-<a href="http://www.htmldog.com/guides/cssadvanced/layout/">页面布局</a>-CSS页面布局中的浮动元素和定位元素实践指导。</p>
<p>你可以查看这些例子：<a href="http://www.htmldog.com/examples/positioning4.html">Absolute Position within a relative box</a><a href="http://www.htmldog.com/examples/float2.html"> two floated boxes</a>和<a href="http://www.htmldog.com/examples/pagelayout3.html"> using a border to provide the background for a column</a></p>
<p>19-<a href="http://leftjustified.net/site-in-an-hour/">Site in an Hour</a>&#8211; 使用复杂CCS布局完成简单的工作。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts40.jpg" alt="" width="450" height="250" /></p>
<h4><strong><span style="color: #008000;">关于布局的最佳资源</span></strong></h4>
<p>下面的大多数这些资源不需要许可就能直接使用，然而，其中的一些需要先发邮件确认一下是否可以使用这些资源。因此，在使用之前最好先检查资源的版权信息。</p>
<p>20-<a href="http://www.maxdesign.com.au/presentation/page_layouts/">简单CSS页面布局</a>&#8211; 这里有一套2栏和3栏的CSS布局。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts10.jpg" alt="" width="450" height="255" /></p>
<p>你可以通过这里查看这些样例<a href="http://www.maxdesign.com.au/presentation/process/example23.htm"> Liquid three column layout</a>,<a href="http://www.maxdesign.com.au/presentation/page_layouts/single04.htm"> Left aligned, set width</a> and <a href="http://www.maxdesign.com.au/presentation/liquid/example13.htm">Liquid insanity</a>.</p>
<p>21-<a href="http://matthewjamestaylor.com/blog/perfect-3-column.htm">完美的三栏变宽布局（百分比定宽度）The Perfect 3 Column Liquid Layout (Percentage widths)</a>&#8211; 没有CSS hack（译者注：不知道怎么翻译，点击<a href="http://baike.baidu.com/view/1119452.htm">这里</a>查看解释）. 良好地收索引擎优化.无图. 无Javascript. 跨浏览器 和IPHONE设备兼容</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts11.gif" alt="" width="450" height="370" /></p>
<p>你可以通过这里查看样例 <a href="http://www.maxdesign.com.au/presentation/process/example23.htm">Liquid three column layout</a>, <a href="http://www.maxdesign.com.au/presentation/page_layouts/single04.htm">Left aligned, set width</a> 和 <a href="http://www.maxdesign.com.au/presentation/liquid/example13.htm">Liquid insanity</a>. (译者注：这里的链接和上面重复了，哎，原文的错误吧)</p>
<p>22-<a href="http://www.intensivstation.ch/en/templates/">CSS模板和样例</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts21.gif" alt="" width="450" height="380" /></p>
<p>你可以通过这里查看这些样例<a href="http://www.intensivstation.ch/files/en_templates/temp06.html"> 3 columns fixed</a> <a href="http://www.intensivstation.ch/files/en_templates/temp06.html">centered</a>, <a href="http://www.intensivstation.ch/files/en_templates/temp11.html">fixed Box totally</a><a href="http://www.intensivstation.ch/files/en_templates/temp11.html">centered</a> and <a href="http://www.intensivstation.ch/files/en_templates/temp03.html">3 columns, all</a><a href="http://www.intensivstation.ch/files/en_templates/temp03.html">dynamic</a></p>
<p>23-<a href="http://layouts.ironmyers.com/">IM 布局</a>&#8211; IM 布局是一种简单地的CSS布局系统，IM布局提供了全A级的浏览器的支持。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts24.gif" alt="" width="450" height="250" /></p>
<p>你可以通过这里查看这些样例:<a href="http://www.ironmyers.com/examples/three_column_layout.html"> The Holy Grail 3 Column Layout</a>, <a href="http://www.ironmyers.com/examples/classic_blog.html">The Classic Blog Layout </a>和<a href="http://www.ironmyers.com/examples/multi_column.html"> The Multi Column Layout.</a></p>
<p>24-<a href="http://www.cssplay.co.uk/layouts/index.html">CSSplay </a>&#8211; CSS布局列表</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts25.gif" alt="" width="450" height="87" /></p>
<p>你可以通过这里查看这些样例:<a href="http://www.cssplay.co.uk/layouts/fixit.html">Cross browser FIXED</a>, <a href="http://www.cssplay.co.uk/layouts/threecol.html">Three columns</a> and <a href="http://www.cssplay.co.uk/layouts/frame.html">CSS Frame – The Holy Grill</a>.</p>
<p>25-<a href="http://blog.html.it/layoutgala/">Layoutgala </a>&#8211; 基于同样的的标记l得到最大数量的不同的布局方式。没有CCS hack，没有CSS workaround ，良好的浏览器兼容性。40种不同布局。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts26.gif" alt="" width="450" height="180" /></p>
<p>你可以通过这里查看这些样例:<a href="http://blog.html.it/layoutgala/LayoutGala07.html">Three fixed Columns</a>, <a href="http://blog.html.it/layoutgala/LayoutGala04.html">Three percentage columns</a> and <a href="http://blog.html.it/layoutgala/LayoutGala19.html">Liquid, three columns, hybrid widths </a>(吐槽：没有等高，不好看).</p>
<p>26-<a href="http://www.glish.com/css/">Glish</a>&#8211; 许多有用的跨浏览器布局技术</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts27.gif" alt="" width="450" height="125" /></p>
<p>你可以通过这里查看这些样例: <a href="http://www.glish.com/css/7.asp">3 columns, the holy grail</a>,<a href="http://www.glish.com/css/9.asp"> 2 columns, ALA style</a> and <a href="http://www.glish.com/css/2.asp">3 columns, all fluid </a></p>
<p>27-<a href="http://www.thenoodleincident.com/tutorials/box_lesson/boxes.html">Thenoodleincident</a>&#8211; CSS 从简单的单盒到3盒并增加一个顶部条，所有都是变宽。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts28.gif" alt="" width="450" height="140" /></p>
<p>28-<a href="http://www.bluerobot.com/web/layouts/">The Layout Reservoir</a>&#8211; 很多有用的CSS布局技术</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts30.gif" alt="" width="450" height="225" /></p>
<p>你可以通过这里查看这些样例:<a href="http://bluerobot.com/web/layouts/layout1.html"> 2 columns – left menu</a>,<a href="http://bluerobot.com/web/layouts/layout3.html"> 3 columns – flanking menus</a>和<a href="http://bluerobot.com/web/css/center1.html"> Auto-width Margins </a>.</p>
<p>29-<a href="http://www.strictlycss.com/articles/article/40/the-only-css-layout-you-need">The only CSS layout you need</a>&#8211; 在这篇文章中将会为你展现10个基于同一的HTML的不同的的布局。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts32.gif" alt="" width="450" height="225" /></p>
<p>你可以通过这里查看这些样例: <a href="http://www.strictlycss.com/examples/three-column-layout-1.asp"> Three column CSS layout – left and right menu</a>, <a href="http://www.strictlycss.com/examples/three-column-layout-2.asp">Two column CSS layout – top and left menu</a> 和 <a href="http://www.strictlycss.com/examples/three-column-layout-7.asp">Three column CSS fluid layout: 100% width</a></p>
<p>30-<a href="http://www.yaml.de/">另一个多栏布局</a>-是一个创建当代流行的变宽的浮动布局的XHTML/CSS框架。这是一个多功能实用的布局。</p>
<p>点击<a href="http://www.yaml.de/fileadmin/download/release_306/yaml_306_080609.zip">这里</a>下载.</p>
<p>31-<a href="http://www.cssliquid.com/">Liquid Designs</a>&#8211; 使用XHTML和CSS的变宽设计库。</p>
<h4><strong><span style="color: #008000;">最佳实践</span></strong></h4>
<p>如果你需要寻找一些布局灵感，你可以从下面的网站链接中找到。这些站点演示了CSS布局如何应用于不同类型的网站。查看这些网站是如何分成2栏或3栏，或混合宽栏和窄栏布局。</p>
<p>32-<a href="http://helldesign.net/">Helldesign</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts5.jpg" alt="" width="450" height="365" /></p>
<p>33-<a href="http://silverbackapp.com/">Silverbackapp</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts12.jpg" alt="" width="450" height="325" /></p>
<p>34-<a href="http://www.os.ca/accueil.php">OS communications informatiques</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts13.jpg" alt="" width="450" height="310" /></p>
<p>35-<a href="http://rockatee.com/">Rockatee</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts14.jpg" alt="" width="450" height="310" /></p>
<p>36-<a href="http://www.darrenhoyt.com/">Darrenhoyt</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts15.jpg" alt="" width="450" height="225" /></p>
<p>37-<a href="http://www.makebetterwebsites.com/">Makebetterwebsites</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts16.jpg" alt="" width="450" height="250" /></p>
<p>38-<a href="http://elitetheme.com/">Elitetheme</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts17.jpg" alt="" width="450" height="250" /></p>
<p>39-<a href="http://www.studio7designs.com/">Studio7designs</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts18.jpg" alt="" width="450" height="250" /></p>
<p>40-<a href="http://brightcreative.com/">Brightcreative</a></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts19.jpg" alt="" width="450" height="300" /></p>
<p><em>(全文完)</em><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="一些有意思的贴子和工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_title">一些有意思的贴子和工具</a></li><li ><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg" alt="Web开发人员速查卡" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_title">Web开发人员速查卡</a></li><li ><a href="https://coolshell.cn/articles/3013.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg" alt="一些非常有意思的杂项资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3013.html" class="wp_rp_title">一些非常有意思的杂项资源</a></li><li ><a href="https://coolshell.cn/articles/1949.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg" alt="Web中的省略号" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1949.html" class="wp_rp_title">Web中的省略号</a></li><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6840.html">CSS 布局:40个教程、技巧、例子和最佳实践</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6840.html/feed</wfw:commentRss>
			<slash:comments>31</slash:comments>
		
		
			</item>
		<item>
		<title>理解Javascript的闭包</title>
		<link>https://coolshell.cn/articles/6731.html</link>
					<comments>https://coolshell.cn/articles/6731.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Wed, 07 Mar 2012 00:30:43 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[ECMAScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[OOP]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6731</guid>

					<description><![CDATA[<p>【感谢 Neo 投递本文 &#8211; 微博帐号：_锟_ 】 前言：还是一篇入门文章。Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6731.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6731.html">理解Javascript的闭包</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>【<span style="color: #cc0000">感谢 Neo 投递本文 &#8211; 微博帐号：<a title="_锟_" href="http://weibo.com/gandalfthegrey" target="_blank">_锟_</a> </span>】</p>
<p><strong>前言：还是一篇入门文章。</strong>Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性，并结合一点ECMAScript语言规范来使读者可以更深入的理解闭包。</p>
<p>注：<strong>本文是入门文章，例子素材整理于网络<strong>，如果你是高手，欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript，不想做语言对比，如果您对Javascript天生不适，请自行绕道。</strong></strong></p>
<h4><strong><span style="color: #008000">什么是闭包</span></strong></h4>
<p>闭包是什么?闭包是Closure，这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西，简而言之，闭包就是：<strong></strong></p>
<ul>
<li><strong>闭包就是函数的局部变量集合，只是这些局部变量在函数返回后会继续存在。</strong></li>
<li><strong>闭包就是就是函数的“堆栈”在函数返回后并不释放，我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配</strong></li>
<li><strong>当在一个函数内定义另外一个函数就会产生闭包</strong></li>
</ul>
<p>上面的第二定义是第一个补充说明，抽取第一个定义的主谓宾——闭包是<strong>函数的‘局部变量’集合</strong>。只是这个局部变量是可以在函数返回后被访问。（这个不是官方定义，但是这个定义应该更有利于你理解闭包）</p>
<p>做为局部变量都可以被函数内的代码访问，这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”，或将这个&#8221;引用&#8221;赋值给某个外部变量，才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个对象，因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是，ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变量。但是在ECMAScript中，函数对象中定义的<strong>内部函数(inner function)</strong>是可以直接访问外部函数的局部变量，通过这种机制，我们就可以以如下的方式完成对闭包的访问了。</p>
<p><span id="more-6731"></span></p>
<p>[javascript]<br />
function greeting(name) {<br />
    var text = &#8216;Hello &#8216; + name; // local variable<br />
    // 每次调用时，产生闭包，并返回内部函数对象给调用者<br />
    return function() { alert(text); }<br />
}<br />
var sayHello=greeting(&quot;Closure&quot;);<br />
sayHello()  // 通过闭包访问到了局部变量text<br />
[/javascript]</p>
<p>上述代码的执行结果是：Hello Closure，因为sayHello()函数在greeting函数执行完毕后，仍然可以访问到了定义在其之内的局部变量text。</p>
<p>好了，这个就是传说中闭包的效果，闭包在Javascript中有多种应用场景和模式，比如Singleton，Power Constructor等这些Javascript模式都离不开对闭包的使用。</p>
<h4><strong><span style="color: #008000">ECMAScript闭包模型</span></strong></h4>
<p>ECMAScript到底是如何实现闭包的呢？想深入了解的亲们可以获取<a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">ECMAScript 规范</a>进行研究，我这里也只做一个简单的讲解，内容也是来自于网络。</p>
<p>在ECMAscript的脚本的函数运行时，每个函数关联都有一个执行上下文场景(Execution Context) ，这个执行上下文场景中包含三个部分</p>
<ul>
<li>文法环境（The LexicalEnvironment）</li>
<li>变量环境（The VariableEnvironment）</li>
<li>this绑定</li>
</ul>
<p>其中第三点this绑定与闭包无关，不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象，该对象包含了两个重要组件，环境记录(Enviroment Recode)，和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量，外部引用指向了外部函数对象的上下文执行场景。全局的上下文场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表，每个引用都指向外层的上下文场景。</p>
<p>例如上面我们例子的闭包模型应该是这样，sayHello函数在最下层，上层是函数greeting，最外层是全局场景。如下图：<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6741" src="https://coolshell.cn/wp-content/uploads/2012/03/closure.png" alt="" width="658" height="478" srcset="https://coolshell.cn/wp-content/uploads/2012/03/closure.png 658w, https://coolshell.cn/wp-content/uploads/2012/03/closure-300x218.png 300w, https://coolshell.cn/wp-content/uploads/2012/03/closure-372x270.png 372w" sizes="(max-width: 658px) 100vw, 658px" /><br />
因此当sayHello被调用的时候，sayHello会通过上下文场景找到局部变量text的值，因此在屏幕的对话框中显示出&#8221;Hello Closure&#8221;<br />
变量环境(The VariableEnvironment)和文法环境的作用基本相似，具体的区别请参看ECMAScript的规范文档。</p>
<h4><strong><span style="color: #008000">闭包的样列</span></strong></h4>
<p>前面的我大致了解了Javascript闭包是什么，闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包，下面共有5个样例，例子来自于<a href="http://blog.morrisjohns.com/javascript_closures_for_dummies.html">JavaScript Closures For Dummies(</a><a href="http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies">镜像</a><a href="http://blog.morrisjohns.com/javascript_closures_for_dummies.html">)</a>。<br />
<strong>例子1:闭包中局部变量是引用而非拷贝</strong></p>
<p>[javascript]<br />
function say667() {<br />
    // Local variable that ends up within closure<br />
    var num = 666;<br />
    var sayAlert = function() { alert(num); }<br />
    num++;<br />
    return sayAlert;<br />
}</p>
<p>var sayAlert = say667();<br />
sayAlert()<br />
[/javascript]</p>
<p>因此执行结果应该弹出的667而非666。</p>
<p><strong>例子2：多个函数绑定同一个闭包，因为他们定义在同一个函数内。</strong></p>
<p>[javascript]<br />
function setupSomeGlobals() {<br />
    // Local variable that ends up within closure<br />
    var num = 666;<br />
    // Store some references to functions as global variables<br />
    gAlertNumber = function() { alert(num); }<br />
    gIncreaseNumber = function() { num++; }<br />
    gSetNumber = function(x) { num = x; }<br />
}<br />
setupSomeGlobals(); // 为三个全局变量赋值<br />
gAlertNumber(); //666<br />
gIncreaseNumber();<br />
gAlertNumber(); // 667<br />
gSetNumber(12);//<br />
gAlertNumber();//12<br />
[/javascript]</p>
<p><strong>例子3：当在一个循环中赋值函数时，这些函数将绑定同样的闭包</strong></p>
<p>[javascript]<br />
function buildList(list) {<br />
    var result = [];<br />
    for (var i = 0; i &lt; list.length; i++) {<br />
        var item = &#8216;item&#8217; + list[i];<br />
        result.push( function() {alert(item + &#8216; &#8216; + list[i])} );<br />
    }<br />
    return result;<br />
}</p>
<p>function testList() {<br />
    var fnlist = buildList([1,2,3]);<br />
    // using j only to help prevent confusion &#8211; could use i<br />
    for (var j = 0; j &lt; fnlist.length; j++) {<br />
        fnlist[j]();<br />
    }<br />
}<br />
[/javascript]</p>
<p>testList的执行结果是弹出item3 undefined窗口三次，因为这三个函数绑定了同一个闭包，而且item的值为最后计算的结果，但是当i跳出循环时i值为4，所以list[4]的结果为undefined.</p>
<p><strong>例子4：外部函数所有局部变量都在闭包内，即使这个变量声明在内部函数定义之后。</strong></p>
<p>[javascript]<br />
function sayAlice() {<br />
    var sayAlert = function() { alert(alice); }<br />
    // Local variable that ends up within closure<br />
    var alice = &#8216;Hello Alice&#8217;;<br />
    return sayAlert;<br />
}<br />
var helloAlice=sayAlice();<br />
helloAlice();<br />
[/javascript]</p>
<p>执行结果是弹出&#8221;Hello Alice&#8221;的窗口。即使局部变量声明在函数sayAlert之后，局部变量仍然可以被访问到。</p>
<p><strong>例子5：每次函数调用的时候创建一个新的闭包</strong></p>
<p>[javascript]<br />
function newClosure(someNum, someRef) {<br />
    // Local variables that end up within closure<br />
    var num = someNum;<br />
    var anArray = [1,2,3];<br />
    var ref = someRef;<br />
    return function(x) {<br />
        num += x;<br />
        anArray.push(num);<br />
        alert(&#8216;num: &#8216; + num +<br />
        &#8216;\nanArray &#8216; + anArray.toString() +<br />
        &#8216;\nref.someVar &#8216; + ref.someVar);<br />
    }<br />
}<br />
closure1=newClosure(40,{someVar:&#8217;closure 1&#8242;});<br />
closure2=newClosure(1000,{someVar:&#8217;closure 2&#8242;});</p>
<p>closure1(5); // num:45 anArray[1,2,3,45] ref:&#8217;someVar closure1&#8242;<br />
closure2(-10);// num:990 anArray[1,2,3,990] ref:&#8217;someVar closure2&#8217;<br />
[/javascript]</p>
<h4><strong><span style="color: #008000">闭包的应用</span></strong></h4>
<p><strong>Singleton 单件：</strong></p>
<p>[javascript]<br />
var singleton = function () {<br />
    var privateVariable;<br />
    function privateFunction(x) {<br />
        &#8230;privateVariable&#8230;<br />
    }</p>
<p>    return {<br />
        firstMethod: function (a, b) {<br />
            &#8230;privateVariable&#8230;<br />
        },<br />
        secondMethod: function (c) {<br />
            &#8230;privateFunction()&#8230;<br />
        }<br />
    };<br />
}();<br />
[/javascript]</p>
<p>这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法，方法1可以方法私有变量，方法2访问内部私有函数。需要注意的地方是匿名主函数结束的地方的'()&#8217;，如果没有这个'()&#8217;就不能产生单件。因为匿名函数只能返回了唯一的对象，而且不能被其他地方调用。这个就是利用闭包产生单件的方法。</p>
<h2><strong><span style="color: #008000">参考：</span></strong></h2>
<p><a href="http://blog.morrisjohns.com/javascript_closures_for_dummies.html">JavaScript Closures For Dummies(</a><a href="http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies">镜像</a><a href="http://blog.morrisjohns.com/javascript_closures_for_dummies.html">)</a> 可惜都被墙了。<br />
<a href="http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/">Advance Javascript</a> （Douglas Crockford 大神的视频，一定要看啊）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/6668.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/02/joo_1-150x150.png" alt="再谈javascript面向对象编程 " width="150" height="150" /></a><a href="https://coolshell.cn/articles/6668.html" class="wp_rp_title">再谈javascript面向对象编程 </a></li><li ><a href="https://coolshell.cn/articles/6441.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg" alt="Javascript 面向对象编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6441.html" class="wp_rp_title">Javascript 面向对象编程</a></li><li ><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="对象的消息模型" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_title">对象的消息模型</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/17524.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg" alt="如何读懂并写出装逼的函数式代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17524.html" class="wp_rp_title">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6731.html">理解Javascript的闭包</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6731.html/feed</wfw:commentRss>
			<slash:comments>91</slash:comments>
		
		
			</item>
		<item>
		<title>再谈javascript面向对象编程</title>
		<link>https://coolshell.cn/articles/6668.html</link>
					<comments>https://coolshell.cn/articles/6668.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Mon, 27 Feb 2012 00:25:13 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[ECMAScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[OOP]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6668</guid>

					<description><![CDATA[<p>前言:虽有陈皓《Javascript 面向对象编程》珠玉在前，但是我还是忍不住再画蛇添足的补上一篇文章，主要是因为javascript这门语言魅力。另外这篇文章...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6668.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6668.html">再谈javascript面向对象编程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>前言:</strong>虽有陈皓<a href="https://coolshell.cn/articles/6441.html">《Javascript 面向对象编程》</a>珠玉在前，但是我还是忍不住再画蛇添足的补上一篇文章，主要是因为javascript这门语言魅力。另外这篇文章是一篇入门文章，我也是才开始学习Javascript，有一点心得，才想写一篇这样文章，文章中难免有错误的地方，还请各位不吝吐槽指正</p>
<h4><strong><span style="color: #008000">吐槽Javascript</span></strong></h4>
<p>初次接触Javascript，这门语言的确会让很多正规军感到诸多的不适，这种不适来自于Javascript的语法的简练和不严谨，这种不适也来自Javascript这个悲催的名称，我在想网景公司的Javascript设计者在给他起名称那天一定是脑壳进水了,让Javascript这么多年来受了这么多不白之冤，人们都认为他是Java的附属物，一个WEB玩具语言。因此才会有些人会对Javascript不屑，认为Javascript不是一门真正的语言，但是这此他们真的错了。Javascript不仅是一门语言，是一门真真正正的语言，而且他还是一门里程碑式的语言，他独创多种新的编程模式原型继承，闭包（<strong>作者注：闭包不是JS首创，应该Scheme首创，prototypal inheritance 和 dynamic objects 是self语言首创，Javascript的首创并不精彩,谢谢网友的指正。</strong>），对后来的动态语言产生了巨大的影响。做为当今最流行的语言（没有之一），看看git上提交的最多的语言类型就能明白。随着HTML5的登场，浏览器将在个人电脑上将大显身手，完全有替换OS的趋势的时候，Javascript做为浏览器上的一门唯一真真的语言，如同C之于 unix/linux，java之于JVM，Cobol之于MainFrame，我们也需要来重新的认真地认识和审视这门语言。另外Javascript的正式名称是：ECMAScript，这个名字明显比Javascript帅太多了！<br />
<span id="more-6668"></span><br />
言归正传，我们切入主题——Javascript的面向对象编程。要谈Javascript的面向对象编程，我们第一步要做的事情就是忘记我们所学的面向对象编程。传统C++或Java的面向对象思维来学习Javascript的面向对象会给你带来不少困惑，让我们先忘记我们所学的，从新开始学习这门特殊的面向对象编程。既然是OO编程，要如何来理解OO编程呢，记得以前学C++，学了很久都不入门，后来有幸读了《Inside The C++ Object Model》这本大作，顿时豁然开朗，因此本文也将以对象模型的方式来探讨的Javascript的OO编程。因为Javascript 对象模型的特殊性，所以使得Javascript的继承和传统的继承非常不一样，同时也因为Javascript里面没有类，这意味着Javascript里面没有extends,implements。那么Javascript到底是如何来实现OO编程的呢？好吧，让我们开始吧，一起在Javascript的OO世界里来一次漫游</p>
<p>首先，我们需要先看看Javascript如何定义一个对象。下面是我们的一个对象定义：</p>
<p>[javascript]<br />
var o = {};<br />
[/javascript]</p>
<p>还可以这样定义一个对象</p>
<p>[javascript]<br />
function f() {<br />
}<br />
[/javascript]</p>
<p>对，你们没有看错，在Javascript里面，函数也是对象。<br />
当然还可以</p>
<p>[javascript]<br />
var array1= [ 1,2,3];<br />
[/javascript]</p>
<p>数组也是一个对象。<br />
其他关于对象的基本的概念的描述，还是请各位亲们参见陈皓<a href="https://coolshell.cn/articles/6441.html">《Javascript 面向对象编程》</a>文章。<br />
对象都有了，唯一没有的就是class，因为在Javascript里面是没有class关键字的，算好还有function，function的存在让我们可以变通的定义类，在扩展这个主题前，我们还需要了解一个Javascript对象最重要的属性，<strong>__proto__</strong>成员。</p>
<h4><strong><span style="color: #008000">__proto__成员</span></strong></h4>
<p>严格的说这个成员不应该叫这个名字，__proto__是Firefox中的称呼，__proto__只有在Firefox浏览器中才能被访问到。<strong>做为一个对象，当你访问其中的一个成员或方法的时候，如果这个对象中没有这个方法或成员，那么Javascript引擎将会访问这个对象的__proto__成员所指向的另外的一个对象，并在那个对象中查找指定的方法或成员，如果不能找到，那就会继续通过那个对象的__proto__成员指向的对象进行递归查找，直到这个链表结束</strong>。<br />
好了，让我们举一个例子。<br />
比如上上面定义的数组对象array1。当我们创建出array1这个对象的时候，array1实际在Javascript引擎中的对象模型如下：<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6675" src="https://coolshell.cn/wp-content/uploads/2012/02/joo_1.png" alt="" width="416" height="208" srcset="https://coolshell.cn/wp-content/uploads/2012/02/joo_1.png 416w, https://coolshell.cn/wp-content/uploads/2012/02/joo_1-300x150.png 300w" sizes="(max-width: 416px) 100vw, 416px" /><br />
array1对象具有一个length属性值为3，但是我们可以通过如下的方法来为array1增加元素：</p>
<p>[javascript]<br />
array1.push(4);<br />
[/javascript]</p>
<p>push这个方法来自于array1的__proto__成员指向对象的一个方法(Array.prototye.push())。正是因为所有的数组对象（通过[]来创建的）都包含有一个指向同一个具有push,reverse等方法对象(Array.prototype)的__proto__成员，才使得这些数组对象可以使用push,reverse等方法。</p>
<p>那么这个__proto__这个属性就相当于面向对象中的&#8221;has a&#8221;关系，这样的的话，只要我们有一个模板对象比如Array.prototype这个对象，然后把其他的对象__proto__属性指向这个对象的话就完成了一种继承的模式。不错！我们完全可以这么干。但是别高兴的太早，这个属性只在FireFox中有效，其他的浏览器虽然也有属性，但是不能通过__proto__来访问，只能通过getPrototypeOf方法进行访问，而且这个属性是只读的。看来我们要在Javascript实现继承并不是很容易的事情啊。</p>
<h4><strong><span style="color: #008000">函数对象prototype成员</span></strong></h4>
<p>首先我们先来看一段函数prototype成员的定义，</p>
<blockquote><p><strong>When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object</strong><br />
当一个函数对象被创建时，这个函数对象就具有一个prototype成员，这个成员是一个对象，这个对象包含了一个构造子成员，这个构造子成员会指向这个函数对象。</p></blockquote>
<p>例如：</p>
<p>[javascript]<br />
function Base() {<br />
    this.id = &quot;base&quot;<br />
}<br />
[/javascript]</p>
<p>Base这个函数对象就具有一个prototype成员，关于构造子其实Base函数对象自身，为什么我们将这类函数称为构造子呢？原因是因为这类函数设计来和new 操作符一起使用的。为了和一般的函数对象有所区别，这类函数的首字母一般都大写。构造子的主要作用就是来创建一类相似的对象。</p>
<p>上面这段代码在Javascript引擎的对象模型是这样的<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6678" src="https://coolshell.cn/wp-content/uploads/2012/02/joo_2.png" alt="" width="382" height="190" srcset="https://coolshell.cn/wp-content/uploads/2012/02/joo_2.png 382w, https://coolshell.cn/wp-content/uploads/2012/02/joo_2-300x149.png 300w" sizes="(max-width: 382px) 100vw, 382px" /></p>
<h4><strong><span style="color: #008000">new 操作符</span></strong></h4>
<p>在有上面的基础概念的介绍之后，在加上new操作符，我们就能完成传统面向对象的class + new的方式创建对象，在Javascript中，我们将这类方式成为Pseudoclassical。<br />
基于上面的例子，我们执行如下代码</p>
<p>[javascript]<br />
var obj = new Base();<br />
[/javascript]</p>
<p>这样代码的结果是什么，我们在Javascript引擎中看到的对象模型是：<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6680" src="https://coolshell.cn/wp-content/uploads/2012/02/joo_3.png" alt="" width="403" height="207" srcset="https://coolshell.cn/wp-content/uploads/2012/02/joo_3.png 403w, https://coolshell.cn/wp-content/uploads/2012/02/joo_3-300x154.png 300w" sizes="(max-width: 403px) 100vw, 403px" /></p>
<p>new操作符具体干了什么呢?其实很简单，就干了三件事情。</p>
<p>[javascript]<br />
var obj  = {};<br />
obj.__proto__ = Base.prototype;<br />
Base.call(obj);<br />
[/javascript]</p>
<p>第一行，我们创建了一个空对象obj<br />
第二行，我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象<br />
第三行，我们将Base函数对象的this指针替换成obj，然后再调用Base函数，于是我们就给obj对象赋值了一个id成员变量，这个成员变量的值是&#8221;base&#8221;，关于call函数的用法，请参看陈皓<a href="https://coolshell.cn/articles/6441.html">《Javascript 面向对象编程》</a>文章<br />
如果我们给Base.prototype的对象添加一些函数会有什么效果呢？<br />
例如代码如下：</p>
<p>[javascript]<br />
Base.prototype.toString = function() {<br />
    return this.id;<br />
}<br />
[/javascript]</p>
<p>那么当我们使用new创建一个新对象的时候，根据__proto__的特性，toString这个方法也可以做新对象的方法被访问到。于是我们看到了：<br />
<strong>构造子中，我们来设置‘类’的成员变量（例如：例子中的id），构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符，模拟出类和类实例化的效果。</strong></p>
<h4><strong><span style="color: #008000">Pseudoclassical 继承</span></strong></h4>
<p>我们模拟类，那么继承又该怎么做呢？其实很简单，我们只要将构造子的prototype指向父类即可。例如我们设计一个Derive 类。如下</p>
<p>[javascript]<br />
function Derive(id) {<br />
    this.id = id;<br />
}<br />
Derive.prototype = new Base();<br />
Derive.prototype.test = function(id){<br />
    return this.id === id;<br />
}<br />
var newObj = new Derive(&quot;derive&quot;);<br />
[/javascript]</p>
<p>这段代码执行后的对象模型又是怎么样的呢？根据之前的推导，应该是如下的对象模型<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6686" src="https://coolshell.cn/wp-content/uploads/2012/02/joo_4.png" alt="" width="645" height="216" srcset="https://coolshell.cn/wp-content/uploads/2012/02/joo_4.png 645w, https://coolshell.cn/wp-content/uploads/2012/02/joo_4-300x100.png 300w" sizes="(max-width: 645px) 100vw, 645px" /><br />
这样我们的newObj也继承了基类Base的toString方法，并且具有自身的成员id。关于这个对象模型是如何被推导出来的就留给各位同学了，参照前面的描述，推导这个对象模型应该不难。<br />
Pseudoclassical继承会让学过C++/Java的同学略微的感受到一点舒服，特别是new关键字，看到都特亲切，不过两者虽然相似，但是机理完全不同。当然不关什么样继承都是不能离不开__proto__成员的。</p>
<h4><strong><span style="color: #008000">Prototypal继承</span></strong></h4>
<p>这是Javascript的另外一种继承方式，这个继承也就是之前陈皓文章《Javascript 面向对象编程》中create函数，非常可惜的是这个是ECMAScript V5的标准，支持V5的浏览器目前看来也就是IE9，Chrome最新版本和Firefox。虽然看着多，但是做为IE6的重灾区的中国，我建议各位还是避免使用create函数。好在没有create函数之前，Javascript的使用者已经设计出了等同于这个函数的。例如：我们看看Douglas Crockford的object函数。</p>
<p>[javascript]<br />
function object(old) {<br />
   function F() {};<br />
   F.prototype = old;<br />
   return new F();<br />
}<br />
var newObj = object(oldObject);<br />
[/javascript]</p>
<p>例如如下代码段</p>
<p>[javascript]<br />
var base ={<br />
  id:&quot;base&quot;,<br />
  toString:function(){<br />
          return this.id;<br />
  }<br />
};<br />
var derive = object(base);<br />
[/javascript]</p>
<p>上面函数的执行后的对象模型是：<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6688" src="https://coolshell.cn/wp-content/uploads/2012/02/joo_5.png" alt="" width="451" height="230" srcset="https://coolshell.cn/wp-content/uploads/2012/02/joo_5.png 451w, https://coolshell.cn/wp-content/uploads/2012/02/joo_5-300x152.png 300w" sizes="(max-width: 451px) 100vw, 451px" /><br />
如何形成这样的对象模型，原理也很简单，只要把object这个函数扩展一下，就能画出这个模型，怎么画留给读者自己去画吧。<br />
这样的继承方式被称为原型继承。相对来说要比Pseudoclassical继承来的简单方便。ECMAScript V5正是因为这原因也才增加create函数，让开发者可以快速的实现原型继承。<br />
上述两种继承方式是Javascript中最常用的继承方式。通过本文的讲解，你应该对Javascript的OO编程有了一些‘原理’级的了解了吧</p>
<h4><strong><span style="color: #008000">参考:</span></strong></h4>
<p><a href="http://msdn.microsoft.com/en-us/scriptjunkie/ff852808">《Prototypes and Inheritance in JavaScript Prototypes and Inheritance in JavaScript》</a><br />
<a href="http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/" target="_blank">Advance Javascript</a> （Douglas Crockford 大神的视频，一定要看啊）</p>
<h4><strong><span style="color: #008000">题外话：</span></strong></h4>
<p>web2.0后，web应用可谓飞速发展，如今在HTML5发布之际，浏览器的功能被大大强化，我感觉Browser远远在不是一个Browser那么简单了。记得C++之父曾经这样说过JAVA，JAVA不是跨平台，JAVA本身就是一个平台。如今的Browser也本身就是一个平台了，好在这个平台是基于标准的。如果Browser是平台，由于Browser安全沙箱的限制，个人电脑的资源被使用的很少，感觉Browser就是一个NC（Network Computer）？我们居然又回到了Sun最初提出的构想，Sun是不是太强大了些？<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/6731.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/closure-150x150.png" alt="理解Javascript的闭包" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6731.html" class="wp_rp_title">理解Javascript的闭包</a></li><li ><a href="https://coolshell.cn/articles/6441.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg" alt="Javascript 面向对象编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6441.html" class="wp_rp_title">Javascript 面向对象编程</a></li><li ><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="对象的消息模型" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_title">对象的消息模型</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/17524.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg" alt="如何读懂并写出装逼的函数式代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17524.html" class="wp_rp_title">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6668.html">再谈javascript面向对象编程</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6668.html/feed</wfw:commentRss>
			<slash:comments>77</slash:comments>
		
		
			</item>
		<item>
		<title>Eclipse开发Android应用程序入门:重装上阵</title>
		<link>https://coolshell.cn/articles/4334.html</link>
					<comments>https://coolshell.cn/articles/4334.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Fri, 08 Apr 2011 00:30:09 +0000</pubDate>
				<category><![CDATA[Java语言]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Eclipse]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=4334</guid>

					<description><![CDATA[<p>翻译:赵锟 原文：http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-a...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/4334.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/4334.html">Eclipse开发Android应用程序入门:重装上阵</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>翻译:赵锟</strong><br />
原文：<a href="http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/">http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/</a></p>
<p>在我们教程系列的<a href="https://coolshell.cn/articles/4270.html">第一部分</a>中，我们使用Android和Eclipse开发了一个简单的饮茶计时器的应用程序。在第二部分，我们将继续开发这个程序，并给它增加一些其他的额外的功能。在开发的过程中，我们将给你介绍更多重要而强大的Android SDK特性，包括持久化数据存储，Activity和Intent，和共享用户首选项（译者注：类似于windows 的注册表的一种机制）。</p>
<p>跟着本教程，你需要上一篇教程中的代码，如果你想直接使用代码，你可以使用如下的指令从<a href="http://github.com/cblunt/BrewClock">GitHub</a>上check out出tutorial_par_1标记的代码：</p>
<p><img decoding="async" loading="lazy" width="793" height="564" src="https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full.jpg" alt="" title="1_starting_point_full"  class="aligncenter size-full wp-image-4362" srcset="https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full.jpg 793w, https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-300x213.jpg 300w, https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-768x546.jpg 768w, https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-380x270.jpg 380w" sizes="(max-width: 793px) 100vw, 793px" /><br />
[code]<br />
$ git clone git://github.com/cblunt/BrewClock.git<br />
$ cd BrewClock<br />
$ git checkout tutorial_part_1<br />
[/code]</p>
<p>在GitHub中检出了代码后，你需要将代码倒入到Eclipse中的项目中：</p>
<ol>
<li>运行      Eclipse 选择 <em>File → Import…</em></li>
<li>在导入窗口, 选择 <em>“Existing Projects into Workspace”</em>并点击<em> “Next.”</em></li>
<li>在下一屏，点击 <em>“Browse,”</em>选择你从GitHub上clone出的代码目录。</li>
<li>点击“Finish” 将项目导入到Eclipse中。</li>
</ol>
<p><span id="more-4334"></span><br />
在导入项目到Eclipse之后，你有可能会看到有如下的警告信息：<br />
[code]<br />
Android required .class compatibility set to 5.0.<br />
Please fix project properties.<br />
[/code]<br />
如果有这种情况，右键点击“Project Explorer ”中新导入的BrewClock项目，并选择 “Fix Project Properties,” 并重启Eclipse。</p>
<h3>数据持久化入门</h3>
<p>当前,BrewClock 让用户为他们泡的茶设置一个定时器。这个非常棒的一个工作，但是如果对于不同的茶使用同一个泡茶时间的结果会怎样呢，是不每种茶都应该有自己的一个泡茶时间呢？如果这样，那岂不是所有的用户都需要记下每一类茶所需要泡的时间！这不是一个很好的用户体验。因此，在这篇教程中，我将新增一个功能来为用户每种不同的茶叶存放一个泡茶时间，并当用户想泡茶的时候，可以从茶叶列表中进行选择。</p>
<p>为了实现这个目的，我们得利用Android的丰富的数据持久化的API。Android提供了几种方式来存储数据，本文将要覆盖其中的两种方式。第一种，使用SQLite数据库引擎来为我们存储数据。</p>
<p>SQLite 是一种流行的轻量级SQL数据库引擎，它将数据存在单个文件中。SQLite经常用于桌面或在那些运行不能运行客户端-服务器SQL引擎（例如MySQL或PostgreSQL)的嵌入式的应用上。</p>
<p>每个安装在Android上的应用都可以保存和使用多个SQLite数据库文件（由数据存储容量决定），这些数据由系统自动地进行管理。应用程序的数据是私有并且不能被其他的应用程序所访问。（数据可以通过ContentProvider(译者注：内容提供者类)类进行共享，但是我们不会在本教程中覆盖关于内容提供者的内容）。当数据应用程序被更新时，数据库文件就进行持久化，当应用程序被删除时，数据库文家就被删除。</p>
<p>我们在BrewClock应用使用SQLite数据来维护我们的茶叶列表和泡茶所需要的时间。下面是我们我们将使用的数据表的一个总体介绍。</p>
<p>[code]<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| Table: teas                         |<br />
+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| Column     | Description            |<br />
+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| _ID        | integer, autoincrement |<br />
| name       | text, not null         |<br />
| brew_time  | integer, not null      |<br />
+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
[/code]</p>
<p>如果以前你使用过SQL，你应该熟悉这些内容。数据表有三个字段，一个唯一标示（_ID），茶叶名称(name)和泡茶时间(brew_time)字段。我们将使用Android提供给我们的API在应用中建立数据表。系统将负责在正确的位置为我们的创建数据库文件。</p>
<h4>抽象数据库</h4>
<p>为了确保数据库的代码容易被维护，我们用一个单独的类TeaData来抽象所有处理数据库创建，插入，和查询的代码。如果你熟悉模型-试图-控制(译者注：MVC)方法的话，这个你也应该熟悉。所有数据库代码与我们的BrewClockActitvity类隔离开来。Actitvity可以初始化一个新的TeaData实例（这个实例将连接数据库）并完成它所需要的工作。以这种方式工作保证了我们可以方便的更改我们所使用的数据库而不用修改其他那些和数据库不相关部分的代码。</p>
<p>通过菜单File → New → Class.在BrewClock项目中创建一个TeaData的新类。确保TeaData扩展于android.database.sqlite.SQLiteOpenHelper 类，并选中“Constructors from superclass”复选框。<br />
<img decoding="async" src="https://coolshell.cn/wp-content/uploads/2011/04/2_create_teadata_class1.jpg" alt="" title="1_starting_point_full"  class="aligncenter size-full wp-image-4362" /></p>
<p>TeaData 类将为你自动地处理SQLite数据库的创建和版本。我们需要增加一些方法来作为其他代码到数据库的接口。</p>
<p>增加两个常量来存储数据库的名字和版本,增加表名和表中列名。我们使用Android提供的常类BaseColumns._ID来做为表的唯一id列：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/TeaData.java
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.provider.BaseColumns;

public class TeaData extends SQLiteOpenHelper {
  private static final String DATABASE_NAME = &quot;teas.db&quot;;
  private static final int DATABASE_VERSION = 1;

  public static final String TABLE_NAME = &quot;teas&quot;;

  public static final String _ID = BaseColumns._ID;
  public static final String NAME = &quot;name&quot;;
  public static final String BREW_TIME = &quot;brew_time&quot;;

  // …
}
</pre>
<p>为TeaData增加一个构造方法，以数据库名称合版本号为参数调用其父类的构造方法。Android将会自动地打开数据库（如果数据库不存在就自动创建它）。</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/TeaData.java
public TeaData(Context context) {
  super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
</pre>
<p>我们需要重载onCreate方法，并执行一个SQL 串执行创建数据库表的操作。Android将会在数据库文件第一次被创建时调用这个方法。</p>
<p>在启动过程中，Android检查数据库的版本是否我们传入的版本一致。如果版本发生了改变，Android将会调用onUpgrade方法，在这个方法总，你可以编写修改数据库结构的业务逻辑。在本教程中，我们将让Android删除数据库并重建数据库。</p>
<p>在onCreate和onUpgrade中增加如下的代码:</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/TeaData.java
@Override
public void onCreate(SQLiteDatabase db) {
  // CREATE TABLE teas (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, brew_time INTEGER);
  String sql =
    &quot;CREATE TABLE &quot; + TABLE_NAME + &quot; (&quot;
      + _ID + &quot; INTEGER PRIMARY KEY AUTOINCREMENT, &quot;
      + NAME + &quot; TEXT NOT NULL, &quot;
      + BREW_TIME + &quot; INTEGER&quot;
      + &quot;);&quot;;

  db.execSQL(sql);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  db.execSQL(&quot;DROP TABLE IF EXISTS &quot; + TABLE_NAME);
  onCreate(db);
}

</pre>
<p>下一步，我们需要新增代码让我们方便地在数据库中新增茶叶记录。我们新增一个带茶叶名称和泡茶时间的方法来负责插入记录。Android为了尽量避免开发者使用SQL语句，提供了一堆类来处理向数据库中查入记录。首先，我们创建一个ContentValues集合，并将相关的值插入到这个集合中去。</p>
<p>对于ContentValues集合，我们只要简单地提供一个列名和值来插入就行了。Android负责创建和运行正确的SQL。使用Android的数据类确保了你能写出安全，跨平台的数据库操作代码。</p>
<p>Add a new method, insert(), to the TeaData class:</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/TeaData.java
public void insert(String name, int brewTime) {
  SQLiteDatabase db = getWritableDatabase();

  ContentValues values = new ContentValues();
  values.put(NAME, name);
  values.put(BREW_TIME, brewTime);

  db.insertOrThrow(TABLE_NAME, null, values);
}

</pre>
<h4>查询数据</h4>
<p>我们应用程序具有了在数据库中保存数据的能力后，我们同样也需要一种方式将数据取回来。Android提供了游标Cursor接口来完成这件工作。一个游标代表了针对数据库运行一个SQL返回的结果集，游标在这个结果集中维护了一个指针来指向结果集中的一行。这个指针可以向前，向后移动，并返回每一列的值，下面我们用图形来帮助你理解游标:</p>
<p>SQL 查询: SELECT * from teas LIMIT 3;<br />
[code]<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+</p>
<p>|  _ID  |  name       |  brew_time  |</p>
<p>+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+</p>
<p>|    1  |  Earl Grey  |          3  |</p>
<p>|    2  |  Green      |          1  | &lt;= Cursor</p>
<p>|    3  |  Assam      |          5  |</p>
<p>+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;-+<br />
[/code]</p>
<p>在这个例子中，游标指向了结果集中的第二条记录（绿茶）。我们可以通过调用cursor.moveToPrevious()方法，将游标向前移动，让它指向第一行（Earl Grey），或者调用moveToNext向前移动指向Assam。要取到游标所指向记录的茶叶的名称，我们只要调用cursor.getString(1)，1代表我们向提取数据列的下标（注意下标识从0开始的，1代表第二列，依次类推）。</p>
<p>在了解游标后，我们增加一个创建游标对象并返回数据库中所有的茶叶信息。在TeaData中增加all方法：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/TeaData.java
public Cursor all(Activity activity) {
  String[] from = { _ID, NAME, BREW_TIME };
  String order = NAME;

  SQLiteDatabase db = getReadableDatabase();
  Cursor cursor = db.query(TABLE_NAME, from, null, null, null, null, order);
  activity.startManagingCursor(cursor);

  return cursor;
}
</pre>
<p>因为这个方法乍一看有点古怪，所以让我们先来关心一下这个方法的一些细节。我们没有使用SQL的查询语句，而是使用了Android提供的数据库接口方法。</p>
<p>第一，我们需要告诉Android，我们所关心的列的信息。我们创建了一个字符串数组，数组中存放这TeaData中列的标示信息。我们还设置了我名们期望的结果集按照哪一个列进行排序的列名。</p>
<p>第二，我们使用getReadalbeDatabase()创建了一个到数据库的只读连接，并调用query方法告诉Android我们希望用query方法运行一个查询。query()方法有很多的参数，Android在内部将这些参数转化为一个查询语句。此外，Android的抽象层保证了即使底层数据储存机制发生了变化，我们的应用程序代码也能正确的工作。</p>
<p>由于我们只要返回表中的所有记录，所以我们没有在方法中使用到链接join，过滤filter和分组group（例如：在SQL中的WHERE，JOIN，和GROUP BY）。from和order变量告诉查询数据库需要返回那些列和提取数据时按什么列进行排序。我们使用SQLiteDatabase.query()作为和数据库的人机交互接口。</p>
<p>最后，我们让Activity（在本例中，我们的BrewClockActivity）来管理游标。通常，游标需要人工刷新内容，因此当我们增加一个新茶信息到数据库中时，我们就需要刷新我们的游标。每当我们的应用被挂起和恢复的时候，通过调用startManagingCursor()让Android来帮我们重建结果集。</p>
<p>在TeaData类中增加count方法:</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/TeaData.java
  public long count() {
    SQLiteDatabase db = getReadableDatabase();
    return DatabaseUtils.queryNumEntries(db, TABLE_NAME);
  }

</pre>
<p>保存TeaData类，使用修正没有import 的类(Source → Organize Imports)，在完成我们的数据类后，下一步我们将着手修改我们BrewClock的人机界面。</p>
<h4>修改BrewClock用户界面，允许进行茶叶选择</h4>
<p>持久化茶和泡茶的时间的目的是让用能快速的选择他们所钟爱的预设置的茶。为了完成这个功能，我们需要再BrewClock的主界面上增加一个Spinner（类似于桌面上弹出菜单），生成一个来自于TeaData的茶列表。</p>
<p>和前面的教程一样，我们使用了Eclipse的布局器编辑器在BrewClock的主界面布局XML文件中增加Spinner。在LinearLayout元素下面增加下面这些代码（大约在24行）。如果你打开了可视化的布局编辑器后，你可以点击窗口下面的地&#8221;Code View&#8221;进行切换。</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;!-- /res/layout/main.xml --&gt;

&lt;!-- Tea Selection --&gt;
&lt;LinearLayout
  android:orientation=&quot;vertical&quot;
  android:layout_width=&quot;fill_parent&quot;
  android:layout_height=&quot;wrap_content&quot;&gt;

  &lt;Spinner
    android:id=&quot;@+id/tea_spinner&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;wrap_content&quot; /&gt;

&lt;/LinearLayout&gt;
</pre>
<p>在BrewClockActivity类里面,增加一个成员变量指向Spinner，通过使用findViewById连接界面上的控件：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/BrewClockActivity.java
protected Spinner teaSpinner;
protected TeaData teaData;

// …

public void onCreate(Bundle savedInstanceState) {
  // …
  teaData = new TeaData(this);
  teaSpinner = (Spinner) findViewById(R.id.tea_spinner);
}
</pre>
<p>运行你的程序以确保新的界面正确地生效。你应该在泡茶计数器下看见一个空白的弹出式菜单（或者是Spinner)。如果点击spinner，Android将显示一个弹出式的菜单并为你提供选择列表。在这时，菜单的内容因该是空的，现在让我们来绑定Spinner和我们的茶叶数据库。</p>
<p><img decoding="async" loading="lazy" width="500" height="356" src="https://coolshell.cn/wp-content/uploads/2011/04/3_blank_spinner.jpg" alt="" title="3_blank_spinner"  class="aligncenter size-full wp-image-4364" srcset="https://coolshell.cn/wp-content/uploads/2011/04/3_blank_spinner.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/04/3_blank_spinner-300x213.jpg 300w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<h4>数据绑定</h4>
<p>当Android从数据库中查询数据时，它将会返回一个游标Cursor对象。Cursor代表了来自数据库的结果集，并可以移动游标来提取结果中的数据。使用一类Android提供的称为“适配器Adapter”的类，我们很容易将这个结果集绑定到Spinner上。适配器完成了提取数据库结果集中的数据并在界面上显示这些数据等这些复杂而困难工作。</p>
<p>在我们的TeaData.all()方法中已经可以返回一个带有tea表内容的游标，使用这个游标，我们所需要做的工作就是创建一个SimpleCursor适配器来绑定我们的teaSpinner，Android会负责处理将数据显示在spinner的列表中。</p>
<p>通过创建一个SimpleCursorAdapter类来连接Spinner与teaData.all()返回的游标：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// com/example/brewclock/BrewClockActivity.java

public void onCreate(Bundle savedInstanceState) {
  // …
  Cursor cursor = teaData.all(this);

  SimpleCursorAdapter teaCursorAdapter = new SimpleCursorAdapter(
    this,
    android.R.layout.simple_spinner_item,
    cursor,
    new String[] { TeaData.NAME },
    new int[] { android.R.id.text1 }
  );

  teaSpinner.setAdapter(teaCursorAdapter);
  teaCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}

</pre>
<p>注意，我们使用了Android内建的android.R对象。这个对象提供了你的应用程序中的默认资源，例如视图和布局。在我们的代码中，我们使用了android.R.layout.simple_spinner_item，它是简单的文本标签布局。</p>
<p>如果你再次运行的应用程序，你将会看到spinner中仍然是空的！虽然我们已经连接了我们的数据库，但是由于数据库中没有任何记录，所以我们任何看到了空列表。</p>
<p>我们通过在构造方法中增加一些默认记录来让用户可以选择所需要的茶叶，为了避免重复记录，我们只有在数据库中记录为0的情况才增加默认记录。在本教程的代码中，我们使用前面增加的count()来检查数据库中表记录是否为空。</p>
<p>增加当数据库中表为空的默认记录代码。把这些代码增加从数据库提取茶叶数据的前面（译者注：上一段的代码前）。</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// com/example/brewclock/BrewClockActivity.java
public void onCreate(Bundle savedInstanceState) {
  // …

  // Add some default tea data! (Adjust to your preference :)
  if(teaData.count() == 0) {
    teaData.insert(&quot;Earl Grey&quot;, 3);
    teaData.insert(&quot;Assam&quot;, 3);
    teaData.insert(&quot;Jasmine Green&quot;, 1);
    teaData.insert(&quot;Darjeeling&quot;, 2);
  }

  // Code from the previous step:
  Cursor cursor = teaData.all(this);

  // …
}

</pre>
<p>现在再次运行你的应用程序。你将会发现茶叶Spinner有了一条选择。点击Spinner让你可以从数据库选择你要的茶叶。</p>
<p><img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2011/04/4_populated_spinner.jpg" alt="" title="4_populated_spinner" width="500" height="356" class="aligncenter size-full wp-image-4365" srcset="https://coolshell.cn/wp-content/uploads/2011/04/4_populated_spinner.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/04/4_populated_spinner-300x213.jpg 300w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p>恭喜你！你已经成功关联了你的界面和代码。这是任何软件开发过程中一个非常重要的方面。正如你所看见的，Android将这一步简化的非常容易，但是功能有是非常的NB。使用游标和适配器，你可以将数据源（丛简单的字符串数组到复杂的数据库查询）绑定到任何类型的视图：spinner或列表，设置是类似iTunes cover-flow gallery!</p>
<p>虽然现在已经可以开始泡茶了，但是我们工作还远没有结束。当你从Spinner选择了不同的茶，这个选择却不会发生任何作用。我们需要根据用户所选茶叶的种类取更新我们的泡茶时间。</p>
<h4>读取选中茶叶数据并更新泡茶时间</h4>
<p>为了能读取用户从数据库中选择茶叶的数据，我们必须增加一个针对此事件的监听器。类似于处理按钮点击事件的OnClickListener监听器一样，我们将实现一个OnItemSelectedListener。当用户从视图中做出一个选择的事件将触发这个监听器，例如从我们的Spinner。</p>
<p>在BrewClockActivity中增加需要实现的接口OnItemSelectedListener。并增加其响应的处理方法onItemSelected()和onNothingSelected()：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/BrewClockActivity.java
public class BrewClockActivity extends Activity implements OnClickListener, OnItemSelectedListener {
  // …
  public void onItemSelected(AdapterView&lt;?&gt; spinner, View view, int position, long id) {
    if(spinner == teaSpinner) {
      // Update the brew time with the selected tea’s brewtime
      Cursor cursor = (Cursor) spinner.getSelectedItem();
      setBrewTime(cursor.getInt(2));
    }
  }

  public void onNothingSelected(AdapterView&lt;?&gt; adapterView) {
    // Do nothing
  }
}

</pre>
<p>在这里我们要检查是触发的spinner此事件是不是BrewClock的teaSpinner。如果是，我们将提取代表选中记录的游标对象。这些都是由关联teaData和Spinner的SimpleCursorAdapter来提供我们完成的。Android知道哪个查询产生的Spinner数据，也知道用户选择的哪个数据。Android使用游标来返回数据库的一行记录，也代表了用户所选择的茶叶数据。</p>
<p>Cursor的getInt()方法带了一个我们想提取的列的下标为参数。在我们的teaData.all()方法中创建游标的时候，我们读取的列是_ID,NAME和BREW_TIME。假设我们在teaSpinner中选择的是Jasmine Tea，那么将返回我们所选数据所对应的数据库记录。</p>
<p>然后我们再通过传递参数2来选择此记录的第二列的整型值。这个值提供给setBrewTime()方法。这个方法用于更新界面上的泡茶时间。</p>
<p>最后，我们需要告诉teaSpinner BrewClockActivity正在监听OnItemSelected事件。在BrewClockActivity的onCreate方法中增加下面的代码：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/BrewClockActivity.java
public void onCreate() {
  // …
  teaSpinner.setOnItemSelectedListener(this);
}
</pre>
<p>大功告成！再次运行你的程序，并从Spinner选择不同的茶叶。每次你所选的茶叶它所对应的泡茶时间都回显示对应的界面上。我们余下的代码中已经可以处理从当前时间开始递减计数。所以在有预先设置的茶叶种类下，我们已经可以完成我们所想要的功能。</p>
<p>你当然可以，回到之前的代码中去增加一些茶叶种类你满足你的口味。但是如果你发布BrewClock程序到Android Market，每当有人向增加新的茶叶数据到数据库中，我就需要去手动的取更新数据中的内容并重新发布它；这样所有的人就必须去更新它，并且所有的人都有一个同样的列表。这听起来非常的不灵活，因此我们还有很多的工作需要完成！</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/5_default_teas.jpg" alt="" title="3_blank_spinner"  class="aligncenter size-full wp-image-4364"/></p>
<p>如果用户自己有方法新增茶叶种类到数据库里面，将会非常的不错的做法。因此我们将在下一章继续。。。</p>
<h3>Activity 介绍</h3>
<p>和你应用程序中每个屏幕关联的代码就是Activity。每次当你从一屏切换到另外一屏，Android就会创建一个新的Activity。在真实世界中，虽然一个应用程序经常由多个屏幕/Activity构成，Andriod却将每个屏幕看作独立的个体。多个Activity工作在一起形成一种关联的体验，这是因为Android让你非常容易地在屏幕/Activity之间传递数据。</p>
<p>在本节最后，你将为你的应用程序新增一个新的Activity（AddTeaActivity）并将它注册到Android系统中。你还需要从最初的BrewClockActivity传递数据到新的Activity中。</p>
<p>首先，我们需要给用户一种方式切换到新的Activity上。我们将使用选项菜单来完成之一步。</p>
<h4>选项菜单</h4>
<p>当用户他们的设备上的“Menu”按键时，选项菜单以弹出菜单的形式出现。Android负责菜单的自动创建和显示；你只需要告诉Android，菜单显示什么内容和当用户点击菜单时该做什么就行。</p>
<p>然而,最好不要在代码中硬编码菜单的标题，我们可以使用Android的字符串资源。字符串资源是一个独立的文件，在这个文件中你可以维护所有用于用户阅读的字符串和标签资源，并可以在代码调用它们。这就意味着当你在未来需要修改字符串时，你只要修改这一处地方即可。.</p>
<p>在project explorer中导航到“res/values”下，你将会看到string.xml文件已经存在。这个是你再创建新项目的时候由Eclipse创建的，这文件存放着在整个应用程序我们将要使用的字符串。</p>
<p>双击打开<em>strings.xml</em> ,通过窗口底部的选项页切换到XML 视图。</p>
<p>在&lt;resources&gt;…&lt;/resources&gt; 元素中增加下面的内容:</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">

&lt;!-- res/values/strings.xml --&gt;
  &lt;resources&gt;
    &lt;!-- … --&gt;
    &lt;string name=&quot;add_tea_label&quot;&gt;Add Tea&lt;/string&gt;
  &lt;/resources&gt;


</pre>
<p>我们在这里定义了一个字符串，add_tea_label和它关联的文本，我们可以在整个程序代码中通过add_tea_label来使用其关联的文本。如果标签因为某个原因需要修改，我们只需要在这个文件修改这一个地方就能完成整个程序的修改。</p>
<p>下一步，让我们创建一个新文件完成选项菜单的定义，如果字符串和布局一样，菜单也使用XML来定义。因此我们将在Eclipse中川建一个新的XML文件：</p>
<p>通过选择File → New → Other, 并选择“Android XML File.”在Eclipse中创建一个新的XML文件。</p>
<p>选择资源的类型为 “Menu”，保存文件名为main.xml。Eclipse将为你自动的创建一个目录<em>res/menu</em>, 来存放你的菜单文件。</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/7_new_menu_xml.jpg"></img></p>
<p>打开<em>res/menus/main.xml</em> 文件, 通过窗口底部的“main.xml”选项页来切换到XML视图。</p>
<p>增加菜单项， add_tea。</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;!-- res/values/strings.xml --&gt;
  &lt;resources&gt;
    &lt;!-- … --&gt;
    &lt;string name=&quot;add_tea_label&quot;&gt;Add Tea&lt;/string&gt;
  &lt;/resources&gt;
</pre>
<p>注意android:title 属性被设置为@string/add_tea_label。这告诉Android在我们的strings.xml文件中查找add_tea_label并返回相关联的标签内容。在本列中我们的菜单项的标签时“Add Tea”。</p>
<p>下一步，我们将告诉我们的Activity，当用户点击设备上的“memu”按键时来显示这个选项菜单。</p>
<p>返回<em>BrewClockActivity.java</em>代码, 重载onCreateOptionsMenu 方法,这个方法告诉Android 当用户点击“Menu”按键时，装载我们的菜单：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/BrewClockActivity.java
@Override
public boolean onCreateOptionsMenu(Menu menu) {
  MenuInflater inflater = getMenuInflater();
  inflater.inflate(R.menu.main, menu);

  return true;
}

</pre>
<p>当用户点击他设备上的“Menu”按键时，Android将调用onCreateOptionsMenu。在这个方法中，我们创建了一个MenuInflater, 这个对象将从你的应用程序包中装载你的菜单资源。就如同按钮和文本域组成你的应用程序布局一样，main.xml资源也是通过全局对象R来生效的，因此我们将此对象提交给MenuInflater对象。</p>
<p>为了测试菜单，保存并在模拟器中并运行应用程序。当程序运行起来使，点击“Menu”按键，你将会看到一个弹出式的菜单显示了一个“Add Tea”选项。</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/8_add_teas_options_menu.jpg"></img></p>
<p>如果你点击“Add Tea”选项，Android自动地检测到点击并关闭菜单。在后台，Android将会提醒应用程序选项已经被点击。</p>
<h4>处理菜单点击</h4>
<p>当用户点击 “Add Tea” 菜单选项，我们想要显示一个新的Activity以便我们能进入增加新茶叶种类的界面。通过选择File → New → Class来创建一个的Activiy。</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/9_new_activity_settings.jpg"></img></p>
<p>将新类命名为 AddTeaActivity,并确保它继承于android.app.Activity类。这个类也放在com.example.brewclock包中:</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
package com.example.brewclock;

import android.app.Activity;
import android.os.Bundle;

public class AddTeaActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }
}

</pre>
<p>上面样例中的空白Activity将不会完成任何工作。但是通过它，我们已经可以完成选项菜单的功能。</p>
<p>在BrewClockActivity增加一个重载方法onOptionsItemSelected 。当用户点击菜单项时，这个方法被Android调用。 (注意点击的MenuItem为它的接收参数：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/BrewClockActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
  switch(item.getItemId()) {
    case R.id.add_tea:
      Intent intent = new Intent(this, AddTeaActivity.class);
      startActivity(intent);
      return true;

    default:
      return super.onOptionsItemSelected(item);
  }
}


</pre>
<p>通过上面的代码，我们告诉Android，当“Add Tea”被点击的时候，我们将要创建一个的Activity；在本教程中，就是AddTeaActivity。然而，不要直接创建这个类的实例，注意我们使用了Intent。Intent有着Android框架的强大特性；他们将Activity绑定在一起来组成应用程序，并允许在他们之间相互传递数据。</p>
<p>Intent的优点甚至让你的应用程序可以使用用户安装的其他的应用程序。例如，当用户要从图库里面显示一张图片，Android自动地给显一个对话框来让用户选择应用程序来显示图片。任何注册为可以处理图片显示的应用程序都会出现在这个对话框的列表中。</p>
<p>Intent功能强大而复杂的主体, 因此它值得你从官方的文档<a href="http://developer.android.com/guide/topics/intents/intents-filters.html">official Android SDK documentation</a>中仔细研究。</p>
<p>让我们运行我们的应用程序，以测试我们的“Add Tea”屏幕。</p>
<p>运行你的项目，按下Menu按键，并点击 “Add Tea.”。</p>
<p>不如你预期的，你并没有看到 “Add Tea” Activity，出现在你面前的是一个Android开发者经常看到的对话框：</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/10_crash.jpg"></img></p>
<p>虽然我们创建了一个Intent并告诉Android启动我们的AddTeaActivity Activity, 由于我们没有将这个Activity注册到Android系统中，我们的应用程序最终还是crash掉了。系统不知道从哪里去找到我们试图运行的Activity（应该还记得Intent可以启动安装在设备上的任何Activity吧）。让我们在应用程序的mainfest文件来注册这些Acitivity。</p>
<p>打开应用的manifest文件，在Eclipse中的AndroidManifest.xml。通过窗口底部的“AndroidManifest.xml”选项页切换到xml视图</p>
<p>应用程序的mainfest文件是保存你应用程序全局设置和信息的地方。你将会看见里面已经有一个.BrewClockActivity 的Activity声明，并且这个Activity在程序运行的时候启动。</p>
<p>在&lt;application&gt;中, 增加一个 &lt;activity&gt; 节点，描述为“Add Tea”的 Activity. 使用我们早先在strings.xml声明的 add_tea_label字符串作为这个Activity的标题：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;!-- AndroidManifest.xml --&gt;
&lt;application …&gt;
  …
  &lt;activity android:name=&quot;.AddTeaActivity&quot; android:label=&quot;@string/add_tea_label&quot; /&gt;
&lt;/application&gt;
</pre>
<p>在你再次运行BrewClock保存这个manifest文件。这一次，当你打开菜单并点击“Add Tea,”时Android将会启动AddTeaActivity。按下back按键返回主屏幕。</p>
<p>完成了Activity的关联，下一步我们将要开发一个增加新茶的界面！</p>
<h3>开发茶叶编辑器界面</h3>
<p>开发一个增加茶叶界面和上一个教程中开发的BrewClock主界面是非常相似的。首先要创建一个布局文件，然后在按照下面的讲解添加适合的XML内容。</p>
<p>和主界面开发所有不同的是，你可以使用Android最近改进的Eclipse布局编辑器来开界面。创建一个新的XML文件来定义你的布局。从菜单File → New然后选择 “Android XML File,” 选择 “Layout”类型。并将文件命令为<em>add_tea.xml</em>。</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/11_new_layout_xml.jpg"></img></p>
<p>用下面的布局内容替换<em>add_tea.xml</em> 文件的内容：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;!-- res/layouts/add_tea.xml --&gt;
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout
  xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
  android:layout_width=&quot;fill_parent&quot;
  android:layout_height=&quot;fill_parent&quot;
  android:orientation=&quot;vertical&quot;
  android:padding=&quot;10dip&quot;&gt;

  &lt;TextView
    android:text=&quot;@string/tea_name_label&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;wrap_content&quot; /&gt;

  &lt;EditText
    android:id=&quot;@+id/tea_name&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;wrap_content&quot;/&gt;

  &lt;TextView
    android:text=&quot;@string/brew_time_label&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;/&gt;

  &lt;SeekBar
    android:id=&quot;@+id/brew_time_seekbar&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;wrap_content&quot;
    android:progress=&quot;2&quot;
    android:max=&quot;9&quot; /&gt;

  &lt;TextView
    android:id=&quot;@+id/brew_time_value&quot;
    android:text=&quot;3 m&quot;
    android:textSize=&quot;20dip&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;wrap_content&quot;
    android:gravity=&quot;center_horizontal&quot; /&gt;
&lt;/LinearLayout&gt;

</pre>
<p>为了这个界面上使用的字符串，我们同样也需要在<em>strings.xml</em> 中增加一些新的内容：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;!-- res/values/strings.xml --&gt;
&lt;resources&gt;
  &lt;!-- … --&gt;
  &lt;string name=&quot;tea_name_label&quot;&gt;Tea Name&lt;/string&gt;

  &lt;string name=&quot;brew_time_label&quot;&gt;Brew Time&lt;/string&gt;
&lt;/resources&gt;

</pre>
<p>在这个布局中，我们加了一个新的界面控件类型，SeekBar。这个控件可以让用户通过从左向右拖拉一个指示器thumb，非常容易的指定泡茶时间。这个值得范围从0到android:max。</p>
<p>在这个界面中，我们使用刻度是0到9，意思是从1分钟到10分钟（泡0分钟茶等于是浪费好茶）。第一，我们需要确保AddTeaActivity能正确地加载我们的界面:</p>
<p>在Activity的onCreate()方法中增加下面的代码用于加载和显示add_tea布局文件：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.add_tea);
}

</pre>
<p>现在通过运行项目来测试你的应用程序，按下“Menu”按键，并点击“Add Tea”菜单。</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/12_add_tea_interface.jpg"/></p>
<p>你将从“Add Tea”屏幕上看到你的新界面。你可以在文本域中输入文字和从左到右拖动SeekBar。但是由于我们没有增加相关代码，这个界面并没有实现什么具体的功能。</p>
<p>在AddTeaActivity中增加下面这些属性，并关联到我们界面上元素：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
public class AddTeaActivity {
  // …

  /** Properties **/
  protected EditText teaName;
  protected SeekBar brewTimeSeekBar;
  protected TextView brewTimeLabel;

  // …

</pre>
<p>下一步,关联属性和你的界面：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
public void onCreate(Bundle savedInstanceState) {
  // …
  // Connect interface elements to properties
  teaName = (EditText) findViewById(R.id.tea_name);
  brewTimeSeekBar = (SeekBar) findViewById(R.id.brew_time_seekbar);
  brewTimeLabel = (TextView) findViewById(R.id.brew_time_value);
}

</pre>
<p>界面非常的简单，我们只要增加相应SeekBar 改变事件的监听器。当用户从左到右移动SeekBar指示器时，我们的应用程序需要读出新值并更新SeekBar之下泡茶时间标签的内容。我们将使用一个监听器来检测SeekBar何时改变的：</p>
<p>在AddTeaActivity类声明中增加实现 onSeekBarChangedListener接口，并添加所必要的方法：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
public class AddTeaActivity
extends Activity
implements OnSeekBarChangeListener {
  // …

  public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    // TODO Detect change in progress
  }

  public void onStartTrackingTouch(SeekBar seekBar) {}

  public void onStopTrackingTouch(SeekBar seekBar) {}
}

</pre>
<p>我们唯一感兴趣的事件时onProgressChanged，因此我们需要在这个方法内增加代码更新泡茶时间标签的内容为SeekBar选中的值。之前我们说过SeekBar的刻度是0到9，因此我们需要将SeekBar的加1的值来显示给用户才有意义。</p>
<p>在<em>AddTeaActivity.java</em>代码中增加如下的onProgressChanged()代码：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  if(seekBar == brewTimeSeekBar) {
    // Update the brew time label with the chosen value.
    brewTimeLabel.setText((progress + 1) + &quot; m&quot;);
  }
}

</pre>
<p>在AddTeaActivity的onCreate方法中设置监听器：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
public void onCreate(Bundle savedInstanceState) {
  // …

  // Setup Listeners
  brewTimeSeekBar.setOnSeekBarChangeListener(this);
}

</pre>
<p>现在运行你的程序，并拖动SeekBar,泡茶时间标签的内容将会同步更新为正确地值：</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/13_seekbar.jpg"></img></p>
<h4>保存新增茶叶</h4>
<p>完成了增加茶叶界面之后,剩下的工作就是让用户可以将他们新增的茶叶保存到数据库中.我们将会对界面上输入数据增加一点校验,以避免茶叶名为空的数据被保存到数据库中！</p>
<p>在编辑器中打开<em>strings.xml</em> 增加一些我们在应用程序将要使用到的新标签。</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;!-- res/values/strings.xml --&gt;
&lt;string name=&quot;save_tea_label&quot;&gt;Save Tea&lt;/string&gt;
&lt;string name=&quot;invalid_tea_title&quot;&gt;Tea could not be saved.&lt;/string&gt;

&lt;string name=&quot;invalid_tea_no_name&quot;&gt;Enter a name for your tea.&lt;/string&gt;


</pre>
<p>如同前面的那样，我们需要为AddTeaActivity创建一个新的选项菜单来让用户可以执行保存茶叶的指令：</p>
<p>在<em>res/menus</em> 目录，通过选择File → New 并选 Other → Android XML 文件来创建一个新的 <em>add_tea.xml</em> XML文件, 记住资源类型为“Menu”。</p>
<p>增加保存茶叶的菜单项：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">

&lt;!-- res/menus/add_tea.xml --&gt;
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;menu xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
  &lt;item android:title=&quot;@string/save_tea_label&quot; android:id=&quot;@+id/save_tea&quot; /&gt;
&lt;/menu&gt;


</pre>
<p>返回 AddTeaActivity 代码中,类似你在BrewClockActivity中一样，增加重载方法onCreateOptionsMenu 和onOptionsItemSelected。唯一的区别是这次你提供的MenuInflater的资源文件名是<em>add_tea.xml</em> ：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
@Override
public boolean onCreateOptionsMenu(Menu menu) {
  MenuInflater inflater = getMenuInflater();
  inflater.inflate(R.menu.add_tea, menu);

  return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
  switch(item.getItemId()) {
    case R.id.save_tea:
      saveTea();

    default:
      return super.onOptionsItemSelected(item);
  }
}

</pre>
<p>下一步, 增加新方法, saveTea(), 来保存茶叶信息。saveTea 首先从界面上读取茶叶的名称和用户所选的泡茶时间，如果这些输入数据都能通过验证，就将这些数据保存到数据库中：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
public boolean saveTea() {
  // Read values from the interface
  String teaNameText = teaName.getText().toString();
  int brewTimeValue = brewTimeSeekBar.getProgress() + 1;

  // Validate a name has been entered for the tea
  if(teaNameText.length() &lt; 2) {
    AlertDialog.Builder dialog = new AlertDialog.Builder(this);
    dialog.setTitle(R.string.invalid_tea_title);
    dialog.setMessage(R.string.invalid_tea_no_name);
    dialog.show();

    return false;
  }

  // The tea is valid, so connect to the tea database and insert the tea
  TeaData teaData = new TeaData(this);
  teaData.insert(teaNameText, brewTimeValue);
  teaData.close();

  return true;
}


</pre>
<p>大段的代码，让我们过一遍这段代码的逻辑。</p>
<p>首先，我们从文本框中读取茶叶名称，从SeekBar读取泡茶时间（记着读的时间要加1以保证时间在1到10分钟之内）。下一步，我们验证茶叶名大于等于2个字符（这是非常简单的验证，如果想做更复杂的验证，那么就使用正则表达式吧）。</p>
<p>如果茶叶名称非法，我们需要让用户知道。我们使用Android提供的工具类，AlertDialog.Biulder类，这个类给我们提供了一个快捷创建和显示模态窗口的方法。在设置完标题和错误信息后，通过调用show方法来显示对话框。这个对话框是模态的modal，因此用户只有按下back按键，这个对话框才会关闭。在这时，我们不想保存任何数据，所以我们的方法返回了false。</p>
<p>如果茶名称合法，我们通过TeaData类创建一个到茶叶数据库的临时连接。这里又一次的显示出把数据库访问抽象成一个独立文件的好处：你可以从任何地方完成对数据库（译者注：其实应该是对TeaData 类）的访问。</p>
<p>当调用完teaData.insert() 来增加记录到数据库后，我们不再需要数据库连接，因此在我们返回成功前，我们关闭了连接。</p>
<p>在模拟器中运行你的程序，按下“Menu”按键，点击屏幕上的“Add Tea”。试图通过在此按下“Menu”和点击屏幕的 “Save Tea.”来保存空茶叶名的茶叶数据。由于是没有茶叶名，一条错误消息将出现在你的面前：</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/14_invalid_tea.jpg"/></p>
<p>下一步，试着键入你的茶叶名，并选择合适的泡茶时间，再次从菜单选择 “Save Tea” 。这一次，你将不在看到错误的消息。事实上，你什么都看消息不到。</p>
<h4>改进用户体验</h4>
<p>这样做不是一个很好的用户体验，用户不能知道他的茶叶是否已经成功地保存了。事实上，用户只有从“Add Tea”界面返回，去茶叶列表中查看这一个办法来检查他的是否成功的被保存。这样的做法不好，让用户知道他们的茶叶数据被成功地保存会是更好的一种方式。在茶叶数据被成功保存后，让我们在屏幕上显示一条成功信息。</p>
<p>我们要一条被动的非模态化的信息，因此AlertDialog这次就不能满足我们的需求了。下面我们将要使用另外一个Android的非常流行的特性，Toast。</p>
<p>Toast 在接近屏幕的下方显示一条消息，但是并不会终止用户的操作。Toast经常用于做非重要的的提醒和状态更新。.</p>
<p>在<em>strings.xml</em> 资源文件中新增一个字符串。注意字符串中的%s。我们在下一步中将保存的茶叶名字结合到这个字符串来显示信息。</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;!-- res/values/strings.xml --&gt;
&lt;string name=&quot;save_tea_success&quot;&gt;%s tea has been saved.&lt;/string&gt;
</pre>
<p>注意，在onOptionsItemSelected 代码中进行修改，当saveTea返回真时，创建并显示一条弹出式的Toast。第二参数getString()用来连接茶叶名称到Toast信息中。最后，我们需要将茶叶名称清楚，以便用户可以快速增加更多的新茶。</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/AddTeaActivity.java
// …
switch(item.getItemId()) {
 case R.id.save_tea:
   if(saveTea()) {
     Toast.makeText(this, getString(R.string.save_tea_success, teaName.getText().toString()), Toast.LENGTH_SHORT).show();
     teaName.setText(&quot;&quot;);
   }
// …
</pre>
<p>现在，重新运行应用程序，并增加和保存一些新茶叶。你将会看到弹出式的Toast并让你知道你的茶叶信息已经被保存成功。getString()方法用于连接存在XML文件中的String和茶叶名称，并将%s替换成茶叶的名称。</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/16_valid_save.jpg"></img></p>
<p>按下“Back”按键，返回应用程序的主屏幕，点击茶叶spinner。你新增的在数据库中的茶叶已近可以显示在spinner的选项中！</p>
<h3>用户首选项</h3>
<p>现在BrewClock已经完成了所有的功能。用户可以增加他们喜爱的茶叶和各自不同的泡茶时间到数据库中，并且他们可以快速的从选择他们并开始泡上一杯新茶。任何新增的茶叶信息都被保存在数据库中，因此，即使你退出你的程序，这些茶叶信息在你下次启动程序时仍然可以从spinner列表中找到。</p>
<p>当你重启BrewClock的时候，有一件事你必须注意，就是泡茶计数被清为了0。这使得跟踪我们每天喝了多少茶（一条重要的数据）变得困难。作为最后一个练习，让我们将泡茶计数保存在我们设备上。</p>
<p>我们将不通过增加茶叶数据库的表来完成这个功能，我们将使用Android的“共享首选项Shared Preferences”，一个Android提供给你应用程序用于存储简单数据的数据库（字符串，数字，等等）。例如，优秀的最高分和用户首选项等（译者注：非常类似Windows下的注册表）。</p>
<p>我们首先在<em>BrewClockActivity.java</em> 中增加一堆常量。这些常量用于存放你的共享首选项的名称。我们将使用键的名称来访问泡茶计数。Android负责保存和持久化我们的共享首选项文件。</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">

// src/com/example/brewclock/BrewClockActivity.java

protected static final String SHARED_PREFS_NAME = &quot;brew_count_preferences&quot;;

protected static final String BREW_COUNT_SHARED_PREF = &quot;brew_count&quot;;

</pre>
<p>下一步，为了我们能在用户首选项中读写泡茶计数，而不是直接的依赖于代码中的初始值，我们将在代码中做一些修改。在BrewClockActivity 的 onCreate 方法中我们将就该setBrewCount附件的代码：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/BrewClockActivity.java
public void onCreate() {
  // … 

  // Set the initial brew values
  SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
  brewCount = sharedPreferences.getInt(BREW_COUNT_SHARED_PREF, 0);
  setBrewCount(brewCount);

  // …
}

</pre>
<p>这里我们将以使用SharedPreference来获取应用程序的共享首选项的实例，并希望得到brew_count键值的值（通过我们之前定义的BREW_COUNT_SHARED_PREF常量来标示）。如果值能获取，这个值将返回给应用程序，如果没有我们使用getInt的第二参数作为默认值返回（在教程中为0）。</p>
<p>现在我们取得存储的泡茶计数值，我们需要确保每当泡茶计数更新的时候，这个值能写回到共享首选项中。</p>
<p>BrewClockActivity的setBrewCount中增加下面的代码：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// src/com/example/brewclock/BrewClockActivity.java
 public void setBrewCount(int count) {
   brewCount = count;
   brewCountLabel.setText(String.valueOf(brewCount));

   // Update the brewCount and write the value to the shared preferences.
   SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();
   editor.putInt(BREW_COUNT_SHARED_PREF, brewCount);
   editor.commit();
 }


</pre>
<p>共享首选项不能直接地保存。我们需要使用Android的SharedPreferences.Editor类。调用SharedPreferences的edit方法，返回一个editor实例，这个实例用来保存我们的首选项值。我们只要调用editor实例的commit方法就可以将值保存到共享首选项中。</p>
<p>我们应用程序的所有代码都已完成，现在让我们测试一下我们的程序！</p>
<p>在模拟器中运行应用程序，定一个泡茶时间（这真是一个良好的借口去泡一杯你自己爱喝的茶哦）并退出应用程序，试着运行模拟器上的安装的其他应用程序确保BrewClock被终止。记住，除非这个应用程序已经不在内存中，否则Android不会终止一个Activity。</p>
<p>当你下一次运行你的应用程序时，你将看见之前的泡茶计数已经被维护了。</p>
<h3>总结</h3>
<p>恭喜!你已经完成了这个应用的程序的所有开发工作,并使用了Android　SDK中的数个核心组件。在本教程中，你从中学到了：</p>
<ul>
<li>创建一个简单的SQLite数据库，并保存你的数据；</li>
<li>使用Android的数据库类和编写客户化类抽象数据访问；</li>
<li>在你的应用程序中增加选项菜单。；</li>
<li>在你应用程序中创建并注册新Activity并使用Intent将他们绑定成一组界面；</li>
<li>使用内建的“共享首选项”数据库来保存和提取简单用户数据。</li>
</ul>
<p>无论你要开发神马样类型的应用程序，数据存储和持久化是一个重要的主题。从工具程序和业务工具到3-D游戏，几乎每个应用程序都需要使用到Android提供的数据工具类。</p>
<p><img decoding="async" src=" https://coolshell.cn/wp-content/uploads/2011/04/17_brew_up.jpg"/></p>
<h4>Activities</h4>
<p>虽然BrewClock现在在某方面来说已经是个功能完善的应用程序了。但是我们仍然可以在增加一些功能以改进用户体验。例如你可以使用下面的方法来改进你的应用程序：</p>
<ul>
<li>在保存茶叶的时候检查是否存在茶叶名称重名；</li>
<li>增加一个菜单选项以将泡茶统计清0；</li>
<li>在共享首选项中保存最后所选的泡茶名称和时间以便程序重启时有一个有意义的默认值；</li>
<li>增加用户从茶叶数据库中删除记录的选项。</li>
</ul>
<p>在<a href="http://github.com/cblunt/BrewClock">GitHub库</a> 可以获取到所有的源代码，库中的未来的分支包含着Activitiy的解决方案 你可以通过切换你的本地代码拷贝到tutorial_2分支，下载这个开发教程源代码：<br />
[code]</p>
<p>$ git clone git://github.com/cblunt/BrewClock.git</p>
<p>$ cd BrewClock</p>
<p>$ git checkout tutorial_2</p>
<p>[/code]<br />
我希望你喜欢这个教程，希望这个教程能帮助你设计和开发更棒的Android应用程序。请通过在下面的回复让我知道你的建议和意见，当然我也欢迎你将你建议写在email中并发送给我。</p>
<p><em>感谢<a href="http://blog.anselmbradford.com/">Anselm</a>的建议和反馈！ </em></p>
<p><em>（全文完）</em><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/4270.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/04/install-150x150.gif" alt="Eclipse开发Android应用程序入门" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4270.html" class="wp_rp_title">Eclipse开发Android应用程序入门</a></li><li ><a href="https://coolshell.cn/articles/17066.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg" alt="关于移动端的钓鱼式攻击" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17066.html" class="wp_rp_title">关于移动端的钓鱼式攻击</a></li><li ><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg" alt="DHH 谈混合移动应用开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_title">DHH 谈混合移动应用开发</a></li><li ><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg" alt="Google Inbox如何跨平台重用代码？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_title">Google Inbox如何跨平台重用代码？</a></li><li ><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt="一些有意思的文章和资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_title">一些有意思的文章和资源</a></li><li ><a href="https://coolshell.cn/articles/3589.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg" alt="食客还是大厨" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3589.html" class="wp_rp_title">食客还是大厨</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/4334.html">Eclipse开发Android应用程序入门:重装上阵</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/4334.html/feed</wfw:commentRss>
			<slash:comments>21</slash:comments>
		
		
			</item>
		<item>
		<title>Eclipse开发Android应用程序入门</title>
		<link>https://coolshell.cn/articles/4270.html</link>
					<comments>https://coolshell.cn/articles/4270.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Thu, 07 Apr 2011 08:40:36 +0000</pubDate>
				<category><![CDATA[Java语言]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Eclipse]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=4270</guid>

					<description><![CDATA[<p>By Chris Blunt 翻译：赵锟 原文出处：http://www.smashingmagazine.com/2010/10/25/get-started...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/4270.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/4270.html">Eclipse开发Android应用程序入门</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>By <a title="Posts by Chris Blunt" href="http://www.smashingmagazine.com/author/chris-blunt/">Chris Blunt</a></p>
<p><strong>翻译：赵锟</strong><br />
原文出处：<a href="http://www.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/">http://www.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/</a></p>
<p>如今的移动设备应用程序开发充满着让人振奋的东西。功能强大的硬件支持，平板电脑，多样的软件平台（塞班 OS，iOS，WebOS，Windows Phone 7&#8230;)，移动设备开发者前景充满了机会和挑战。</p>
<p>当你想要开始开发你的移动设备程序时，如此多的选择可能让你产生困扰。究竟应该选择神马平台？我应该学习神马语言？为你计划的项目选择神马工具？在本教程中，你将学会如何在Google公司的开源移动设备操作系统Android下开发应用程序。</p>
<h3>为神马选Android</h3>
<p>Android是一个基于Linux内核的开源平台， 并且被安装在来自于不同厂商的上千种设备中。Android将各种移动设备的硬件如 电子罗盘，摄像头，GPS，方向感应，等等暴露给你的应用程序。<br />
<span id="more-4270"></span><br />
Android的免费开发工具可以让你以0成本开始编写你的软件。当你想向世界展示你的应用程序的时候，你可以将你的软件发布到Google的 Android 市场。向Andriod Market 发布程序只一次性的收取注册费用（25元），并且不像苹果的App Store ，对每一次的提交都要做检查，除非你的程序明显地违法，在经过一个快速检查的流程后，才能让你的程序提供给客户下载和购买。</p>
<p>下面是Android对于开发者的优点：</p>
<ul>
<li>Android的SDK可以在Windows,Mac和Linux上运行，因此你不需要为了开发环境支付额外的新硬件投入。（译者注：我曾近在Win7 64x + VMWare上成功的安装Mac Snow leopard + XCode的开发环境，对于爱用盗版的人来说，这点MS优势不是很大啊）</li>
<li>构建于JAVA上的SDK。如果你熟悉JAVA语言，你就是事半功倍了。（译者注：这个酷壳有篇文章讨论过，大家可以参看：<a href="https://coolshell.cn" target="_blank">https://coolshell.cn</a>）</li>
<li>你只要在Android Market上发布应用程序，你将有潜在的成千上万的用户。而且你不一定非要把程序发布在Android Market上，你还可以在你的博客上发布。而且有传言，Amazon已近在最近准备搭建他们自己的Android 应用程序商店了。</li>
<li>除了了技术性的<a href="http://developer.android.com/sdk/index.html">SDK 文档</a>外,还可以找到其他更多的使用者和开发者的资源。</li>
</ul>
<p>闲话少说——下面让我们进入正题，开始开发我们的Android应用程序。</p>
<h3>安装Eclipse和Android SDK</h3>
<p>Android应用程序的推荐开发环境是带有Android开发包插件(Android Devlopment Toolkit (ADT))的Eclipse。我在这里简要说明一下安装流程。如果你需要更多的细节，Google的<a href="http://developer.android.com/sdk/">开发人员网页</a>中详尽地解释了具体的安装配置过程</p>
<ul>
<li>为你的平台下载<a href="http://developer.android.com/">Android      SDK</a>（Windows ， Mac OS X 或者 Linux）。</li>
<li>在你的硬盘上解压下载文件 (在Linux, 我使用 /opt/local/).</li>
<li>如果你没有安装Eclipse，下载并安装<a href="http://eclipse.org/downloads/packages/eclipse-ide-java-developers/galileosr2">Eclipse JAVA 集成开发环境</a>包。 用于编程的话,      Google推荐使用Eclipse 3.5 (Galileo).</li>
<li>运行Eclipse 并选择<em>Help-&gt;Install New      Software</em>.</li>
<li>在Available Software窗口中点击Add按钮。</li>
<li>进入 Android Development Tools 的<em>Name</em>输入框, 在Location      输入框输入https://dl-ssl.google.com/android/eclipse/</li>
<li>检查可用软件中有Developer Tools并点击OK按钮。这将安装Android      Development Tools 和DDMS, Android的调试工具。</li>
</ul>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4306" title="install" src="https://coolshell.cn/wp-content/uploads/2011/04/install.gif" alt="" width="500" height="519" /></p>
<ul>
<li>点击Next和Finish按钮以完成安装，安装完成后，你需要重启你的Eclipse一次。</li>
<li>在Eclipse重启后，选择Window-&gt;Preference 后你可以在分类列表中看到Android这一项了。</li>
<li>现在需要告诉Eclipse，你的Android SDK安装在什么地方。点击Android项后浏览选择你解压后的Android SDK所在的路径。例如/opt/local/android-sdk。</li>
</ul>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4303" title="eclipse_android_preferences" src="https://coolshell.cn/wp-content/uploads/2011/04/eclipse_android_preferences.jpg" alt="" width="696" height="649" srcset="https://coolshell.cn/wp-content/uploads/2011/04/eclipse_android_preferences.jpg 696w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_android_preferences-300x279.jpg 300w" sizes="(max-width: 696px) 100vw, 696px" /></p>
<ul>
<li>点击OK按钮，保存信息。</li>
</ul>
<h3>选择Android 平台</h3>
<p>在你开始编写Android应用程序之前，你需要为你需要开发应用程序的Android设备下载SDK平台。每个平台都有可以安装在用户设备上的不同版本的SDK。对于Android1.5或以上版本，有两个可用的平台： <em>Android Open Source Project</em> 和 <em>Google</em>.</p>
<p><em>Android Open Source Project</em> 平台是开源的，但是不包括Google公司的私有化扩展，比如Google Map。如果不选择使用Google的API，Google的地图功能就不会在你的应用程序中生效。除非你有特别的原因，否则我们推荐你选择Google平台，因为这样你可享受到Google的扩展类库提供的便利。</p>
<ul>
<li>选择<em>Window Android SDK and AVD Manager</em>.</li>
<li>点击左栏中的<em>Available Packages</em> 并选择选择Respository中有效的Android SDK平台。</li>
<li>你可以选择列表中所需要的平台，或全选下载所有有效的平台。当你选择完毕，单击<em>Install Selected </em>并完成安装。</li>
</ul>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4307" title="sdk" src="https://coolshell.cn/wp-content/uploads/2011/04/sdk.jpg" alt="" width="500" height="291" srcset="https://coolshell.cn/wp-content/uploads/2011/04/sdk.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/04/sdk-300x174.jpg 300w" sizes="(max-width: 500px) 100vw, 500px" /><br />
一旦成功的下载所有的平台后，你就可以准备开始开发Android应用程序了。</p>
<h3>创建一个新的Android项目</h3>
<p>Eclipse的新建项目向导能为你创建一个新的Android项目，并生成可以开始运行的文件和代码。通过向导生成代码，可以让你马上得到一个Android程序运行的直观映像并为你提供了一个帮助你快速入门的方法：</p>
<ul>
<li>选择 <em>File-&gt;New-&gt;Project…</em></li>
<li>选择<em>Android Project</em></li>
<li>在<em>New Project</em> 对话框, 键入如下的设置:</li>
</ul>
<p>[code]<br />
Project Name: BrewClock<br />
Build Target: Google Inc. 1.6 (Api Level 4)<br />
Application Name: BrewClock<br />
Package Name: com.example.brewclock<br />
Create Activity: BrewClockActivity<br />
Min SDK Version: 4<br />
[/code]</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4304" title="eclipse_new_project_settings" src="https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings.jpg" alt="" width="525" height="1061" srcset="https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings.jpg 525w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings-148x300.jpg 148w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings-506x1024.jpg 506w" sizes="(max-width: 525px) 100vw, 525px" /></p>
<p>在点击了完成按钮之后，Eclipse将为你创建一个新的可以运行的Android项目。注意，你通知了Eclipse生成了一个叫做BrewClockActivity的Activity。这个Activity的代码用于运行你的应用程序。生成的代码将在程序运行时非常简单地显示一条“Hello World”消息。</p>
<h4>包</h4>
<p>包名是你的应用程序标示。当你开始准备在Android Market上发布你的应用程序的时候，Android用这个标识符精确地记录你的应用程序的更新过程，因此让包名唯一是非常重要的。尽管我们在这里使用了com.example.brewclock这样的名字空间，对于真实的应用程序，你应该选择类似于com.你的公司名.你的应用程序名 这样的包名。</p>
<h4>SDK 版本</h4>
<p>Min SDK Version 是你的Android程序所能运行得最早版本号。对于每个新发布的Android，SDK会增加并修改一些方法。通过选择一个版本号，Android（Android Market）会知道你的应用程序能运行在等于或晚于指定版本的设备之上。</p>
<h3>运行你的应用程序</h3>
<p>现在让我们开始在Eclipse中运行我们的应用程序。由于是第一次运行，Eclipse将会询问你的项目类型：</p>
<ul>
<li>选择<em>Run-&gt;Run</em> 或 按下 <em>Ctrl+F11</em>.</li>
<li>选择<em>Android Application</em> 并点击 <em>OK </em>按钮.</li>
</ul>
<p>Eclipse 将会在一个Android设备上运行一个应用程序。在这个时候，由于你没有任何Android设备，因此在运行时一定会返回一个失败，并且询问你是否要新建一个Android的虚拟设备。（AVD）<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4305" title="eclipse_no_avd" src="https://coolshell.cn/wp-content/uploads/2011/04/eclipse_no_avd.jpg" alt="" width="534" height="172" srcset="https://coolshell.cn/wp-content/uploads/2011/04/eclipse_no_avd.jpg 534w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_no_avd-300x96.jpg 300w" sizes="(max-width: 534px) 100vw, 534px" /></p>
<h4>Android 虚拟设备</h4>
<p>Android 虚拟设备 (AVD) 是一个模拟真实世界中Android设备的模拟器，例如移动电话或平板电脑。你可以在不买任何真实Android设备情况下，使用AVD测试你的应用。</p>
<p>你可以创建任意多个你喜欢的AVD，每个可以建立在不同版本的Android平台之上。对于你创建的每个Android设备，你可以配置不同的硬件属性，比如是否具有物理键盘，是否支持GPS，摄像头的像素，等等。</p>
<p>在你开始运行你的应用程序之前，你需要创建你的AVD，来运行指定的SDK平台（Google APIs 1.6）。</p>
<p>现在让我开始:</p>
<ul>
<li>如果还没有开始运行你的应用程序，点击run（或按下 <em>Ctrl+F11</em>）。</li>
<li>当目标设备弹出警告，点击<em>Yes</em> 以创建新的AVD。</li>
<li>单击<em>Android SDK and AVD      Manager</em> 对话框内的<em>New</em> 按钮.</li>
<li>为你的AVD键入如下的设置：</li>
</ul>
<p>[code]<br />
Name: Android_1.6<br />
Target: Google APIs (Google Inc.) &#8211; API Level 4<br />
SD Card Size: 16 MiB<br />
Skin Built In: Default (HVGA)<br />
[/code]</p>
<ul>
<li>单击 <em>Create AVD</em> 让Android为你创建一个新虚拟设备。</li>
<li>关闭the <em>Android SDK and AVD Manager</em> 对话框.</li>
</ul>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4308" title="sdk_manager_new_avd" src="https://coolshell.cn/wp-content/uploads/2011/04/sdk_manager_new_avd.jpg" alt="" width="400" height="574" srcset="https://coolshell.cn/wp-content/uploads/2011/04/sdk_manager_new_avd.jpg 400w, https://coolshell.cn/wp-content/uploads/2011/04/sdk_manager_new_avd-209x300.jpg 209w" sizes="(max-width: 400px) 100vw, 400px" /></p>
<h4>运行代码</h4>
<p>再次运行你的应用程序（<em>Ctrl+F11</em>）。 Eclipse 将build 你的项目并运行一个新的AVD。记住，AVD模拟了一个完全的Android系统，因此你需要有耐心来等待这个缓慢的启动过程，就如同你重启真实的Android设备一样。一个好的做法是不要关闭你的AVD，直到你完成了你一天的工作。<br />
当你的模拟器启动后，Eclipse自动地安装并运行你的应用程序。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4301" title="app_running-550-e1287474474253" src="https://coolshell.cn/wp-content/uploads/2011/04/app_running-550-e1287474474253.jpg" alt="" width="499" height="355" srcset="https://coolshell.cn/wp-content/uploads/2011/04/app_running-550-e1287474474253.jpg 499w, https://coolshell.cn/wp-content/uploads/2011/04/app_running-550-e1287474474253-300x213.jpg 300w" sizes="(max-width: 499px) 100vw, 499px" /></p>
<h3>开发你第一个Android应用</h3>
<p>生成的代码能良好的运行，但是你真正想要的是开发一个真实的应用程序。为此，我们首先果一个咸蛋的设计流程，并开始创建一个可以让你部署在Android设备上的应用。</p>
<p>大部分的开发者（包括我自己）都喜欢每天一杯咖啡或茶。在下一节中，你将开发一个简单的泡茶计数器应用程序来记录用户泡了多少杯茶，并为泡每杯茶做一个定时器。</p>
<p>你可以从<a href="http://github.com/cblunt/brewclock">GitHub</a>下载整个教程的源代码.</p>
<h4>设计用户界面</h4>
<p>在开发任何Android应用程序之前的第一步就是设计和开发用户界面。下面是一个我们这个应用程序的用户界面的一个概览。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4302" title="design_sketch" src="https://coolshell.cn/wp-content/uploads/2011/04/design_sketch.jpg" alt="" width="331" height="505" srcset="https://coolshell.cn/wp-content/uploads/2011/04/design_sketch.jpg 331w, https://coolshell.cn/wp-content/uploads/2011/04/design_sketch-196x300.jpg 196w" sizes="(max-width: 331px) 100vw, 331px" /></p>
<p>用户将能通过+和-按钮设置一个泡茶的定时器。当单击开始按钮，定时器将开始按指定的时间递减。除非用户再次点击按钮以取消计时，否则当定时器为0的时候，累计的泡茶计数brew将增加1。</p>
<h4>开发用户界面</h4>
<p>Android 用户界面或布局<em>layouts</em>, 是通过XML文档来描述的，可以在项目的res/layouts目录下找到。在之前运行在模拟器上代码中，我们可以看到由eclipse自动生成的布局代码在res/layouts/main.xml 中。</p>
<p>Eclipse有一个图形化的布局设计器，通过在屏幕上的拖拽控制来完成布局的设计，然而，我却发现直接写XML并使用图形布局来预览是更容易的方式。</p>
<p>现在让我们对main.xml做一些工作以达到上图的效果：</p>
<ul>
<li>在Eclipse中通过双击PackageExplorer的res/layouts/main.xml 来打开xml。</li>
<li>点击屏幕下方main.xml 来切换为xml视图。</li>
</ul>
<p>将main.xml中内容改为如下的内容：</p>
<p>[code]<br />
# /res/layouts/main.xml<br />
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br />
&lt;LinearLayout<br />
  xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;<br />
  android:orientation=&quot;vertical&quot;<br />
  android:layout_width=&quot;fill_parent&quot;<br />
  android:layout_height=&quot;fill_parent&quot;&gt;<br />
  &lt;LinearLayout<br />
    android:orientation=&quot;horizontal&quot;<br />
    android:layout_width=&quot;fill_parent&quot;<br />
    android:layout_height=&quot;wrap_content&quot;<br />
    android:padding=&quot;10dip&quot;&gt;<br />
    &lt;TextView<br />
      android:layout_width=&quot;wrap_content&quot;<br />
      android:layout_height=&quot;wrap_content&quot;<br />
      android:textSize=&quot;20dip&quot;<br />
      android:text=&quot;Brews: &quot; /&gt;<br />
    &lt;TextView<br />
      android:layout_width=&quot;fill_parent&quot;<br />
      android:layout_height=&quot;wrap_content&quot;<br />
      android:text=&quot;None&quot;<br />
      android:gravity=&quot;right&quot;<br />
      android:textSize=&quot;20dip&quot;<br />
      android:id=&quot;@+id/brew_count_label&quot; /&gt;<br />
  &lt;/LinearLayout&gt;<br />
  &lt;LinearLayout<br />
    android:orientation=&quot;horizontal&quot;<br />
    android:layout_width=&quot;fill_parent&quot;<br />
    android:layout_height=&quot;wrap_content&quot;<br />
    android:layout_weight=&quot;1&quot;<br />
    android:gravity=&quot;center&quot;<br />
    android:padding=&quot;10dip&quot;&gt;<br />
    &lt;Button<br />
      android:id=&quot;@+id/brew_time_down&quot;<br />
      android:layout_width=&quot;wrap_content&quot;<br />
      android:layout_height=&quot;wrap_content&quot;<br />
      android:text=&quot;-&quot;<br />
      android:textSize=&quot;40dip&quot; /&gt;<br />
    &lt;TextView<br />
      android:id=&quot;@+id/brew_time&quot;<br />
      android:layout_width=&quot;wrap_content&quot;<br />
      android:layout_height=&quot;wrap_content&quot;<br />
      android:text=&quot;0:00&quot;<br />
      android:textSize=&quot;40dip&quot;<br />
      android:padding=&quot;10dip&quot; /&gt;<br />
    &lt;Button<br />
      android:id=&quot;@+id/brew_time_up&quot;<br />
      android:layout_width=&quot;wrap_content&quot;<br />
      android:layout_height=&quot;wrap_content&quot;<br />
      android:text=&quot;+&quot;<br />
      android:textSize=&quot;40dip&quot; /&gt;<br />
  &lt;/LinearLayout&gt;<br />
  &lt;Button<br />
    android:id=&quot;@+id/brew_start&quot;<br />
    android:layout_width=&quot;fill_parent&quot;<br />
    android:layout_height=&quot;wrap_content&quot;<br />
    android:layout_gravity=&quot;bottom&quot;<br />
    android:text=&quot;Start&quot; /&gt;<br />
&lt;/LinearLayout&gt;</p>
<p>[/code]</p>
<p>正如你所见的，Android的XML布局文件是繁琐的，但却能让你控制到屏幕的各个元素。</p>
<p>在Android中最重要的接口元素是布局Layout容器，例如例子中使用的LinearLayout 。这些元素对于用户是不可见的,但是却扮演者例如Buttons 和TextViews这些元素的布局容器。</p>
<p>Android中有几种不同类型的布局视图layout view，每一种都用于开发不同的布局。如同LinearLayout 和AbsoluteLayout ，TableLayout 可以让你使用更为复杂的基于表格结构的布局。你可以在SDK的API文档的<a href="http://developer.android.com/guide/topics/ui/layout-objects.html">通用布局对象</a>中查找到更多的布局。</p>
<h4>关联你的布局Layout与代码</h4>
<p>保存你的布局，在Eclipse中点击<em>Run</em>图标或按下<em>Ctrl+F11</em>重新在模拟器中运行你的程序。你现看到不是之前出现的Hello World消息了，你将看到Android显示了一个新的界面。</p>
<p>如果点击界面上的任何按钮，他们将期望的显示为高亮，但是不会执行任何操作。现在让我们在布局修改后改进一下我们的源码：</p>
<p># /src/com/example/brewclock/BrewClockActivity.java</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
...
import android.widget.Button;
import android.widget.TextView;

public class BrewClockActivity extends Activity {
  /** Properties **/
  protected Button brewAddTime;
  protected Button brewDecreaseTime;
  protected Button startBrew;
  protected TextView brewCountLabel;
  protected TextView brewTimeLabel;

  ...
 }
</pre>
<p>下一步,我们将修改调用onCreate。当Android启动你的应用程序的时候，Android会首先调用这个方法。 在Eclipse生成的代码中，onCreate把activity的视图设置成R.layout.main。这行代码告诉Android解释我们的布局配置XML文件，并显示它。</p>
<h4>资源对象</h4>
<p>在Android中，R是一个自动生成的对象，这是一个特殊的对象，你可以在代码中通过这个对象访问项目中的资源（布局，字符串，菜单，图标，&#8230;） 。每个资源都有一个给定的id。在上面的那个布局文件中，有一些@+id XML 属性。我们将通过这些值来关联布局中的Buttons 与TextViews和我们的代码和：</p>
<p># /src/com/example/brewclock/BrewClockActivity.java</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
...
public class BrewClockActivity extends Activity {
  ...
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Connect interface elements to properties
    brewAddTime = (Button) findViewById(R.id.brew_time_up);
    brewDecreaseTime = (Button) findViewById(R.id.brew_time_down);
    startBrew = (Button) findViewById(R.id.brew_start);
    brewCountLabel = (TextView) findViewById(R.id.brew_count_label);
    brewTimeLabel = (TextView) findViewById(R.id.brew_time);
  }
}
</pre>
<h4>监听事件</h4>
<p>为了检测到用户单击我们的按钮，我们需要实现一个监听器listener。你可能会从其他的事件驱动系统中熟悉监听器或回调函数<em>callbacks</em>。比如Javascript/JQuery事件或Rails的回调函数。</p>
<p>Android通过Listener接口提供相似的机制，例如OnClickListener，这个接口中定义了那些会被事件触发的方法。当用户点击屏幕的时候，实现OnClickListener 接口将会通知你的应用程序，并告诉他们所按得屏幕按钮。你当然也需要告诉每个button的ClickListener，以便Android知道具体通知到那个监听器：</p>
<p># /src/com/example/brewclock/BrewClockActivity.java</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
...
// Be sure not to import
// `android.content.dialoginterface.OnClickListener`.
import android.view.View.OnClickListener;

public class BrewClockActivity extends Activity
  implements OnClickListener {
  ...
  public void onCreate(Bundle savedInstanceState) {
    ...
    // Setup ClickListeners
    brewAddTime.setOnClickListener(this);
    brewDecreaseTime.setOnClickListener(this);
    startBrew.setOnClickListener(this);
  }
  ...
  public void onClick(View v) {
    // TODO: Add code to handle button taps
  }
}
</pre>
<p>下一步，我们将增加每个按钮按下的处理过程。我们将为Activity类增加4个属性，这些属性将用来让用户设置和记录我们泡茶时间，泡茶计数，计时器是否在运行的标志。</p>
<p># /src/com/example/brewclock/BrewClockActivity.java</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
...
public class BrewClockActivity extends Activity
  implements OnClickListener {
  ...
  protected int brewTime = 3;
  protected CountDownTimer brewCountDownTimer;
  protected int brewCount = 0;
  protected boolean isBrewing = false;
  ...
  public void onClick(View v) {
    if(v == brewAddTime)
      setBrewTime(brewTime + 1);
    else if(v == brewDecreaseTime)
      setBrewTime(brewTime -1);
    else if(v == startBrew) {
      if(isBrewing)
        stopBrew();
      else
        startBrew();
    }
  }
}
</pre>
<p>注意我们使用了Android提供的类CountDownTimer 。这让我们非常容易的创建和开始一个简单的递减计数，这个递减计数在递减运行的时候，每当执行一个递减就发出一个通知。你将在下面的startBrew 方法中使用到这个计数器。</p>
<p>在下面的方法是所有处理逻辑，这些处理逻辑用于处理设置泡茶时间，开始停止计数和维护计数器。我们同样地在onCreate方法中来初始化我们的 brewTime和 brewCount变量。</p>
<p>将这些代码放入到不同的类中是一种好做法。但是为了简洁，我把我们所有的代码都放到了BrewClockActivity中：</p>
<p># /src/com/example/brewclock/BrewClockActivity.java</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
...
public class BrewClockActivity extends Activity
  implements OnClickListener {
  ...
  public void onCreate(Bundle savedInstanceState) {
    ...
    // Set the initial brew values
    setBrewCount(0);
    setBrewTime(3);
  }

  /**
   * Set an absolute value for the number of minutes to brew.
   * Has no effect if a brew is currently running.
   * @param minutes The number of minutes to brew.
   */
  public void setBrewTime(int minutes) {
    if(isBrewing)
      return;

    brewTime = minutes;

    if(brewTime &lt; 1)
      brewTime = 1;

    brewTimeLabel.setText(String.valueOf(brewTime) + &quot;m&quot;);
  }

  /**
   * Set the number of brews that have been made, and update
   * the interface.
   * @param count The new number of brews
   */
  public void setBrewCount(int count) {
    brewCount = count;
    brewCountLabel.setText(String.valueOf(brewCount));
  }

  /**
   * Start the brew timer
   */
  public void startBrew() {
    // Create a new CountDownTimer to track the brew time
    brewCountDownTimer = new CountDownTimer(brewTime * 60 * 1000, 1000) {
      @Override
      public void onTick(long millisUntilFinished) {
        brewTimeLabel.setText(String.valueOf(millisUntilFinished / 1000) + &quot;s&quot;);
      }

      @Override
      public void onFinish() {
        isBrewing = false;
        setBrewCount(brewCount + 1);

        brewTimeLabel.setText(&quot;Brew Up!&quot;);
        startBrew.setText(&quot;Start&quot;);
      }
    };

    brewCountDownTimer.start();
    startBrew.setText(&quot;Stop&quot;);
    isBrewing = true;
  }

  /**
   * Stop the brew timer
   */
  public void stopBrew() {
    if(brewCountDownTimer != null)
      brewCountDownTimer.cancel();

    isBrewing = false;
    startBrew.setText(&quot;Start&quot;);
  }
  ...
}
</pre>
<p>这段代码唯一和Android相关的就是使用setText方法来设置文本的显示文字。在startBrew方法中，我们创建，并开始了一个CountDownTimer来开每秒递减计数直到计数器为0。注意，我们定义了CountDownTimer以内联方式监听onTick 和 onFinish方法。 onTick 方法将每1000毫秒（1秒）执行一次，并递减, 当计数器为0的时候，onFinish方法被调用。</p>
<h4>避免在你的代码中硬编码</h4>
<p>为了使教程代码简单，我故意地在程序中将控件的标号直接写到字串中（例如： &#8220;Brew Up!&#8221;, &#8220;Start&#8221;, &#8220;Stop&#8221;） 通常，这不是一个好的做法，因为如果在大型项目中，这样做会使得修改变得麻烦。</p>
<p>Android 提供了一种简洁的方法让你使用R对象来使字符串和代码分离。R 让你在xml文件（res/values/strings.xml）定义所有你程序中字符串，并让你可以在代码中应用到这些字符串。例如：</p>
<p># /res/values/strings.xml</p>
<p>[code]<br />
&lt;string name=&quot;brew_up_label&quot;&gt;Brew Up!&lt;/string&gt;<br />
&#8230;<br />
[/code]</p>
<p># /res/com/example/brewclock/BrewClockActivity.java</p>
<p>[code]<br />
&#8230;<br />
brewLabel.setText(R.string.brew_up_label);<br />
&#8230;<br />
[/code]</p>
<p>现在，如果你想改变Brew Up! 字样，你只要一次性的修改strings.xml文件就行了。你的应用将生成一堆代码来保证你程序中所有使用到这些字符串的地方都能被生效！</p>
<h4>运行Brew Clock</h4>
<p>代码完成之后，现在是试运行程序的时候了。单击<em>Run</em> 或 <em>Ctrl+F11</em> 在模拟器中启动我们的应用. 所有都运行良好，你将会看到你创建的用户界面在准备时间一到就可以喝你所泡的茶了！试着设置不同的时间，并点击<em>Start</em> 观看倒计时。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-4300" title="app_finished-550-e1287474491689" src="https://coolshell.cn/wp-content/uploads/2011/04/app_finished-550-e1287474491689.jpg" alt="" width="499" height="355" srcset="https://coolshell.cn/wp-content/uploads/2011/04/app_finished-550-e1287474491689.jpg 499w, https://coolshell.cn/wp-content/uploads/2011/04/app_finished-550-e1287474491689-300x213.jpg 300w" sizes="(max-width: 499px) 100vw, 499px" /></p>
<h3>总结</h3>
<p>在这个关于Android的简单介绍中，你已学会如何安装Android SDK和Eclipse的Android 开发工具插件（ADT）。你也学会如何创建一个模拟设备，并通过这个设备来测试你的应用程序。你还学会了如何开发Android应用程序。上面了那些作为标题的关键概念在以后你自己开发Android应用程序的时候将会经常用到。</p>
<p>我们希望，这个教程能激发你的开发移动应用程序的欲望，并步入这个令人激动的领域。Android为当前和即将到来的移动设备应用程序开发提供了一条宽广的道路。如果你已经开发你自己的移动应用，请在评论中告诉我们。</p>
<p><em>(ik), (vf)</em></p>
<p><em>（全文完）</em><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/4334.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg" alt="Eclipse开发Android应用程序入门:重装上阵" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4334.html" class="wp_rp_title">Eclipse开发Android应用程序入门:重装上阵</a></li><li ><a href="https://coolshell.cn/articles/17066.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg" alt="关于移动端的钓鱼式攻击" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17066.html" class="wp_rp_title">关于移动端的钓鱼式攻击</a></li><li ><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg" alt="DHH 谈混合移动应用开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_title">DHH 谈混合移动应用开发</a></li><li ><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg" alt="Google Inbox如何跨平台重用代码？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_title">Google Inbox如何跨平台重用代码？</a></li><li ><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt="一些有意思的文章和资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_title">一些有意思的文章和资源</a></li><li ><a href="https://coolshell.cn/articles/3589.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg" alt="食客还是大厨" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3589.html" class="wp_rp_title">食客还是大厨</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/4270.html">Eclipse开发Android应用程序入门</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/4270.html/feed</wfw:commentRss>
			<slash:comments>34</slash:comments>
		
		
			</item>
		<item>
		<title>WSDL 1.1 中文规范</title>
		<link>https://coolshell.cn/articles/4131.html</link>
					<comments>https://coolshell.cn/articles/4131.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Fri, 01 Apr 2011 00:30:21 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[企业应用]]></category>
		<category><![CDATA[技术读物]]></category>
		<category><![CDATA[WSDL]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=4131</guid>

					<description><![CDATA[<p>WSDL规范目前最新的版本是2.0 ，但是目前大部分还是按1.1的版本进行使用，而且1.1的内容看上去比2.0也简单些，所以我就翻译了这个版本。 作为一种《炒作...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/4131.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/4131.html">WSDL 1.1 中文规范</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>WSDL规范目前最新的版本是2.0 ，但是目前大部分还是按1.1的版本进行使用，而且1.1的内容看上去比2.0也简单些，所以我就翻译了这个版本。</p>
<p>作为一种《<a title="那些炒作过度的技术和概念" href="https://coolshell.cn/articles/3609.html">炒作过度的技术和概念</a>》的一类，WEB Service的确是太过重量级，对于小型的应用，还是因该避免去使用xml和SOAP这些技术。但是在企业级的应用，WEB Service已经开始成为了一种常态，所以对其有一定了解或多或少都是有一些好处的。</p>
<p>当然，通过读规范来学习一门技术的方法，从来都不是一种好的学习方法，规范只是配合你学习的参考。而且WSDL1.1规范中笔误太多，笔者就发现了两处，都一一做了修正。</p>
<p>原文的地址在：<a href="http://www.w3.org/TR/wsdl">http://www.w3.org/TR/wsdl</a> ，学习WSDL，需要有一定XML，XML Schema XSD，SOAP的相关知识，请在阅读时特别注意。</p>
<p>另外WSDL1.1是一个宽泛的规范，所有的语法都以非正式的形式出现，而且为了满足WEB Service 扩展性的需求，也不可能定义出详尽的语法，请在阅读时特别注意。</p>
<p>我的翻译版本以word形式提供，请要转载的同学们别把酷壳logo去掉的，转载请注明出处。</p>
<p>由于个人水平有限，翻译难免出现错误。还请读者海涵。</p>
<p>下载：<a href="https://coolshell.cn/wp-content/uploads/2011/03/WSDL-中文规范1.1.doc">WSDL 中文规范1.1</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/2622.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg" alt="为什么敏捷方法能在软件开发中行之有效？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2622.html" class="wp_rp_title">为什么敏捷方法能在软件开发中行之有效？</a></li><li ><a href="https://coolshell.cn/articles/355.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png" alt="20本最好的Linux免费书籍" width="150" height="150" /></a><a href="https://coolshell.cn/articles/355.html" class="wp_rp_title">20本最好的Linux免费书籍</a></li><li ><a href="https://coolshell.cn/articles/1907.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg" alt="UI的恶梦" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1907.html" class="wp_rp_title">UI的恶梦</a></li><li ><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="一些有意思的贴子和工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_title">一些有意思的贴子和工具</a></li><li ><a href="https://coolshell.cn/articles/12206.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg" alt="HTML6 展望" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12206.html" class="wp_rp_title">HTML6 展望</a></li><li ><a href="https://coolshell.cn/articles/21214.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png" alt="Go编程模式：委托和反转控制" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21214.html" class="wp_rp_title">Go编程模式：委托和反转控制</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/4131.html">WSDL 1.1 中文规范</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/4131.html/feed</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>实用Android开发工具和资源精选</title>
		<link>https://coolshell.cn/articles/2853.html</link>
					<comments>https://coolshell.cn/articles/2853.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Thu, 26 Aug 2010 00:43:01 +0000</pubDate>
				<category><![CDATA[技术读物]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[App Inventor]]></category>
		<category><![CDATA[ebook]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=2853</guid>

					<description><![CDATA[<p>出处：A Useful Selection of Android Developer Tools and Resources 在google、开源平台，和来自移...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/2853.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/2853.html">实用Android开发工具和资源精选</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>出处</strong>：<a href="http://speckyboy.com/2010/08/04/a-useful-selection-of-android-developer-tools-and-resources/">A Useful Selection of Android Developer Tools and Resources</a></p>
<p>在google、开源平台，和来自移动电话制造商HTC,Samsung和Sony Ericsson的支持下，Android平台在市场占有率上相比去年取得的886%增长！如果我只看增长率，IPhone据统计才有61%的增长。这些数据可以给你关于两个平台流行度的印像，但是事实上，这些数据告诉了智能机开发员的谁才是真正的赢家。在时下，智能机越来越流行，因此成为一个快速增长的Android平台的智能机开发人员将会是一个不错的选择。</p>
<p>相比较其他的平台，Android不依赖于任何约束第三方应用程序的私有的操作系统（题外话：美国最近<a href="http://www.bbc.co.uk/news/technology-10836692">宣布</a>对于IPhone的破解和越狱是合法行为），Android本身就是开源的。由于开源，Android有巨大的开发社区支持。各种个样的例子和教程，GUI素材，和开发工具下载。几乎所有的都是免费提供的。我们选出接近20个可以免费或开源的，工具，资源，开发指南。希望这些资源能给你的Android应用带来帮助。<br />
<strong> 相关文章参看:</strong></p>
<ul>
<li><a href="http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/">Android App Developers GUI Kits, Icons, Fonts and Tools →</a></li>
<li><a href="http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/">iPhone and iPad Development GUI Kits, Stencils and Icons →</a></li>
<li><a href="http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/">Mobile Web and App Development Testing and Emulation Tools →</a></li>
<li><a href="http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/">14 Free Mobile Application Development Icon Sets →</a></li>
</ul>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_01.jpg" alt="" /></p>
<p><span id="more-2853"></span></p>
<h3><a href="http://andbook.anddev.org/">免费的Android开发人员电子书:andbook</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_02.jpg" alt="" /></p>
<p>如果你刚步入Android的开发，那么对于第一次开发Android应用程序的你来说，这本书是非常适合的。这本只有62页的PDF电子书里，有简单易懂的入门教程，帮助你在没有任何Android开发知识的背景下，教你开发Android应用程序。<br />
<a href="http://andbook.anddev.org/">Free Android Developer Ebook: andbook! →</a></p>
<h3><a href="http://kronox.org/documentacion/Professional_Android_Application_Development.pdf">免费的Android开发人员电子书:专业Android应用程序开发</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_03.jpg" alt="" /></p>
<p>专业Android应用程序开发PDF电子书，是一本创建移动手机应用程序的上手指南，这本书特点简洁，还有着能帮助你快速构建真实Android移动电话应用程序的典型的例子。本书覆盖了Android手机所有本质特性，并同时展示了Android手机的高级特性。<br />
<a href="http://kronox.org/documentacion/Professional_Android_Application_Development.pdf">Free Android Developer Ebook: Professional Android Application Development →</a></p>
<h3>免费和开源的应用程序</h3>
<ul>
<li><a href="http://code.google.com/p/apps-for-android/">apps-for-android (Open Source Applications) →<br />
</a>这个链接中包含了许多实用的开源的Android应用程序。这些应用程序展示了Android的许多特性。</li>
</ul>
<ul>
<li><a href="http://developer.android.com/resources/samples/index.html">List of Sample Android Apps →</a>（<strong>译者注：</strong>我勒个擦！墙掉了，中国Android开发人员杯具了，看来官方不给力啊，这次元还真是不毛之地啊）<br />
这个Web页面是一个Android开发包中的实例程序列表。使用这个页面上的链接，你可以通过你的浏览器来阅读这些例子程序的源代码。你也可以把这些实例程序下载下来，当你需要的时候，你可以修改并使用他们。</li>
</ul>
<ul>
<li><a href="http://code.google.com/p/android-cookbook/">Android Cookbook (Examples in Cookbook Form) →<br />
</a>这个站点有很多实用的Android示例程序，你完全可以重用这些例子。</li>
</ul>
<ul>
<li><a href="http://www.openintents.org/en/">OpenIntents →<br />
</a>OpenIntends 设计和实现了开放式 intents和接口，其使得Android移动应用程序能更紧密的结合在一起。同时OpenIntends免费的提供了更专业和复杂的实例应用程序来演示他们的用法。</li>
</ul>
<ul>
<li><a href="http://www.androidsnippets.org/">Android Snippets (Share Useful Snippets of Source Code) →<br />
</a>Android Snippets 是一个Android的实用代码段库，这个库是用来分享实用和优秀的Android应用程序代码；如果没有特别的需求，我们可以大量的重用这些代码库。</li>
</ul>
<h3><a href="http://www.addictivetips.com/windows-tips/download-google-android-emulator/">Windows上的Android</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_10.jpg" alt="" /></p>
<p>对于那些想测试驱动Android的开发者，可以使用这个Android模拟器，这个模拟器以单独的应用程序的形式独立运行在Windows PC之上，使用这个模拟器不用下载和完全安装复杂的Android开发包。你甚至可以在这个模拟器上安装和测试Android系统兼容的应用程序。<br />
<a href="http://developer.android.com/guide/developing/tools/emulator.html">Android Emulator on Windows →</a></p>
<h3><a href="http://developer.android.com/guide/developing/tools/emulator.html">来自应用程序开发入门的Android模拟器</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_11.jpg" alt="" /></p>
<p>Android的开发包中包含了一个移动设备模拟器。这个模拟器模仿了典型移动设备的硬件和软件特性(当然，不包含打电话)。这个模拟器提供各种个样的导航和控制按键，可以使用你的鼠标和键盘来“按”下这些按键为你的应用程序生成事件。这个模拟器也提供一个屏幕为你显示应用程序。同时，SDK中提供了很多能在模拟器上运行的应用程序。<br />
<a href="http://developer.android.com/guide/developing/tools/emulator.html">Android Emulator from The Developer’s Guide →</a></p>
<h3><a href="http://www.openintents.org/en/node/23">感应模拟器</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_12.jpg" alt="" /></p>
<p>感应模拟器是一个JAVA独立应用程序，感应模拟器模拟感应数据并把数据传给Android模拟器。这个感应模拟器可以让你模拟加速度仪器，指南针，和方向感应，这些数据可以用于Android应用程序，并通过感应器进行控制。<br />
<a href="http://www.openintents.org/en/node/23">SensorSimulator →</a></p>
<h3><a href="http://github.com/commonsguy/droidex">DroidEx:大（巨）屏上的Android项目</a></h3>
<p>DroidEx 可以让你附加的Android设备的显示屏内容复制一份到你的开发机屏幕。使用DroidEx来做演示是非常有用的。因为你可以把你的Android设备连接到你的笔记本电脑上或投影仪上，你的客户就可以通过这些设备来看你Android设备上的内容。DroidEx还可以用来演示那些用模拟器不方便演示的内容，比如说GPS或加速度仪器等内容。<br />
<a href="http://github.com/commonsguy/droidex">DroidEx: Projecting Android on the Big(ger) Screen →</a></p>
<h3><a href="http://appinventor.googlelabs.com/about/">Android的App Inventor</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_13.jpg" alt="" /><br />
Android 的App Inventor是来自Google为非开发人员准备的新工具，通过这个工具，非开发人员可以非常容易地在里面创建应用程序。可以通过网站的视屏来预览这个工具的功能特性。(你可以参看酷壳的<a href="https://coolshell.cn/articles/2608.html" target="_blank">这篇文章</a>)</p>
<p>(<strong>译者注：</strong>这里还有一个youtube视屏，可惜也墙掉了）<br />
<a href="http://developer.android.com/guide/appendix/faq/commontasks.html"></a></p>
<h3><a href="http://developer.android.com/guide/appendix/faq/commontasks.html">如何开发Android中的常用任务</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_04.jpg" alt="" /></p>
<p>这是一个关于Android开发可能遇到的常用任务列表集合，并提供了一个快速、 how-to方式的帮助，来帮助你完成这些任务。</p>
<p><a href="http://developer.android.com/guide/appendix/faq/commontasks.html">Common Tasks and How to Do Them in Android→</a></p>
<h3><a href="http://andblogs.net/fastboot/">快速启动小抄</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_14.jpg" alt="" /></p>
<p>快速启动小抄是一个非常实用和快速的列表，这个列表中罗列一些关于快速启动的相关任务。<br />
<a href="http://andblogs.net/fastboot/">Fastboot Cheat Sheet →</a></p>
<h3><a href="http://developer.android.com/guide/practices/ui_guidelines/index.html">UI指导原则</a></h3>
<p>在这里你可以找一些到官方文章的连接，这些连接来自于“The Developer&#8217;s Guide”。这些文章的内容描述了关于Android可视交互应用程序的UI设计开发的指导原则。</p>
<ul>
<li><a href="http://developer.android.com/guide/practices/ui_guidelines/icon_design.html">Icon Design Guidelines →</a></li>
<li>图标指导原则描述每类图标的细节，并做关于尺寸，颜色，阴影其他的细节的规范，根据这些规范你的设计的图标可以适用于Android系统。你也可以下载Android图标模板包，这个包里面是一些Photoshop和Illustrator模板和滤镜文件，通过这个模板包你可以更简单的创建满足规范的图标。</li>
<li><a href="http://developer.android.com/guide/practices/ui_guidelines/icon_design.html#templatespack">Download the Android Icon Templates Pack</a></li>
<li>控件设计指导描述了如何设计适合其他主页屏的控件。这个连接会连接到一些图形文件和模板，通过这些模板和文件可以使你设计更简单。</li>
<li><a href="http://developer.android.com/guide/practices/ui_guidelines/widget_design.html">Widget Design Guidelines →</a></li>
<li>Activity和Task设计指导描述了活动的工作方式，并用图解示例演示了Activity，并描述了其重要的底层机制和原理，如多任务系统，Activity重用，intents，Activity栈，和Task。以设计层面的角度覆盖了活动的所有内容。</li>
<li><a href="http://developer.android.com/guide/practices/ui_guidelines/activity_task_design.html">Activity and Task Design Guidelines →</a></li>
<li>菜单设计指导描述了上下文菜单和选项菜单的不同。如何放置菜单项，何时放置屏幕命令，和其他的一些菜单细节。</li>
<li><a href="http://developer.android.com/guide/practices/ui_guidelines/widget_design.html">Menu Design Guidelines →</a></li>
</ul>
<p><strong><a href="http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts">理解Android中的用户接口 来自于</a><a href="http://mobiforge.com/">mobiforge.com</a></strong></p>
<p>这4部分的文档来自于<a href="http://mobiforge.com/">mobiforge.com</a>,文档中包含了组成Android UI的各种要素。文档的第一部分讨论Android中各种各样的有效的的布局。</p>
<ol>
<li>
<ol>
<li><a href="http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts">Understanding User Interface in Android – Part 1 →</a></li>
<li><a href="http://mobiforge.com/designing/story/understanding-user-interface-android-part-2-views">Understanding User Interface in Android – Part 2 →</a></li>
<li><a href="http://mobiforge.com/designing/story/understanding-user-interface-android-part-3-more-views">Understanding User Interface in Android – Part 3 →</a></li>
<li><a href="http://mobiforge.com/designing/story/understanding-user-interface-android-part-4-even-more-views">Understanding User Interface in Android – Part 4 →</a></li>
</ol>
</li>
</ol>
<h3><a href="http://www.androidpatterns.com/">Android UI模式</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_16.jpg" alt="" /></p>
<p><a href="http://www.androidpatterns.com/">Android UI Patterns →</a></p>
<h3><a href="http://www.droiddraw.org/" target="_blank">DroidDraw:Android用户接口图形编辑器</a></h3>
<p style="text-align: left;"><a href="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_19.jpg"><img decoding="async" loading="lazy" class="size-full wp-image-2868 alignnone" title="android_dev_19" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_19.jpg" alt="" width="300" height="223" /></a></p>
<p>DroidDraw是一个为Android创建图形用户界面的UI设计器。它是一个独立的可执行程序，可以运行在Mac OS X，Windows和Linux上。</p>
<p><a href="http://www.droiddraw.org/">DroidDraw : Graphical User Interface Editor for Android →</a></p>
<h3><a href="http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/">Android GUI PSD 向量包</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_20.jpg" alt="" /></p>
<p>Android GUI Starter Kit包里面包含了多个按钮元素和不同接口选项的AndroidGUI内容。这些元素是基于Android1.5 GUI的，并且这些包里面的被提供给开源社区的Android应用程序模型。大部分的GUI元素和手机图例都是使用向量路径制成，所以他们非常地容易被缩放。对于文本AndroidSans包被使用。<br />
<a href="http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/">Android GUI PSD Vector Kit →</a></p>
<h3><a href="http://unitid.nl/2009/11/fireworks-template-for-android/">Android的Firworks Template</a></h3>
<p><img decoding="async" title="android_dev_01" src="https://coolshell.cn/wp-content/uploads/2010/08/android_dev_22.jpg" alt="" /></p>
<p>在Fireworks模板中，Android的各种元素被以向量图形的方式被重绘。在目录中，这些元素的名称大多根据Android词汇表被命名。<br />
<a href="http://unitid.nl/2009/11/fireworks-template-for-android/">Fireworks Template for Android →</a></p>
<h3><a href="http://www.tomhume.org/2010/01/android-wireframe-templates.html">Android线框模板 </a></h3>
<p>线框PDF是信纸大小（8.5英寸 * 11英寸）并且各部件都被拉伸。因此你可以非常容易的以纸张原型或拉伸为真实尺寸的方式来使用。如果你没有信纸，你可以用A4纸来打印。<br />
<a href="http://www.tomhume.org/2010/01/android-wireframe-templates.html">Android Wireframe Templates →</a><br />
你也可以参考：</p>
<ul>
<li><a href="http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/">Android App Developers GUI Kits, Icons, Fonts and Tools →</a></li>
<li><a href="http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/">iPhone and iPad Development GUI Kits, Stencils and Icons →</a></li>
<li><a href="http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/">Mobile Web and App Development Testing and Emulation Tools →</a></li>
<li><a href="http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/">14 Free Mobile Application Development Icon Sets →</a></li>
<li><a href="http://speckyboy.com/2009/09/15/45-cool-google-android-apps-the-perfect-iphone-replacement/">45+ Cool Google Android Apps – The Perfect iPhone Replacement →</a></li>
</ul>
<p><strong>（全文完）</strong><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt="一些有意思的文章和资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_title">一些有意思的文章和资源</a></li><li ><a href="https://coolshell.cn/articles/2608.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-150x150.jpg" alt="Google App Inventor " width="150" height="150" /></a><a href="https://coolshell.cn/articles/2608.html" class="wp_rp_title">Google App Inventor </a></li><li ><a href="https://coolshell.cn/articles/17066.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg" alt="关于移动端的钓鱼式攻击" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17066.html" class="wp_rp_title">关于移动端的钓鱼式攻击</a></li><li ><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg" alt="DHH 谈混合移动应用开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_title">DHH 谈混合移动应用开发</a></li><li ><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg" alt="Google Inbox如何跨平台重用代码？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_title">Google Inbox如何跨平台重用代码？</a></li><li ><a href="https://coolshell.cn/articles/4710.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="Python 和 PyGame 的一些示例" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4710.html" class="wp_rp_title">Python 和 PyGame 的一些示例</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/2853.html">实用Android开发工具和资源精选</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/2853.html/feed</wfw:commentRss>
			<slash:comments>36</slash:comments>
		
		
			</item>
		<item>
		<title>免费电子书列表</title>
		<link>https://coolshell.cn/articles/2775.html</link>
					<comments>https://coolshell.cn/articles/2775.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Wed, 04 Aug 2010 10:37:59 +0000</pubDate>
				<category><![CDATA[技术读物]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[book]]></category>
		<category><![CDATA[Cheat Sheet]]></category>
		<category><![CDATA[ebook]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=2775</guid>

					<description><![CDATA[<p>在StackOverflow上，有人要打算收集个免费电子书的列表，结果很快就有人分享了一个列表。很不错，我就转过来了。原帖的地址在http://stackove...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/2775.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/2775.html">免费电子书列表</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>在StackOverflow上，有人要打算收集个免费电子书的列表，结果很快就有人分享了一个列表。很不错，我就转过来了。原帖的地址在<a href="http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books">http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books</a> （注意：有些连接可能会被墙掉）</p>
<p>List of Free Programming books (compiled):  <strong>Meta-List</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.htdp.org/2003-09-26/Book/">How to Design Programs: An Introduction to Computing and Programming</a></li>
<li><a rel="nofollow" href="http://www.coderholic.com/25-free-computer-science-books/">25 Free Computer Science Ebooks</a></li>
<li><a rel="nofollow" href="http://www.freetechbooks.com/">Free Tech Books</a></li>
<li><a rel="nofollow" href="http://www.mindviewinc.com/Books/">MindView Inc</a> (List of Free Books)</li>
<li><a rel="nofollow" href="http://en.wikibooks.org/wiki/Category%3aProgramming">Wikibooks: Programming</a></li>
<li><a rel="nofollow" href="http://en.wikibooks.org/wiki/Category%3aProgramming"></a><a rel="nofollow" href="http://refcardz.dzone.com/">Cheat Sheets (Free)</a></li>
<li><a rel="nofollow" href="http://blogs.msdn.com/wriju/archive/2009/01/07/free-ebooks-at-codeplex.aspx">CodePlex List of Free E-Books</a></li>
<li><a rel="nofollow" href="http://www.booktraining.net/">Book Training &#8211; On Video!</a></li>
<li><a rel="nofollow" href="http://www.spmn.com/products_guidebooks.html">Sofware Program Managers Network &#8211; Free EBooks</a></li>
<li><a rel="nofollow" href="http://www.linbai.info/">EBook Share @ linbai.info</a></li>
<li><a rel="nofollow" href="http://www.freebooksclub.net/">FreeBooksClub.Net</a></li>
<li><a rel="nofollow" href="http://theassayer.org/">Theassayer.org</a> (Free Online books)</li>
<li><a rel="nofollow" href="http://oreilly.com/openbook/">O&#8217;Reilly&#8217;s Open Books Project</a></li>
<li><a rel="nofollow" href="http://www.techbooksforfree.com/">TechBooksForFree.com</a></li>
<li><a rel="nofollow" href="http://www.galileocomputing.de/katalog/openbook">Galileo Computing</a> (Free Downloadable German Books)</li>
</ul>
<p><span id="more-2775"></span><strong>Graphics Programming</strong></p>
<ul>
<li>Nvidia <a rel="nofollow" href="http://http.developer.nvidia.com/GPUGems/gpugems_part01.html">GPU Gems 1</a></li>
<li>Nvidia <a rel="nofollow" href="http://http.developer.nvidia.com/GPUGems2/gpugems2_part01.html">GPU Gems 2</a></li>
<li>Nvidia <a rel="nofollow" href="http://http.developer.nvidia.com/GPUGems3/gpugems3_part01.html">GPU Gems 3</a></li>
<li><a rel="nofollow" href="http://www.gamedev.net/reference/articles/article1698.asp">Graphics Programming Black Book</a></li>
</ul>
<p><strong>Language Agnostic</strong></p>
<ul>
<li><a rel="nofollow" href="http://codebetter.com/files/folders/codebetter_downloads/entry179694.aspx">Foundations of Programming</a> by Karl Seguin</li>
<li><a rel="nofollow" href="http://scpd.stanford.edu/knuth/index.jsp">Computer Musings</a> (Lectures by Donald Knuth)</li>
<li><a rel="nofollow" href="http://www.catb.org/esr/writings/cathedral-bazaar/">The Cathedral and the Bazaar</a> (Introduction to Open Source Software)</li>
<li><a rel="nofollow" href="http://www.codeplex.com/AppArchGuide">Patterns and Practices: Application Architecture Guide 2.0</a></li>
<li><a rel="nofollow" href="http://www.cl.cam.ac.uk/~rja14/book.html">Security Engineering</a></li>
<li><a rel="nofollow" href="http://www.dspguide.com/">Digital Signal Processing For Engineers and Scientists</a></li>
<li><a rel="nofollow" href="http://gettingreal.37signals.com/">Getting Real</a> (Courtesy <a rel="nofollow" href="http://37signals.com/">37 Signals</a>)</li>
<li><a rel="nofollow" href="http://mitpress.mit.edu/sicp/">Structure and Interpretation of Computer Programs</a></li>
<li><a rel="nofollow" href="http://www.infoq.com/minibooks/domain-driven-design-quickly">Domain Driven Design Quickly</a></li>
<li><a rel="nofollow" href="http://homepage.mac.com/s_lott/books/oodesign.html">OO Design</a></li>
<li><a rel="nofollow" href="http://smartbear.com/codecollab-code-review-book.php">Best Kept Secrets of Peer Code Review</a></li>
<li><a rel="nofollow" href="http://www.scribd.com/doc/7181362/NASA-Software-Measurement-Guidebook">NASA Software Measurement Handbook</a></li>
<li><a rel="nofollow" href="http://homepages.inf.ed.ac.uk/dts/pm/Papers/nasa-manage.pdf">NASA Manager Handbook for Software Development</a></li>
<li><a rel="nofollow" href="http://www.cl.cam.ac.uk/teaching/Lectures/funprog-jrh-1996/">Introduction to Functional Programming</a> &#8211; Class Lectures and Slides</li>
<li><a rel="nofollow" href="http://www.htdp.org/">How to Design Programs</a> &#8211; MIT Press</li>
<li><a rel="nofollow" href="http://www.swebok.org/stoneman/trial_1_00.html">Guide to the Software Engineering Body of Knowledge</a> &#8211; IEEE Computer Society Press</li>
<li><a rel="nofollow" href="http://ocw.mit.edu/OcwWeb/web/home/home/index.htm">Online Course Materials</a> &#8211; MIT</li>
<li><a rel="nofollow" href="http://www.cs.berkeley.edu/~vazirani/algorithms.html">Algorithms</a> (Draft Copy)</li>
<li><a rel="nofollow" href="http://dotnetslackers.com/projects/Data-Structures-And-Algorithms/">Data Structures and Algorithms</a></li>
<li><a rel="nofollow" href="http://www.agileskills.org/download.html.en">Essential Skills for Agile Development</a></li>
<li><a rel="nofollow" href="http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/">Programming Languages: Application and Interpretation</a></li>
<li><a rel="nofollow" href="http://pine.fm/LearnToProgram/">Learn to Program</a></li>
<li><a rel="nofollow" href="http://www.dreamsongs.com/Files/PatternsOfSoftware.pdf">Patterns of Software: Tales from the Software Community</a></li>
<li><a rel="nofollow" href="http://freeworld.thc.org/root/phun/unmaintain.html">How to write Unmaintainable Code</a></li>
<li><a rel="nofollow" href="http://catb.org/esr/writings/taoup/html/">The Art of Unix Programming</a></li>
<li><a rel="nofollow" href="http://nexus.realtimepublishers.com/dgbcq.php">The Definitive Guide to Building Code Quality</a></li>
<li><a rel="nofollow" href="http://openbookproject.net/thinkcs/cpp.php">How to Think Like a Computer Scientist</a></li>
<li><a rel="nofollow" href="http://planning.cs.uiuc.edu/">Planning Algorithms</a></li>
<li><a rel="nofollow" href="http://greenteapress.com/semaphores/">The Little Book of Semaphores</a></li>
<li><a rel="nofollow" href="http://www.ii.uib.no/~michal/und/i227/book/book.pdf">Mathematical Logic &#8211; an Introduction</a></li>
<li><a rel="nofollow" href="http://www.cse.ohio-state.edu/~gurari/theory-bk/theory-bk.html">An Introduction to the Theory of Computation</a></li>
<li><a rel="nofollow" href="http://devshaped.com/book">Developers Developers Developers Developers</a></li>
<li><a rel="nofollow" href="http://www.iecc.com/linker/">Linkers and loaders</a></li>
<li><a rel="nofollow" href="http://beej.us/guide/bgnet/">Beej&#8217;s Guide to Network Programming</a></li>
<li><a rel="nofollow" href="http://www.infoq.com/minibooks/domain-driven-design-quickly">Domain Driven Design Quickly</a></li>
<li><a rel="nofollow" href="http://compilers.iecc.com/crenshaw/">Let&#8217;s Build a Compiler</a></li>
<li><a rel="nofollow" href="http://producingoss.com/">Producing Open Source Software</a></li>
<li><a rel="nofollow" href="http://www.lindaspaces.com/book/">How to Write Parallel Programs</a></li>
<li><a rel="nofollow" href="http://www.neildavidson.com/dontjustrollthedice.html">Don&#8217;t Just Roll the Dice</a></li>
</ul>
<p><strong>ASP.NET MVC</strong>:</p>
<ul>
<li><a rel="nofollow" href="http://weblogs.asp.net/scottgu/archive/2009/03/10/free-asp-net-mvc-ebook-tutorial.aspx">NerdDinner Walkthrough</a></li>
</ul>
<p><strong>Assembly Language</strong></p>
<ul>
<li><a rel="nofollow" href="http://download.savannah.gnu.org/releases/pgubook/ProgrammingGroundUp-1-0-booksize.pdf">ProgrammingGroundUp</a></li>
<li><a rel="nofollow" href="http://drpaulcarter.com/pcasm/">Paul Carter&#8217;s Tutorial on x86 Assembly</a></li>
<li><a rel="nofollow" href="http://www.agner.org/optimize/">Software optimization resources by Agner Fog</a></li>
</ul>
<p><strong>Bash</strong></p>
<ul>
<li><a rel="nofollow" href="http://tldp.org/LDP/abs/html/">Advanced Bash-Scripting Guide</a></li>
</ul>
<p><strong>C/C++</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.knosof.co.uk/cbook/cbook.html">The new C standard &#8211; an annotated reference</a></li>
<li><a rel="nofollow" href="http://publications.gbdirect.co.uk/c_book/">The C book</a></li>
<li><a rel="nofollow" href="http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html">Thinking in C++, Second Edition</a></li>
<li><a rel="nofollow" href="http://cppannotations.sourceforge.net/">C++ Annotations</a></li>
<li><a rel="nofollow" href="http://www.agner.org/optimize/">Software optimization resources by Agner Fog</a></li>
<li><a rel="nofollow" href="http://cartan.cas.suffolk.edu/oopdocbook/opensource/index.html">Introduction to Design Patterns in C++ with Qt 4</a> (Open Publication License)</li>
</ul>
<p><strong>C#</strong></p>
<ul>
<li>See <strong>.NET</strong> below</li>
</ul>
<p><strong>Django</strong></p>
<ul>
<li><a rel="nofollow" href="http://djangobook.com/">Djangobook.com</a></li>
</ul>
<p><strong>Forth</strong></p>
<ul>
<li><a rel="nofollow" href="http://home.iae.nl/users/mhx/sf.html">Starting Forth</a></li>
</ul>
<p><strong>Git</strong></p>
<ul>
<li><a rel="nofollow" href="http://progit.org/book/">Pro Git</a></li>
<li><a rel="nofollow" href="http://book.git-scm.com/index.html">The Git Community Book</a></li>
</ul>
<p><strong>Haskell</strong></p>
<ul>
<li><a rel="nofollow" href="http://learnyouahaskell.com/chapters">Learn You a Haskell</a></li>
<li><a rel="nofollow" href="http://book.realworldhaskell.org/read/">Real World Haskell</a></li>
</ul>
<p><strong>Java</strong></p>
<ul>
<li><a rel="nofollow" href="http://java.sun.com/docs/books/tutorial/">Sun&#8217;s Java Tutorials</a></li>
<li><a rel="nofollow" href="http://www.mindview.net/Books/TIJ/">Thinking in Java</a></li>
<li><a rel="nofollow" href="http://openbookproject.net/thinkcs/java.php">How to Think Like a Computer Scientist</a></li>
<li><a rel="nofollow" href="http://www.redbooks.ibm.com/redbooks/SG245118.html">Java Thin-Client Programming</a></li>
<li><a rel="nofollow" href="http://s3.amazonaws.com/neilbartlett.name/osgibook_preview_20090110.pdf">OSGi in Practice</a> (CreativeCommons Attribution Non-commercial Share Alike License)</li>
</ul>
<p><strong>JavaScript</strong></p>
<ul>
<li><a rel="nofollow" href="http://eloquentjavascript.net/">Eloquent JavaScript</a></li>
<li><a rel="nofollow" href="http://www.crockford.com/javascript/">Crockford&#8217;s JavaScript</a></li>
<li><a rel="nofollow" href="http://www.rebeccamurphey.com/jqfundamentals/">jQuery Fundamentals</a> (starts with JS basics)</li>
</ul>
<p><strong>Linux</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.advancedlinuxprogramming.com/">Advanced Linux Programming</a></li>
</ul>
<p><strong>Lisp</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.gigamonkeys.com/book/">Practical Common Lisp</a></li>
<li><a rel="nofollow" href="http://www.paulgraham.com/onlisp.html">On Lisp</a></li>
<li><a rel="nofollow" href="http://www.paulgraham.com/acl.html">ANSI Common Lisp</a></li>
<li><a rel="nofollow" href="http://www.cs.cmu.edu/Groups/AI/html/cltl/mirrors.html">Common Lisp the Language, 2nd Edition</a></li>
<li><a rel="nofollow" href="http://psg.com/~dlamkins/sl/contents.html">Successful Lisp</a></li>
<li><a rel="nofollow" href="http://letoverlambda.com/index.cl/toc">Let Over Lamda &#8211; 50 Years of Lisp</a></li>
</ul>
<p><strong>Lua</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.lua.org/pil/">Programming In Lua</a> (for v5 but still largely relevant)</li>
</ul>
<p><strong>Maven</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.maestrodev.com/better-build-maven">Better Builds with Maven</a></li>
<li><a rel="nofollow" href="http://www.sonatype.com/books/mvnex-book/reference/public-book.html">Maven by Example</a></li>
<li><a rel="nofollow" href="http://www.sonatype.com/books/maven-book/reference/">Maven: The Definitive Guide</a></li>
</ul>
<p><strong>Mercurial</strong></p>
<ul>
<li><a rel="nofollow" href="http://hgbook.red-bean.com/">Mercurial: The Definitive Guide</a></li>
<li><a rel="nofollow" href="http://hginit.com/">HGInit &#8211; Mercurial Tutorial by Joel Spolsky</a></li>
</ul>
<p><strong>.NET (C#)</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.programmersheaven.com/2/CSharpBook">Free C# Book</a> Covers C#1.0 and 2.0 (Courtesy of <a rel="nofollow" href="http://www.programmersheaven.com/">Programmers Heaven</a>)</li>
<li><a rel="nofollow" href="http://www.infoq.com/minibooks/vsnettt">Visual Studio Tips and Tricks</a></li>
<li><a rel="nofollow" href="http://weblogs.asp.net/zeeshanhirani/archive/2008/12/05/my-christmas-present-to-the-entity-framework-community.aspx">Entity Framework</a> (514 pages)</li>
<li><a rel="nofollow" href="http://www.charlespetzold.com/dotnet/index.html">Charles Petzold&#8217;s .Net Book 0</a></li>
<li><a rel="nofollow" href="http://www.albahari.com/threading/">Threading in C#</a></li>
<li><a rel="nofollow" href="http://www.csharpcourse.com/">C# Yellow Book</a> (Intro to programming)</li>
<li><a rel="nofollow" href="http://en.wikibooks.org/wiki/C_Sharp_Programming">C# Programming &#8211; Wikibook</a></li>
<li><a rel="nofollow" href="http://www.techotopia.com/index.php/C_Sharp_Essentials">C# Essentials</a></li>
<li><a rel="nofollow" href="http://www.brpreiss.com/books/opus6/">Data Structures and Algorithms with Object-Oriented Design Patterns in C#</a></li>
<li><a rel="nofollow" href="http://downloads.red-gate.com/ebooks/DotNet/illustratedcsharp2008.zip">Illustrated C# 2008</a> (Download) (<strong>.ZIP</strong>) [dead link]</li>
<li><a rel="nofollow" href="http://www.red-gate.com/products/ants_performance_profiler/be_ahead_of_the_game_ebook.htm?utm_source=simpletalk&amp;utm_medium=email&amp;utm_content=nlv_aheadofgame-ebook&amp;utm_campaign=antsperformanceprofiler">O&#8217;Reilly&#8217;s C# Pocket Reference Manual</a> (<em>Free Ebook courtesy of <a rel="nofollow" href="http://red-gate.com/">Red Gate Software</a></em>) [dead link]</li>
</ul>
<p><strong>NoSQL</strong></p>
<ul>
<li><a rel="nofollow" href="http://books.couchdb.org/relax/">CouchDB: The Definitive Guide</a></li>
</ul>
<p><strong>Objective-C</strong></p>
<ul>
<li><a rel="nofollow" href="http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/">The Objective-C Programming Language</a></li>
</ul>
<p><strong>Parrot / Perl 6</strong></p>
<ul>
<li><a rel="nofollow" href="http://github.com/perl6/book/">Perl 6</a> (Work in progress)</li>
</ul>
<p><strong>Perl</strong></p>
<ul>
<li><a rel="nofollow" href="http://hop.perl.plover.com/book/">Higher Order Perl</a></li>
<li><a rel="nofollow" href="http://www.greenteapress.com/perl/">Perl The Hard Way</a></li>
<li><a rel="nofollow" href="http://www.extremeperl.org/bk/home">Extreme Perl</a></li>
<li><a rel="nofollow" href="http://linkmingle.com/list/13-plus-List-of-Free-Great-Perl-Books-available-Online-freebooksandarticles">Perl Free Online EBooks</a> <strong>Meta-List</strong></li>
<li><a rel="nofollow" href="http://www.masonbook.com/book/">The Mason Book</a></li>
<li><a rel="nofollow" href="http://modperlbook.org/">Practical mod_perl</a></li>
<li><a rel="nofollow" href="http://www.perl.org/books/beginning-perl/">Beginning Perl</a></li>
<li><a rel="nofollow" href="http://www.masonbook.com/book/">Embedding Perl in HTML with Mason</a> (Open Publication License)</li>
<li><a rel="nofollow" href="http://lwp.interglacial.com/index.html">Perl &amp; LWP</a></li>
<li><a rel="nofollow" href="http://www.globalspin.com/thebook/">Perl for the Web</a></li>
<li><a rel="nofollow" href="http://oreilly.com/openbook/webclient/">Web Client Programming with Perl</a></li>
<li><a rel="nofollow" href="http://github.com/chromatic/modern_perl_book/">Modern Perl 5</a> (Work in progress)</li>
</ul>
<p><strong>PHP</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.ipbwiki.com/Practical_PHP_Programming%3aTable_Of_Contents">Practical PHP Programming</a> (Wiki that contains O&#8217;Reilly&#8217;s <em>PHP In a Nutshell</em>)</li>
<li><a rel="nofollow" href="http://www.survivethedeepend.com/">Zend Framework: Survive the Deep End</a></li>
</ul>
<p><strong>PowerShell</strong></p>
<ul>
<li><a rel="nofollow" href="http://powershell.com/cs/blogs/ebook/">Mastering PowerShell</a></li>
</ul>
<p><strong>Prolog</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.amzi.com/ExpertSystemsInProlog/">Building Expert Systems in Prolog</a></li>
<li><a rel="nofollow" href="http://www.amzi.com/AdventureInProlog/advfrtop.htm">Adventure in Prolog</a></li>
<li><a rel="nofollow" href="http://computing.unn.ac.uk/staff/cgpb4/prologbook/">Prolog Programming A First Course</a></li>
<li><a rel="nofollow" href="http://www.ida.liu.se/~ulfni/lpp/">Logic, Programming and Prolog (2ed)</a></li>
<li><a rel="nofollow" href="http://www.j-paine.org/prolog/mathnotes/files/pms/pms.html">Introduction to Prolog for Mathematicians</a></li>
<li><a rel="nofollow" href="http://www.learnprolognow.org/">Learn Prolog Now!</a></li>
<li><a rel="nofollow" href="http://cs.union.edu/~striegnk/courses/nlp-with-prolog/html/">Natural Language Processing Techniques in Prolog</a></li>
</ul>
<p><strong>PostgreSQL</strong></p>
<ul><a rel="nofollow" href="http://cs.union.edu/~striegnk/courses/nlp-with-prolog/html/"> </a></p>
<li><a rel="nofollow" href="http://www.commandprompt.com/ppbook/">Practical PostgreSQL</a></li>
</ul>
<p><strong>Python</strong></p>
<ul>
<li><a rel="nofollow" href="http://diveintopython.org/">Dive Into Python</a></li>
<li><a rel="nofollow" href="http://diveintopython3.org/">Dive Into Python 3</a></li>
<li><a rel="nofollow" href="http://www.swaroopch.com/notes/Python">Byte of Python</a></li>
<li><a rel="nofollow" href="http://homepage.mac.com/s_lott/books/python.html">Building Skills in Python Version 2.5</a></li>
<li><a rel="nofollow" href="http://linkmingle.com/list/List-of-Free-Online-Python-Books-freebooksandarticles">Python Free Online Ebooks</a> <strong>Meta-List</strong></li>
<li><a rel="nofollow" href="http://openbookproject.net/pybiblio/">Python Bibliotheca</a></li>
<li><a rel="nofollow" href="http://www.greenteapress.com/thinkpython/thinkpython.pdf">Think Python</a></li>
<li><a rel="nofollow" href="http://www.brpreiss.com/books/opus7/html/book.html">Data Structures and Algorithms in Python</a></li>
<li><a rel="nofollow" href="http://www.greenteapress.com/thinkpython/thinkCSpy/">How to Think Like a Computer Scientist: Learning with Python</a></li>
<li><a rel="nofollow" href="http://www.openbookproject.net/py4fun/">Python for Fun</a></li>
<li><a rel="nofollow" href="http://inventwithpython.com/">Invent Your Own Computer Games With Python</a></li>
<li><a rel="nofollow" href="http://www.mindview.net/Books/TIPython/">Thinking in Python</a></li>
<li><a rel="nofollow" href="http://djangobook.com/">The Django Book</a></li>
<li><a title="SWFK" rel="nofollow" href="http://www.briggs.net.nz/log/writing/snake-wrangling-for-kids/">Snake Wrangling For Kids</a></li>
</ul>
<p><strong>Ruby</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.ruby-doc.org/docs/ProgrammingRuby/">Programming Ruby</a></li>
<li><a rel="nofollow" href="http://mislav.uniqpath.com/poignant-guide/">Why&#8217;s (Poignant) Guide to Ruby</a> (<a rel="nofollow" href="http://www.scribd.com/doc/2236084/Whys-Poignant-Guide-to-Ruby">Mirror</a> via <a rel="nofollow" href="http://www.scribd.com/">Scribd</a>)</li>
<li><a rel="nofollow" href="http://www.humblelittlerubybook.com/">Mr. Neighborly&#8217;s Humble Little Ruby Book</a></li>
<li><a rel="nofollow" href="http://rubybestpractices.com/">Ruby Best Practices</a></li>
<li><a rel="nofollow" href="http://macruby.labs.oreilly.com/">MacRuby: The Definitive Guide</a></li>
<li><a rel="nofollow" href="http://www.railstutorial.org/">Ruby on Rails Tutorial: Learn Rails By Example</a></li>
</ul>
<p><strong>Scala</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.scala-lang.org/docu/files/ScalaTutorial.pdf">A Scala Tutorial for Java programmers</a></li>
<li><a rel="nofollow" href="http://www.scala-lang.org/docu/files/ScalaByExample.pdf">Scala By Example</a></li>
<li><a rel="nofollow" href="http://programming-scala.labs.oreilly.com/index.html">Programing Scala</a></li>
<li><a rel="nofollow" href="http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/">Xtrace</a> (Github)</li>
<li><a rel="nofollow" href="http://github.com/tjweir/liftbook">List</a> (Github)</li>
<li><a rel="nofollow" href="http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/">Pro Scala: Monadic Design Patterns for the Web</a></li>
<li><a rel="nofollow" href="http://github.com/tjweir/liftbook">Exploring Lift</a> (published earlier as &#8220;The Definitive Guide to Lift&#8221;, <a rel="nofollow" href="http://groups.google.com/group/the-lift-book">pdf</a>)</li>
</ul>
<p><strong>Scheme</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.scheme.com/tspl4/">The Scheme Programming Language (Edition 4)</a></li>
</ul>
<p><strong>SmallTalk</strong></p>
<ul>
<li><a rel="nofollow" href="http://stephane.ducasse.free.fr/FreeBooks.html">SmallTalk Free Ebooks</a> <em>Courtesy of Stéphane Ducasse</em></li>
<li><a rel="nofollow" href="http://www.squeakbyexample.org/">Squeak By Example</a> (Smalltalk IDE)</li>
</ul>
<p><strong>Subversion</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.phptr.com/content/images/0131855182/downloads/Nagel_book.pdf">Subversion Version Control: Using the Subversion Version Control System in Development Projects</a></li>
</ul>
<p><strong>*<em>SQL (Implementation agnostic) *</em> </strong></p>
<li><a rel="nofollow" href="http://www.cs.arizona.edu/people/rts/publications.html">Developing Time-Oriented Database Applications in SQL</a>,Richard T. Snodgrass</li>
<p><strong>Vim</strong></p>
<ul>
<li><a rel="nofollow" href="http://www.swaroopch.com/notes/Vim">A Byte of Vim</a></li>
</ul>
<p>你有和我们分享的计算机电子书列表吗？欢迎在回复中和我们分享。<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/3270.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg" alt="两本电子书" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3270.html" class="wp_rp_title">两本电子书</a></li><li ><a href="https://coolshell.cn/articles/3192.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg" alt="一些非常不错的资料" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3192.html" class="wp_rp_title">一些非常不错的资料</a></li><li ><a href="https://coolshell.cn/articles/5479.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png" alt="给程序员的VIM速查卡" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5479.html" class="wp_rp_title">给程序员的VIM速查卡</a></li><li ><a href="https://coolshell.cn/articles/4710.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="Python 和 PyGame 的一些示例" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4710.html" class="wp_rp_title">Python 和 PyGame 的一些示例</a></li><li ><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg" alt="一些有意思的文章和资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4220.html" class="wp_rp_title">一些有意思的文章和资源</a></li><li ><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="一些有意思的贴子和工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_title">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/2775.html">免费电子书列表</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/2775.html/feed</wfw:commentRss>
			<slash:comments>31</slash:comments>
		
		
			</item>
		<item>
		<title>最佳编程语录</title>
		<link>https://coolshell.cn/articles/2753.html</link>
					<comments>https://coolshell.cn/articles/2753.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Mon, 02 Aug 2010 00:15:45 +0000</pubDate>
				<category><![CDATA[技术读物]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Bjarne Stroustrup]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Edsger Dijkstra]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=2753</guid>

					<description><![CDATA[<p>以前本站发布过《22条经典的编程引言》、《编程引言补充》、《Linus Torvalds 语录》还有《十条不错的编程观点》。今天向大家介绍“最佳编程语录”，条条...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/2753.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/2753.html">最佳编程语录</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>以前本站发布过《<a rel="bookmark" href="https://coolshell.cn/articles/808.html" target="_blank">22条经典的编程引言</a>》、《<a rel="bookmark" href="https://coolshell.cn/articles/1212.html" target="_blank">编程引言补充</a>》、《<a rel="bookmark" href="https://coolshell.cn/articles/1278.html" target="_blank">Linus Torvalds 语录</a>》还有《<a href="https://coolshell.cn/articles/2424.html" target="_blank">十条不错的编程观点</a>》。今天向大家介绍“最佳编程语录”，条条都是很不错的语录，如同我们的太阳，照亮了我们的方向（所以我们选用了一个红色的图片，希望能够通过五毛们的网络审查）。<img decoding="async" loading="lazy" class="alignright size-medium wp-image-2755" title="Best Programming Quotations" src="https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-201x300.jpg" alt="" width="201" height="300" srcset="https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-201x300.jpg 201w, https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-181x270.jpg 181w, https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations.jpg 335w" sizes="(max-width: 201px) 100vw, 201px" />其中只有一两条在以前本站发布过的文章中出现过。这篇<a href="http://www.linfo.org/q_programming.html" target="_blank"><strong>文章的出处在这里</strong></a>，下面是“<a href="https://coolshell.cn/?author=4" target="_blank">Neo</a>”和“<a href="https://coolshell.cn/?author=2" target="_blank">陈皓</a>”的翻译，我们的翻译水平有限，所以，我们提供了中英文对照，有不当之处，还请各位指正。</p>
<blockquote><p>A good programmer is someone who looks both ways before crossing a one-way street.   &#8212; Doug Linder, systems administrator</p>
<p>好的程序员这样一类人，这类人在横穿一条单行道前都要先看一下路两边。&#8211; Doug Linder, 系统管理员</p></blockquote>
<blockquote><p>A most important, but also most elusive, aspect of any tool is its influence on the habits of those who train themselves in its use. If the tool is a programming language this influence is, whether we like it or not, an influence on our thinking habits.   &#8212; Edsger Dijkstra, computer scientist</p>
<p>关于工具，一个最重要的，也是最不易察觉的方面是，工具对使用此工具的人的习惯的潜移默化的影响。如果这个工具是一门程序语言，不管我们是否喜欢它，它都会影响我们的思维惯式。 &#8211;Edsger Dijkstra, 著名的计算机科学家。</p></blockquote>
<blockquote><p>Being abstract is something profoundly different from being vague&#8230; The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.   &#8212; Edsger Dijkstra</p>
<p>抽象和模糊完全地不同，抽象的目的并不是把事情变模糊，而去创建一个新的语义层，在那里是绝对精确的描述。 &#8212; Edsger Dijkstra</p></blockquote>
<blockquote><p>Besides a mathematical inclination, an exceptionally good mastery of one&#8217;s native tongue is the most vital asset of a competent programmer.   &#8212; Edsger Dijkstra</p>
<p>除了数学爱好，对于一个有能力的程序员来说，出色地掌握自己的母语是最宝贵的财富。&#8211; Edsger Dijkstra</p></blockquote>
<p><span id="more-2753"></span></p>
<blockquote><p>C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows away your whole leg.   &#8212; Bjarne Stroustrup, developer of the C++ programming language</p>
<p>C很容易使你搬起石头砸自己的脚，而C++把这事变得更难，但是如果一定要这么做，那么你的整条腿都会被炸飞 -Bjarne Stroustrup, C++语言的发明者</p></blockquote>
<blockquote><p>Commentary: most debugging problems are fixed easily; identifying the location of the problem is hard.   &#8212; unknown</p>
<p>修复bug很容易，但是定位bug却很困难 &#8211; 匿名</p></blockquote>
<blockquote><p>Considering the current sad state of our computer programs, software development is clearly still a black art, and cannot yet be called an engineering discipline.   &#8212; Bill Clinton, former President of the United States</p>
<p>看看当前计算机程序糟糕的事态，软件开发明显一直是一门妖术，其仍然不能被称为一个工程学。 &#8211;比尔.克林顿 美国前总统</p></blockquote>
<blockquote><p>For a long time it puzzled me how something so expensive, so leading edge, could be so useless, and then it occurred to me that a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are, in short, a perfect match.   &#8212; Bill Bryson, author, from Notes from a Big Country</p>
<p>长期以来，有个事一直困扰着我，那就是越是昂贵的，越是前沿的，就越可能是没用的。然后，困扰我的另一个事是，计算机是一个死的机器，却可以不可思议地去完成那些巧妙的事情，而计算机程序员是那么聪明人却在做着不可思议的愚蠢的事情，简而言之，他们真是天生的一对。&#8211; Bill Bryson旅游文学作家 Big Country中的笔记</p></blockquote>
<blockquote><p>Given enough eyeballs, all bugs are shallow (e.g., given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone).   &#8212; Eric S. Raymond, programmer and advocate of open source software, from The Cathedral and the Bazaar</p>
<p>足够多的眼睛，就可让所有问题浮现(比如：只要给于足够多的beta测试者和开发人员一起工作，那么，几所所有的问题都会很快的出现，而修正也会是显而易见的）</p></blockquote>
<blockquote><p>Good code is its own best documentation. As you&#8217;re about to add a comment, ask yourself, &#8216;How can I improve the code so that this comment isn&#8217;t needed?&#8217; Improve the code and then document it to make it even clearer.   &#8212; Steve McConnell, software engineer and author, from Code Complete</p>
<p>好的代码自己本身就是最好的文档。当你打算加注释的时候，问问自己‘我如何才能把我的代码改善到不需增加注释？’重构自己的代码，然后使文档让其更清楚。 &#8212; Steve McConnell《代码大全》的作者</p></blockquote>
<blockquote><p>Hey! It compiles! Ship it!   &#8212; unknown</p>
<p>嘿，编译通过了！出货！&#8211;匿名</p></blockquote>
<blockquote><p>Inside every well-written large program is a well-written small program.   &#8212; Charles Antony Richard Hoare, computer scientist</p>
<p>在每个编写精良的大程序里面都是一个编写精良的小程序。 &#8211;Charles Antony Richard Hoare,计算机科学家</p></blockquote>
<blockquote><p>It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. Basic professional ethics would instead require him to write a DestroyCity procedure, to which Baghdad could be given as a parameter.   &#8212; Nathaniel S. Borenstein, computer scientist</p>
<p>需要注意的是，没有哪个经过规范培训的工程师会赞成写一个DestoryBaghdad（摧毁巴克达）的函数。最基本的职业规范会告诉他们应该去写一个叫DestoryCity的函数，然后把“Baghdad”（巴克达）当成这个函数的参数。——　Nathaniel S. Borenstein,　计算机科学家</p></blockquote>
<blockquote><p>Managing programmers is like herding cats.   &#8212; unknown</p>
<p>管理程序员就如同养一群猫一样 &#8211;匿名</p></blockquote>
<blockquote><p>Measuring programming progress by lines of code is like measuring aircraft building progress by weight.   &#8212; Bill Gates, co-founder of Microsoft Corporation</p>
<p>用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造进度一样。——Bill Gates，微软创始人</p></blockquote>
<blockquote><p>More good code has been written in languages denounced as bad than in languages proclaimed wonderful &#8212; much more.   &#8212; Bjarne Stroustrup, from The Design and Evolution of C++</p>
<p>更多的优秀代码是用被认为很烂的语言写成的，而不是用那些被说的好的不得了的语言。——Bjarne Stroustrup, 摘自《The Design and Evolution of C++》</p></blockquote>
<blockquote><p>Programs must be written for people to read, and only incidentally for machines to execute.   &#8212; Harold Abelson and Gerald Jay Sussman, computer scientists and authors, from The Structure and Interpretation of Computer Programs</p>
<p>代码应该是写给其他人来读的，而能让机器运行的仅仅是附带着的。——　Harold Abelson 与 Gerald Jay Sussman, 计算机科学家和作家，摘自《The Structure and Interpretation of Computer Programs》</p></blockquote>
<blockquote><p>Real programmers don&#8217;t comment their code. If it was hard to write, it should be hard to understand.   &#8212; unknown</p>
<p>真正程序员从来不写代码的注释，如果代码非常难写，那么同样代码的注释也会非常难懂 &#8211;匿名</p></blockquote>
<blockquote><p>Simplicity is prerequisite for reliability.   &#8212; Edsger Dijkstra</p>
<p>简单是可靠的前提条件 &#8212; 迪杰斯特拉</p></blockquote>
<blockquote><p>The C programming language &#8212; a language which combines the flexibility of assembly language with the power of assembly language.   &#8212; unknown</p>
<p>C语言—— 一门同时具有了汇编语言灵活性和汇编语言强大能力的语言。&#8211; 匿名</p></blockquote>
<blockquote><p>The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.   &#8212; Tom Cargill, object-oriented programming expert at Bell Labs</p>
<p>开始的90%的代码用了90%的开发时间，而剩下的最后的10%的代码会需要另外90%的开发时间。&#8211; Tom Cargill,贝尔实验室的面向对象编程专家。</p></blockquote>
<blockquote><p>The important point is that the cost of adding a feature isn&#8217;t just the time it takes to code it. The cost also includes the addition of an obstacle to future expansion. Sure, any given feature list can be implemented, given enough coding time. But in addition to coming out late, you will usually wind up with a codebase that is so fragile that new ideas that should be dead-simple wind up taking longer and longer to work into the tangled existing web. The trick is to pick the features that don&#8217;t fight each other.   &#8212; John Carmack, computer game programmer</p>
<p>增加一个功能特性的成本并不单单是为这些功能编码所花费时间的成本，还这个成本应该包括特性扩展的障碍成本。当然，任何的功能清单都可以被实现，只需要有足够的时间。但是除些之外，你应该对你的代码库的脆弱性感到紧张，而那些新的想法应该足够的简单，而不是去花费更多更多的时间去纠缠于现有的蜘蛛网。这里的决窃是挑选那些不会和别人冲突的的功能。</p></blockquote>
<blockquote><p>The key to performance is elegance, not battalions of special cases. The terrible temptation to tweak should be resisted unless the payoff is really noticeable.   &#8212; Jon Bently and M. Douglas McIlroy, both computer scientists at Bell Labs</p>
<p>表现的关键是精美和典雅的，并不是使用大量的特殊案例。对于任何调整的冲动都应该是被限制的，除非其回报真的是值得注意的。&#8211; Jon Bently and M. Douglas McIlroy,  二者都是贝尔试验实的计算机科学家</p></blockquote>
<blockquote><p>The last good thing written in C was Franz Schubert&#8217;s Symphony Number 9.   &#8212; Erwin Dieterich, programmer<br />
最后一件用C做的好作品就是弗朗茨.舒伯特的C大调第9交响曲 &#8212; Erwin Dieterich, programmer程序员</p></blockquote>
<blockquote><p>The problem with using C++ &#8230; is that there&#8217;s already a strong tendency in the language to require you to know everything before you can do anything.   &#8212; Larry Wall, developer of the Perl language</p>
<p>使用C++最大的问题是..在C++语言里，存在这一种很强的趋势，就是如果你不明白C++语言的细节，你就无法做好任何事情。&#8211; Larry Wall, developer of the Perl language</p></blockquote>
<blockquote><p>The sooner you start to code, the longer the program will take.   &#8212; Roy Carlson, University of Wisconsin</p>
<p>你越早开始都手编码，你所花费来编程的时间就越长 &#8212; Roy Carlson, University of Wisconsin</p></blockquote>
<blockquote><p>The value of a prototype is in the education it gives you, not in the code itself.   &#8212; Alan Cooper, software author, from The Inmates are Running the Asylum</p>
<p>原型的价值在于他给你的教训，而不是代码自身 &#8212; Alan Cooper, software author, from The Inmates are Running the Asylum</p></blockquote>
<blockquote><p>There are only two kinds of programming languages: those people always bitch about and those nobody uses.   &#8212; Bjarne Stroustrup</p>
<p>世界上只有两类编程语言：人们都抱怨的语言和从来没有人使用的语言 &#8212; Bjarne Stroustrup</p></blockquote>
<blockquote><p>There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.   &#8212; Charles Antony Richard Hoare</p>
<p>世界上有两个设计软件的方法，一种方法是设计的尽量简单，以至于明显的没有什么缺陷，另外一种方式是使他尽量的复杂，以至于其缺陷不那么明显。</p></blockquote>
<blockquote><p>Ugly programs are like ugly suspension bridges: they&#8217;re much more liable to collapse than pretty ones, because the way humans (especially engineer-humans) perceive beauty is intimately related to our ability to process and understand complexity. A language that makes it hard to write elegant code makes it hard to write good code.   &#8212; Eric S. Raymond</p>
<p>丑陋的程序就像一座丑陋的吊桥：他们相比漂亮的良好的吊桥起来，更有可能会坍塌，这是因为人类（尤其是工程师）感知漂亮的东西是和我们处理和理解复杂问题的能力相关的。所以，一个程序语言如果很难以优雅地方式编程，那么其就很难写出好的代码。</p></blockquote>
<blockquote><p>Weeks of programming can save you hours of planning.   &#8212; unknown</p>
<p>多做几周的编程可以节省你做计划的时间 —— 匿名 （意思为，只有实践过了，你才更容易做计划，没有实践过，做起计划来将会很头痛）</p></blockquote>
<blockquote><p>When a programming language is created that allows programmers to program in simple English, it will be discovered that programmers cannot speak English.   &#8212; unknown</p>
<p>当程序语言被设计成允许程序以很简单的英语来编程的时候，人们将会发现编写程序的程序员都来自不会说英语的地方。 &#8211;匿名</p></blockquote>
<p>（全文完，翻译水平有限，如果有误，还请批评指正！）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg" alt="API设计原则 &#8211; Qt官网的设计实践总结" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_title">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href="https://coolshell.cn/articles/5444.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="千万不要把 bool 设计成函数参数" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5444.html" class="wp_rp_title">千万不要把 bool 设计成函数参数</a></li><li ><a href="https://coolshell.cn/articles/5201.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif" alt="重构代码的7个阶段" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5201.html" class="wp_rp_title">重构代码的7个阶段</a></li><li ><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif" alt="一个空格引发的惨剧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_title">一个空格引发的惨剧</a></li><li ><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="如何写出无法维护的代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_title">如何写出无法维护的代码</a></li><li ><a href="https://coolshell.cn/articles/3005.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg" alt="代码重构的一个示例" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3005.html" class="wp_rp_title">代码重构的一个示例</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/2753.html">最佳编程语录</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/2753.html/feed</wfw:commentRss>
			<slash:comments>41</slash:comments>
		
		
			</item>
		<item>
		<title>JAVA的字符串拼接与性能</title>
		<link>https://coolshell.cn/articles/2235.html</link>
					<comments>https://coolshell.cn/articles/2235.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Sun, 28 Mar 2010 01:42:14 +0000</pubDate>
				<category><![CDATA[Java语言]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=2235</guid>

					<description><![CDATA[<p>概述：本文主要研究的是JAVA的字符串拼接的性能，原文中的测试代码在功能上并不等价，导致concat的测试意义不大。不过原作者在评论栏给了新的concat结果，...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/2235.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/2235.html">JAVA的字符串拼接与性能</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>概述：</strong>本文主要研究的是JAVA的字符串拼接的性能，原文中的测试代码在功能上并不等价，导致concat的测试意义不大。不过原作者在评论栏给了新的concat结果，如果有兴趣的同学建议自己修改代码测试。</p>
<p>原文出处:<a href="http://www.venishjoe.net/2009/11/java-string-concatenation-and.html">http://www.venishjoe.net/2009/11/java-string-concatenation-and.html</a></p>
<p>在JAVA中拼接两个字符串的最简便的方式就是使用操作符&#8221;+&#8221;了。如果你用&#8221;+&#8221;来连接固定长度的字符串，可能性能上会稍受影响，但是如果你是在循环中来&#8221;+&#8221;多个串的话，性能将指数倍的下降。假设有一个字符串，我们将对这个字符串做大量循环拼接操作，使用&#8221;+&#8221;的话将得到最低的性能。但是究竟这个性能有多差？如果我们同时也把StringBuffer,StringBuilder或String.concat()放入性能测试中，结果又会如何呢？本文将会就这些问题给出一个答案！<br />
<span id="more-2235"></span></p>
<p>我们将使用<a href="http://perf4j.codehaus.org/index.html">Per4j</a>来计算性能，因为这个工具可以给我们一个完整的性能指标集合，比如最小，最大耗时，统计时间段的标准偏差等。在测试代码中，为了得到一个准确的标准偏差值，我们将执行20个拼接&#8221;*&#8221;50,000次的测试。下面是我们将使用到的拼接字符串的方法：</p>
<ul>
<li>Concatenation Operator (+)</li>
<li>String concat method &#8211; concat(String str)</li>
<li>StringBuffer append method &#8211; append(String str)</li>
<li>StringBuilder append method &#8211; append(String str)</li>
</ul>
<p>最后，我们将看看字节码，来研究这些方法到底是如何执行的。现在，让我们先开始来创建我扪的类。注意为了计算每个循环的性能，代码中的每段测试代码都需要用Per4J库进行封装。首先我们先定义迭代次数</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
private static  final int  OUTER_ITERATION=20;
private static final int INNER_ITERATION=50000;
</pre>
<p>接下来，我们将使用上述4个方法来实现我们的测试代码。</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
  	String addTestStr = &quot;&quot;;
  	String concatTestStr = &quot;&quot;;
  	StringBuffer concatTestSb = null;
  	StringBuilder concatTestSbu = null;
  	 
  	for (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {
  	    StopWatch stopWatch = new LoggingStopWatch(&quot;StringAddConcat&quot;);
  	    addTestStr = &quot;&quot;;
  	    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)
  	    addTestStr += &quot;*&quot;;
  	    stopWatch.stop();
  	}       
  	 
  	for (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {
  	    StopWatch stopWatch = new LoggingStopWatch(&quot;StringConcat&quot;);
  	    concatTestStr = &quot;&quot;;
  	    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)
  	    concatTestStr.concat(&quot;*&quot;);
  	    stopWatch.stop();
  	}
  	 
  	for (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {
  	    StopWatch stopWatch = new LoggingStopWatch(&quot;StringBufferConcat&quot;);
  	    concatTestSb = new StringBuffer();
  	    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)
  	    concatTestSb.append(&quot;*&quot;);
  	    stopWatch.stop();
  	}
  	 
  	for (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {
  	    StopWatch stopWatch = new LoggingStopWatch(&quot;StringBuilderConcat&quot;);
  	    concatTestSbu = new StringBuilder();
  	    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)
  	    concatTestSbu.append(&quot;*&quot;);
  	    stopWatch.stop();
  	}
</pre>
<p>接下来通过运行程序来生成性能指标。我的运行环境是64位的Windown7操作系统，32位的JVM(7-ea) 带4GB内存，双核Quad 2.00GHz的CPU的机器.</p>
<p>经过20次迭代后，我们得到如下的数据：<br />
<img decoding="async" loading="lazy" alt="" src="https://coolshell.cn/wp-content/uploads/2010/03/String_Perf_Chart_217.png" title="结果" class="alignnone" width="586" height="351" /></p>
<p>结果非常完美如我们想象的那样。唯一比较有趣的事情是为什么String.concat也很不错，我们都知道，String是一个常类（初始化后就不会改变的类），那么为什么concat的性能会更好一些呢。(<strong>译者注</strong>：其实原文作者的测试代码有问题，对于concat()方法的测试代码应该写成concatTestStr=concatTestStr.concat(&#8220;*&#8221;)才对。)为了回答这个问题，我们应该看看concat反编译出来的字节码。在本文的下载包里面包含了所有的字节码，但是现在我们先看一下concat的这个代码片段：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
    46:  new #6; //class java/lang/StringBuilder
    49:  dup
    50:  invokespecial   #7; //Method java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V
    53:  aload_1
    54:  invokevirtual   #8; //Method java/lang/StringBuilder.append:
             (Ljava/lang/String;)Ljava/lang/StringBuilder;
    57:  ldc #9; //String *
    59:  invokevirtual   #8; //Method java/lang/StringBuilder.append:
             (Ljava/lang/String;)Ljava/lang/StringBuilder;
    62:  invokevirtual   #10; //Method java/lang/StringBuilder.toString:()
             Ljava/lang/String;
    65:  astore_1
    66:  iinc    7, 1
    69:  goto    38
</pre>
<p>这段代码是String.concat()的字节码，从这段代码中，我们可以清楚的看到，concat()方法使用了StringBuilder，concat()的性能应该和StringBuilder的一样好，但是由于额外的创建StringBuilder和做.append(str).append(str).toString()的操作，使得concate的性能会受到一些影响，所以StringBuilder和String Cancate的时间是1.8和3.3。</p>
<p>因此，即时在做最简单的拼接时，如果我们不想创建StringBuffer或StringBuilder实例使，我们也因该使用concat。但是对于大量的字符串拼接操作，我们就不应该使用concat(<strong>译者注：</strong>因为测试代码功能上并不完全等价，更换后的测试代码concat的平均处理时间是1650.9毫秒。这个结果在原文的评论里面。)，因为concat会降低你程序的性能，消耗你的cpu。因此，在不考虑线程安全和同步的情况下，为了获得最高的性能，我们应尽量使用StringBuilder</p>
<p>本文的源代码，编译目标文件和字节码可以通过下面的这个链接获得：</p>
<p>下载源代码，类和字节码：<a herf="http://www.box.net/shared/hzl48ma8ne">String_Concatenation _Performance.zip</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg" alt="Rust语言的编程范式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_title">Rust语言的编程范式</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/11541.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="面向GC的Java编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11541.html" class="wp_rp_title">面向GC的Java编程</a></li><li ><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="从LongAdder看更高效的无锁实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_title">从LongAdder看更高效的无锁实现</a></li><li ><a href="https://coolshell.cn/articles/11175.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg" alt="Java中的CopyOnWrite容器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11175.html" class="wp_rp_title">Java中的CopyOnWrite容器</a></li><li ><a href="https://coolshell.cn/articles/9703.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg" alt="无锁HashMap的原理与实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9703.html" class="wp_rp_title">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/2235.html">JAVA的字符串拼接与性能</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/2235.html/feed</wfw:commentRss>
			<slash:comments>20</slash:comments>
		
		
			</item>
		<item>
		<title>使用Flex Bison 和LLVM编写自己的编译器</title>
		<link>https://coolshell.cn/articles/1547.html</link>
					<comments>https://coolshell.cn/articles/1547.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Mon, 12 Oct 2009 04:47:18 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编译原理]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1547</guid>

					<description><![CDATA[<p>使用Flex Bison 和 LLVM编写你自己的编译器 原文出处：http://gnuu.org/2009/09/18/writing-your-own-to...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1547.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1547.html">使用Flex Bison 和LLVM编写自己的编译器</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>使用Flex Bison 和 LLVM编写你自己的编译器</strong><br />
原文出处：<a href="http://gnuu.org/2009/09/18/writing-your-own-toy-compiler/">http://gnuu.org/2009/09/18/writing-your-own-toy-compiler</a></p>
<h2> 1、介绍</h2>
<p>我总是对编译器和语言非常感兴趣，但是兴趣并不会让你走的更远。大量的编译器的设计概念可以搞的任何一个程序员迷失在这些概念之中。不用说，我也曾今尝试过，但是并没有取得太大的成功，我以前的尝试都停留在语义分析阶段。本文的灵感主要来源于我最近一次的尝试，并且在这一次中我取得一点成就。</p>
<p>幸运的是，最近的几年，我参加了一些项目，这些项目给了我在建立编译器上很多有用的经验和观点。另外一件事是，我非常幸运得到<a href="http://llvm.org/">LLVM</a>的帮助。对于这个工具，我不知道改怎么去形容它，但是他给我的这个编译器的确带来非常大的帮助。<br />
<span id="more-1547"></span></p>
<h3>1.1、你为什么要阅读本文</h3>
<p>你也许想看看我正在做的事情，但是更有可能的是，你也是和我一样对编译器和语言非常感兴趣，并且也可能遇到了一些在探索的过程中遇到了一些难题，你可能正打算解决这些难题，但是却没有发现好的资源。本文的目标就是提供这些资源，并以一种手把手的方式教你从头到尾的去创建一个具有基本功能的语言编译器。</p>
<p>在本文，我不会去解释一些编译器基本理论，所以你要在开始本文前去了解什么是<a href="http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form">BNF语法</a>，什么是<a href="http://en.wikipedia.org/wiki/Abstract_syntax_tree">抽象语法树数据结构 AST data structure</a>，什么是基础<a href="http://en.wikipedia.org/wiki/Compiler">编译器流水线 complier pipline</a>。就是说，我会把本文描述的尽量简单。本文的目的就是以一种简单易懂的方式来介绍相关编译器资源的方式来帮助那些从来没有编译器经验的人。</p>
<h3>1.2、达到的成果</h3>
<p>如果你根据文章内容一步步来，你将会得到一个能定义函数，调用函数，定义变量，给变量赋值执行基本数学操作的语言。这门语言支持两种基本类型，double和integer类型。还有一些功能还未实现，因此，你可以通过自己去实现这些功能得到你满意的功能并且能为你理解编写一个编译器提供不少的帮助。</p>
<p><h3>1.3 问题解答</h3>
</p>
<h4>1.3.1 我需要了解什么样的语言</h4>
<p>我们使用的工具是基于C/C++的。LLVM是基于C++的，我们的这个语言也基于C++，因为C++具有很多面向对象的优点和可以被重用的STL。此外对于C，Lex和Bison都具有那些初看起来令人迷惑的语法，但是我将尽可能的去解释他。我们需要处理的语法非常小，最多就100行，因此它是比较容易理解的。</p>
<h4>1.3.2 很复杂吗？</h4>
<p>是或否，这里面有很多的东西你需要了解，甚至多的让人感觉到害怕，但是老实说，其实这些都非常简单，我们同样会使用很多工具分解这些层次的复杂性，并使得这些内容可管理。</p>
<h4>1.3.3 完成它需要多长时间</h4>
<p>我们将要完成的编译器花了我三天的时间。但是如果你按“follow me”的方式来完成这个编译器的话，你将会花费更少的时间。如果要全部理解这里面的内容可能会花去稍微长一点的时间，但是你至少应该在一个下午就将整个编译器运行起来。</p>
<p>好，如果你已经准备好，我们开始吧。</p>
<p><h2>2、准备开始</h2>
</p>
<h3>2.1 构成编译器的最基本的要素</h3>
<p>一个编译器是由一组有三个到四个组件(还有一些子组件)构成，数据以管道的方式从一个组件输入并流向下一个组件。在我们这个编译器中，可能会用到一些稍微不同的工具。下面这个图展示了我们构造一个编译器的步骤，和每个步骤中将使用的工具。</p>
<p><img decoding="async" loading="lazy" alt="Compiler Pipeline" src="http://gnuu.org/wp-content/uploads/2009/09/pipeline.png" width="620" height="76" /> </p>
<p>从上图你可以看到在Linking这一步是灰掉的。我们的语言将不支持编译器的连接(很多的语言都不支持编译器的连接)。在文法分析阶段，我们将使用开源工具Lex，即如今的<a href="http://flex.sourceforge.net/">Flex</a>，文法分析一般都伴随者语法分析，我们使用的语法分析工具将会是Yacc，或者说是<a href="http://www.gnu.org/software/bison/">Bison</a>，最后一旦语义分析完成，我们将遍历我们的抽象语法树，并生成我们的&#8221;bytecode 字节码&#8221;，或&#8221;机器码 matchine code&#8221;。做这一步，我们将使用<a href="http://llvm.org/">LLVM</a>，它能生成快速字节码，我们将使用LLVM的JIT(Just In Tinme)来在我们的机器上编译执行它</p>
<p>总结一下，步骤如下：</p>
<ol>
<li><strong>文法分析用<em>Flex</em></strong>:将数据分隔成一个个的标记token (标示符identifiers，关键字keywords，数字numbers, 中括号brackets, 大括号braces, 等等etc.) </li>
<li><strong>语法分析用<em>Bison</em></strong>: 在分析标记的时候生成抽象语法树. Bison 将会做掉几乎所有的这些工作, 我们定义好我们的抽象语法树就OK了. </li>
<li><strong>组装用<em>LLVM</em></strong>: 这里我们将遍历我们的抽象语法树，并未每一个节点生成字节/机器码。 这听起来似乎很疯狂，但是这几乎就是<em>最简单的</em> 一步了. </li>
</ol>
<p>在我们开始下一步之前，你应该准备安装好Flex,Bison和LLVM。因为我们马上就要使用到它们。</p>
<p><h3>2.2 定义我们的语法</h3>
</p>
<p>我们语法是我们语言中最核心的部分，我们的语法使用类似标准C的语法，因为这样的语法非常熟悉，而且简单。我们语法的一个典型的例子如下：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
int do_math(int a) {
  int x = a * 5 + 3
}

do_math(10)
</pre>
<p>看起来很简单。它和C非常相似，但是它没有使用分号做语句的分隔，同时你也会注意到我们的语法中没有return语句。这就是你可以自己实现的部分。</p>
<p>现在我们还没有任何机制来验证结果。我们将通过检查我们编译之后LLVM打印出的字节码验证我们程序的正确性。</p>
<p><h2>3、 第一步，使用Flex进行文法分析 </h2>
</p>
<p>这是最简单的一步，给定语法之后，我们需要将我们的输入转换一系列内部标记token。如前所述，我们的语法具有非常基础的标记token:标示符identifier ，数字常量(整型和浮点型)，数学运算符号，括号，中括号，我们的文法定义文件称为token.l，它具有一些固定的语法。定义如下：</p>
<pre>
%{
#include <string>
#include "node.h"
#include "parser.hpp"
#define SAVE_TOKEN yylval.string = new std::string(yytext, yyleng)
#define TOKEN(t) (yylval.token = t)
extern "C" int yywrap() { }
%}

%%

[ \t\n]                 ;
[a-zA-Z_][a-zA-Z0-9_]*  SAVE_TOKEN; return TIDENTIFIER;
[0-9]+\.[0-9]*          SAVE_TOKEN; return TDOUBLE;
[0-9]+                  SAVE_TOKEN; return TINTEGER;
"="                     return TOKEN(TEQUAL);
"=="                    return TOKEN(TCEQ);
"!="                    return TOKEN(TCNE);
"<"                     return TOKEN(TCLT);
"<="                    return TOKEN(TCLE);
">"                     return TOKEN(TCGT);
">="                    return TOKEN(TCGE);
"("                     return TOKEN(TLPAREN);
")"                     return TOKEN(TRPAREN);
"{"                     return TOKEN(TLBRACE);
"}"                     return TOKEN(TRBRACE);
"."                     return TOKEN(TDOT);
","                     return TOKEN(TCOMMA);
"+"                     return TOKEN(TPLUS);
"-"                     return TOKEN(TMINUS);
"*"                     return TOKEN(TMUL);
"/"                     return TOKEN(TDIV);
.                       printf("Unknown token!\n"); yyterminate();

%%
</pre>
<p>在第一节(译者注：即%{%}中定义的部分)声明了一些特定的C代码。由于Bison不会去访问我门的yytext变量，我们使用宏&#8221;SAVE_TOKEN&#8221;来保证标示符的文本和文本长度是安全的(而不是靠标记本身来保证)。第一个token告诉我们要忽略掉那些空白字符。你会注意到我们有些一些等价性比较的标记和其他。还有一些标记还没有实现，你可以非常自由的将这些标记加到你自己的编译器中去。</p>
<p>现在我们在这里做的是定义这些标记和他们的符号名。符号(比如TIDENTFIER)将成为我们语法中的终结符。我们只是返回它，我们从未定义它，他们是在什么地方定义的？当然是在bison语法文件中。我们包含的parser.hpp头文件将会被bison所生成，并且里面的所有符号都将被生成，并被我们在这里使用。</p>
<p>我们对这个token.l运行flex命令，并生成tokens.cpp文件，这个程序将会和我们的语法分析器一起编译并提供yylex()函数来识别这些标记。我们将在稍后运行这个命令，因为现在我们需要从bison那里生成头文件。</p>
<p><h2>4、第2步 使用Bison进行语法分析</h2>
</p>
<p>这是我们工作中最富有挑战性的一部分。生成一个正确的无二义的语法并不是一项简单的工作，要经过很多实践努力。庆幸的是，我们例子中的语法是简单而完整的。在我们实现我们的语法之前，我们需要详细的讲解一下我们的设计。</p>
<p><h3>4.1、设计AST(抽象语法树)</h3>
</p>
<p>语法分析的最终结果是抽象语法树AST，正如我们将看到的，Bison生成抽象语法树的最优工具；我们唯一需要做的事情就是将我们的代码插入到语法中去。</p>
<p>文本形式字符串，例如&#8221;int x&#8221;代表了我们语言的文本形式，和这个类似，抽象语法树AST则代表了我们语言在内存中的表现形式一样(在语言在组装成而进程码之前)。正因如此，我们要在把这些插入在语法分析中的数据结构首先设计好。这个过程是非常直接的，因为我们为语法中的每个语义单元创建了一个结构。方法声明、方法调用，变量声明，引用，这些都构成了抽象语法树的节点。我们语言的抽象语法树的节点如下图：<br />
<img decoding="async" loading="lazy" border="0" alt="Our Toy Language AST" src="http://gnuu.org/wp-content/uploads/2009/09/ClassDiagram.png" width="640" height="505" /><br />
上图的C++代码如下：<br />
node.h文件</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;llvm/Value.h&gt;

class CodeGenContext;
class NStatement;
class NExpression;
class NVariableDeclaration;

typedef std::vector&lt;NStatement*&gt; StatementList;
typedef std::vector&lt;NExpression*&gt; ExpressionList;
typedef std::vector&lt;NVariableDeclaration*&gt; VariableList;

class Node {
public:
    virtual ~Node() {}
    virtual llvm::Value* codeGen(CodeGenContext&amp; context) { }
};

class NExpression : public Node {
};

class NStatement : public Node {
};

class NInteger : public NExpression {
public:
    long long value;
    NInteger(long long value) : value(value) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NDouble : public NExpression {
public:
    double value;
    NDouble(double value) : value(value) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NIdentifier : public NExpression {
public:
    std::string name;
    NIdentifier(const std::string&amp; name) : name(name) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NMethodCall : public NExpression {
public:
    const NIdentifier&amp; id;
    ExpressionList arguments;
    NMethodCall(const NIdentifier&amp; id, ExpressionList&amp; arguments) :
        id(id), arguments(arguments) { }
    NMethodCall(const NIdentifier&amp; id) : id(id) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NBinaryOperator : public NExpression {
public:
    int op;
    NExpression&amp; lhs;
    NExpression&amp; rhs;
    NBinaryOperator(NExpression&amp; lhs, int op, NExpression&amp; rhs) :
        lhs(lhs), rhs(rhs), op(op) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NAssignment : public NExpression {
public:
    NIdentifier&amp; lhs;
    NExpression&amp; rhs;
    NAssignment(NIdentifier&amp; lhs, NExpression&amp; rhs) :
        lhs(lhs), rhs(rhs) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NBlock : public NExpression {
public:
    StatementList statements;
    NBlock() { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NExpressionStatement : public NStatement {
public:
    NExpression&amp; expression;
    NExpressionStatement(NExpression&amp; expression) :
        expression(expression) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NVariableDeclaration : public NStatement {
public:
    const NIdentifier&amp; type;
    NIdentifier&amp; id;
    NExpression *assignmentExpr;
    NVariableDeclaration(const NIdentifier&amp; type, NIdentifier&amp; id) :
        type(type), id(id) { }
    NVariableDeclaration(const NIdentifier&amp; type, NIdentifier&amp; id, NExpression *assignmentExpr) :
        type(type), id(id), assignmentExpr(assignmentExpr) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

class NFunctionDeclaration : public NStatement {
public:
    const NIdentifier&amp; type;
    const NIdentifier&amp; id;
    VariableList arguments;
    NBlock&amp; block;
    NFunctionDeclaration(const NIdentifier&amp; type, const NIdentifier&amp; id,
            const VariableList&amp; arguments, NBlock&amp; block) :
        type(type), id(id), arguments(arguments), block(block) { }
    virtual llvm::Value* codeGen(CodeGenContext&amp; context);
};

</pre>
<p>非常的清晰明了，我们省略了getter和setter方法，这里只列出了共有成员；这些类也不需要影藏私有数据，并省略了codeGen方法。在我们导出AST成LLVM的字节码时，就需要使用到这个方法。</p>
<p><h3>4.2、Bison介绍</h3>
</p>
<p>bison的语法定义文件同样是由这些标记构成的最复杂的部分。这并不是说技术上有多复杂，但是我也会花一些时间来讨论一下Bison的语法细节，好，现在让我们立刻来熟悉一下Bison的语法。我们将使用基于类似于BNF的语法，使用定义的好终结符和非终结符来组成我们有效的每一个语句和表达式(这些语句和表达式就代表我们之前定义的AST节点)。例如：</p>
<pre>
if_stmt : IF '(' condition ')' block { /* do stuff when this rule is encountered */ }
        | IF '(' condition ')'       { ... }
        ;
</pre>
<p>在上面例子中，我们定义了一个if语句(如果我们支持if语句话)，它和BNF不同之处在于，每个语法后面都跟了一系列动作(在'{&#8216;和&#8217;}&#8217;之间的内容)。这个动作将在此条语法被识别(译者注：归约)的时候被执行。这个过程将会递归地按从叶子符号到根节点符号的次序执行，在这个过程，每一个非终结符最终会被合并为一棵大的语法树。你将会看到的&#8217;$$&#8217;符号代表着当前树的跟节点(译者注：&#8217;$$&#8217;代表本条语法规则中冒号左边的部分的语义内容)。此外&#8217;$1&#8217;代表了本条规则叶子中的第一个符号(译者注：&#8217;$1&#8217;代表了本条语法规则冒号右边的内容，$1代表冒号右边的第一个符号的语义值)。在上面的例子中，当&#8217;condition&#8217;有效时我们将会把$3 赋值给$$。这个例子可以解释如何将我们AST和语法规则关联起来。我们将在每一条规则中通常赋值一个节点到$$，最后这些规则会合并成一个大的抽象语法树。Bison的部分是我们语言最复杂的部分，你需要花一点时间去理解它。此外到此为止，你还没有看到完整的代码。下面就是完整的Bison部分的代码：<br />
parser.y</p>
<pre>
%{
    #include "node.h"
    NBlock *programBlock; /* the top level root node of our final AST */

    extern int yylex();
    void yyerror(const char *s) { printf("ERROR: %s\n", s); }
%}

/* Represents the many different ways we can access our data */
%union {
    Node *node;
    NBlock *block;
    NExpression *expr;
    NStatement *stmt;
    NIdentifier *ident;
    NVariableDeclaration *var_decl;
    std::vector<NVariableDeclaration*> *varvec;
    std::vector<NExpression*> *exprvec;
    std::string *string;
    int token;
}

/* Define our terminal symbols (tokens). This should
   match our tokens.l lex file. We also define the node type
   they represent.
 */
%token <string> TIDENTIFIER TINTEGER TDOUBLE
%token <token> TCEQ TCNE TCLT TCLE TCGT TCGE TEQUAL
%token <token> TLPAREN TRPAREN TLBRACE TRBRACE TCOMMA TDOT
%token <token> TPLUS TMINUS TMUL TDIV

/* Define the type of node our nonterminal symbols represent.
   The types refer to the %union declaration above. Ex: when
   we call an ident (defined by union type ident) we are really
   calling an (NIdentifier*). It makes the compiler happy.
 */
%type <ident> ident
%type <expr> numeric expr
%type <varvec> func_decl_args
%type <exprvec> call_args
%type <block> program stmts block
%type <stmt> stmt var_decl func_decl
%type <token> comparison

/* Operator precedence for mathematical operators */
%left TPLUS TMINUS
%left TMUL TDIV

%start program

%%

program : stmts { programBlock = $1; }
        ;

stmts : stmt { $$ = new NBlock(); $$->statements.push_back($<stmt>1); }
      | stmts stmt { $1->statements.push_back($<stmt>2); }
      ;

stmt : var_decl | func_decl
     | expr { $$ = new NExpressionStatement(*$1); }
     ;

block : TLBRACE stmts TRBRACE { $$ = $2; }
      | TLBRACE TRBRACE { $$ = new NBlock(); }
      ;

var_decl : ident ident { $$ = new NVariableDeclaration(*$1, *$2); }
         | ident ident TEQUAL expr { $$ = new NVariableDeclaration(*$1, *$2, $4); }
         ;

func_decl : ident ident TLPAREN func_decl_args TRPAREN block
            { $$ = new NFunctionDeclaration(*$1, *$2, *$4, *$6); delete $4; }
          ;

func_decl_args : /*blank*/  { $$ = new VariableList(); }
          | var_decl { $$ = new VariableList(); $$->push_back($<var_decl>1); }
          | func_decl_args TCOMMA var_decl { $1->push_back($<var_decl>3); }
          ;

ident : TIDENTIFIER { $$ = new NIdentifier(*$1); delete $1; }
      ;

numeric : TINTEGER { $$ = new NInteger(atol($1->c_str())); delete $1; }
        | TDOUBLE { $$ = new NDouble(atof($1->c_str())); delete $1; }
        ;

expr : ident TEQUAL expr { $$ = new NAssignment(*$<ident>1, *$3); }
     | ident TLPAREN call_args TRPAREN { $$ = new NMethodCall(*$1, *$3); delete $3; }
     | ident { $<ident>$ = $1; }
     | numeric
     | expr comparison expr { $$ = new NBinaryOperator(*$1, $2, *$3); }
     | TLPAREN expr TRPAREN { $$ = $2; }
     ;

call_args : /*blank*/  { $$ = new ExpressionList(); }
          | expr { $$ = new ExpressionList(); $$->push_back($1); }
          | call_args TCOMMA expr  { $1->push_back($3); }
          ;

comparison : TCEQ | TCNE | TCLT | TCLE | TCGT | TCGE
           | TPLUS | TMINUS | TMUL | TDIV
           ;

%%
</pre>
<p><h2>5、生成Flex和Bison代码</h2>
</p>
<p>现在我们有了Flex的token.l文件和Bison的parser.y文件。我们需要将这两个文件传递给工具，并由工具来生成c++代码文件。注意Bison同时会为Flex生成parser.hpp头文件；这样做是通过-d开关实现的，这个开关是的我们的标记声明和源文件分开，这样就是的我们可以让这些token标记被其他的程序使用。下面的命令创建parser.cpp，parser.hpp和tokens.cpp源文件。</p>
<pre>
$ bison -d -o parser.cpp parser.y
$ lex -o tokens.cpp tokens.l
</pre>
<p>如果上述工作都没有出错的话，我们现在位置已经完成了我们编译器工作总量的2/3。如果你现在想测试一下我们的代码，你可以编译一个简单的main.cpp程序：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &lt;iostream&gt;
#include &quot;node.h&quot;
extern NBlock* programBlock;
extern int yyparse();

int main(int argc, char **argv)
{
    yyparse();
    std::cout &lt;&lt; programBlock &lt;&lt; endl;
    return 0;
}
</pre>
<p>你可以编译这些文件：<br />
$ g++ -o parser parser.cpp tokens.cpp main.cpp<br />
现在你需要安装LLVM了，因为llvm::Value被node.h引用了。如果你不想这么做，只是想测试一下Flex和Bison部分，你可以注释掉node.h中codeGen()方法。</p>
<p>如果上述工作都完成了，你现在将有一个语法分析器，这个语法分析器将从标准输入读入，并打出在内存中代表抽象语法树跟节点的内存非零地址。</p>
<h2>6、组装AST和LLVM</h2>
<p>编译器的下一步很自然地是应该将AST转换成机器码。这意味着将每一个语义节点转换成等价的机器指令。LLVM将帮助我们把这步变得非常简单，因为LLVM将真实的指令抽象成类似AST的指令。这意味着我们真正要做的事就是将AST转换成抽象指令。<br />
你可以想象这个过程是从抽象语法树的根节点开始遍历每一个树上节点并产生字节码的过程。现在就是使用我们在Node中定义的codeGen方法的时候了。例如，当我们遍历NBlock代码的时候(语义上NBlock代表一组我们语言的语句的集合)，我们将调用列表中每条语句的codeGen方法。上面步骤代码类似如下的形式：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
Value* NBlock::codeGen(CodeGenContext&amp; context)
{
    StatementList::const_iterator it;
    Value *last = NULL;
    for (it = statements.begin(); it != statements.end(); it++) {
        std::cout &lt;&lt; &quot;Generating code for &quot; &lt;&lt; typeid(**it).name() &lt;&lt; endl;
        last = (**it).codeGen(context);
    }
    std::cout &lt;&lt; &quot;Creating block&quot; &lt;&lt; endl;
    return last;
}
</pre>
<p>我们将实现抽象语法树上所有节点的codeGen方法，然后在向下遍历树的时候调用它，并隐式的遍历我们整个抽象语法树。在这个过程中，我们在CodeGenContext类来告诉我们生成字节码的位置。</p>
<p><h3> 6.1、关于LLVM要注意的一些信息</h3>
</p>
<p>LLVM最大的一个确定就是，你很难找到LLVM的相关文档。在线手册、教程、或其他的文档都没有及时的得到相关维护，这些文档大部分都是过期的文档。除非你去深入研究，否则你很难找到关于C++ API的信息。如果你自己安装LLVM，docs<br />
是最新的文档。</p>
<p>我发现最好学习LLVM的方法就是通过LLVM的例子去学习。在LLVM的压缩包的&#8217;example&#8217;目录下有很多快速生成字节码的例子。在<a href="http://llvm.org/demo/">LLVM demo site</a>上可以将C做输入，然后生成C++ API的例子。以这些例子提供的方法，我找到了类似于int x = 5 ;的指令的生成方法。我使用这些工具实现大部分的节点。</p>
<p>关于LLVM的问题描述到此为止，我将在下面罗列出codegen.h和codegen.cpp的源代码</p>
<p>codegen.h的内容。</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &lt;stack&gt;
#include &lt;llvm/Module.h&gt;
#include &lt;llvm/Function.h&gt;
#include &lt;llvm/PassManager.h&gt;
#include &lt;llvm/CallingConv.h&gt;
#include &lt;llvm/Bitcode/ReaderWriter.h&gt;
#include &lt;llvm/Analysis/Verifier.h&gt;
#include &lt;llvm/Assembly/PrintModulePass.h&gt;
#include &lt;llvm/Support/IRBuilder.h&gt;
#include &lt;llvm/ModuleProvider.h&gt;
#include &lt;llvm/ExecutionEngine/GenericValue.h&gt;
#include &lt;llvm/ExecutionEngine/JIT.h&gt;
#include &lt;llvm/Support/raw_ostream.h&gt;

using namespace llvm;

class NBlock;

class CodeGenBlock {
public:
    BasicBlock *block;
    std::map&lt;std::string , Value*&gt; locals;
};

class CodeGenContext {
    std::stack&lt;codegenblock  *&gt; blocks;
    Function *mainFunction;

public:
    Module *module;
    CodeGenContext() { module = new Module(&quot;main&quot;); }

    void generateCode(NBlock&amp; root);
    GenericValue runCode();
    std::map&lt;std::string , Value*&gt;&amp; locals() { return blocks.top()-&gt;locals; }
    BasicBlock *currentBlock() { return blocks.top()-&gt;block; }
    void pushBlock(BasicBlock *block) { blocks.push(new CodeGenBlock()); blocks.top()-&gt;block = block; }
    void popBlock() { CodeGenBlock *top = blocks.top(); blocks.pop(); delete top; }
};
</pre>
<p>codegen.cpp的内容。</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &quot;node.h&quot;
#include &quot;codegen.h&quot;
#include &quot;parser.hpp&quot;

using namespace std;

/* Compile the AST into a module */
void CodeGenContext::generateCode(NBlock&amp; root)
{
    std::cout &lt;&lt; &quot;Generating code...\n&quot;;

    /* Create the top level interpreter function to call as entry */
    vector&lt;const type*&gt; argTypes;
    FunctionType *ftype = FunctionType::get(Type::VoidTy, argTypes, false);
    mainFunction = Function::Create(ftype, GlobalValue::InternalLinkage, &quot;main&quot;, module);
    BasicBlock *bblock = BasicBlock::Create(&quot;entry&quot;, mainFunction, 0);

    /* Push a new variable/block context */
    pushBlock(bblock);
    root.codeGen(*this); /* emit bytecode for the toplevel block */
    ReturnInst::Create(bblock);
    popBlock();

    /* Print the bytecode in a human-readable format
       to see if our program compiled properly
     */
    std::cout &lt;&lt; &quot;Code is generated.\n&quot;;
    PassManager pm;
    pm.add(createPrintModulePass(&amp;outs()));
    pm.run(*module);
}

/* Executes the AST by running the main function */
GenericValue CodeGenContext::runCode() {
    std::cout &lt;&lt; &quot;Running code...\n&quot;;
    ExistingModuleProvider *mp = new ExistingModuleProvider(module);
    ExecutionEngine *ee = ExecutionEngine::create(mp, false);
    vector&lt;genericvalue&gt; noargs;
    GenericValue v = ee-&gt;runFunction(mainFunction, noargs);
    std::cout &lt;&lt; &quot;Code was run.\n&quot;;
    return v;
}

/* Returns an LLVM type based on the identifier */
static const Type *typeOf(const NIdentifier&amp; type)
{
    if (type.name.compare(&quot;int&quot;) == 0) {
        return Type::Int64Ty;
    }
    else if (type.name.compare(&quot;double&quot;) == 0) {
        return Type::FP128Ty;
    }
    return Type::VoidTy;
}

/* -- Code Generation -- */

Value* NInteger::codeGen(CodeGenContext&amp; context)
{
    std::cout &lt;&lt; &quot;Creating integer: &quot; &lt;&lt; value &lt;&lt; endl;
    return ConstantInt::get(Type::Int64Ty, value, true);
}

Value* NDouble::codeGen(CodeGenContext&amp; context)
{
    std::cout &lt;&lt; &quot;Creating double: &quot; &lt;&lt; value &lt;&lt; endl;
    return ConstantFP::get(Type::FP128Ty, value);
}

Value* NIdentifier::codeGen(CodeGenContext&amp; context)
{
    std::cout &lt;&lt; &quot;Creating identifier reference: &quot; &lt;&lt; name &lt;&lt; endl;
    if (context.locals().find(name) == context.locals().end()) {
        std::cerr &lt;&lt; &quot;undeclared variable &quot; &lt;&lt; name &lt;&lt; endl;
        return NULL;
    }
    return new LoadInst(context.locals()[name], &quot;&quot;, false, context.currentBlock());
}

Value* NMethodCall::codeGen(CodeGenContext&amp; context)
{
    Function *function = context.module-&gt;getFunction(id.name.c_str());
    if (function == NULL) {
        std::cerr &lt;&lt; &quot;no such function &quot; &lt;&lt; id.name &lt;&lt; endl;
    }
    std::vector&lt;value *&gt; args;
    ExpressionList::const_iterator it;
    for (it = arguments.begin(); it != arguments.end(); it++) {
        args.push_back((**it).codeGen(context));
    }
    CallInst *call = CallInst::Create(function, args.begin(), args.end(), &quot;&quot;, context.currentBlock());
    std::cout &lt;&lt; &quot;Creating method call: &quot; &lt;&lt; id.name &lt;&lt; endl;
    return call;
}

Value* NBinaryOperator::codeGen(CodeGenContext&amp; context)
{
    std::cout &lt;&lt; &quot;Creating binary operation &quot; &lt;&lt; op &lt;&lt; endl;
    Instruction::BinaryOps instr;
    switch (op) {
        case TPLUS:     instr = Instruction::Add; goto math;
        case TMINUS:    instr = Instruction::Sub; goto math;
        case TMUL:      instr = Instruction::Mul; goto math;
        case TDIV:      instr = Instruction::SDiv; goto math;

        /* TODO comparison */
    }

    return NULL;
math:
    return BinaryOperator::Create(instr, lhs.codeGen(context),
        rhs.codeGen(context), &quot;&quot;, context.currentBlock());
}

Value* NAssignment::codeGen(CodeGenContext&amp; context)
{
    std::cout &lt;&lt; &quot;Creating assignment for &quot; &lt;&lt; lhs.name &lt;&lt; endl;
    if (context.locals().find(lhs.name) == context.locals().end()) {
        std::cerr &lt;&lt; &quot;undeclared variable &quot; &lt;&lt; lhs.name &lt;&lt; endl;
        return NULL;
    }
    return new StoreInst(rhs.codeGen(context), context.locals()[lhs.name], false, context.currentBlock());
}

Value* NBlock::codeGen(CodeGenContext&amp; context)
{
    StatementList::const_iterator it;
    Value *last = NULL;
    for (it = statements.begin(); it != statements.end(); it++) {
        std::cout &lt;&lt; &quot;Generating code for &quot; &lt;&lt; typeid(**it).name() &lt;&lt; endl;
        last = (**it).codeGen(context);
    }
    std::cout &lt;&lt; &quot;Creating block&quot; &lt;&lt; endl;
    return last;
}

Value* NExpressionStatement::codeGen(CodeGenContext&amp; context)
{
    std::cout &lt;&lt; &quot;Generating code for &quot; &lt;&lt; typeid(expression).name() &lt;&lt; endl;
    return expression.codeGen(context);
}

Value* NVariableDeclaration::codeGen(CodeGenContext&amp; context)
{
    std::cout &lt;&lt; &quot;Creating variable declaration &quot; &lt;&lt; type.name &lt;&lt; &quot; &quot; &lt;&lt; id.name &lt;&lt; endl;
    AllocaInst *alloc = new AllocaInst(typeOf(type), id.name.c_str(), context.currentBlock());
    context.locals()[id.name] = alloc;
    if (assignmentExpr != NULL) {
        NAssignment assn(id, *assignmentExpr);
        assn.codeGen(context);
    }
    return alloc;
}

Value* NFunctionDeclaration::codeGen(CodeGenContext&amp; context)
{
    vector&lt;const type*&gt; argTypes;
    VariableList::const_iterator it;
    for (it = arguments.begin(); it != arguments.end(); it++) {
        argTypes.push_back(typeOf((**it).type));
    }
    FunctionType *ftype = FunctionType::get(typeOf(type), argTypes, false);
    Function *function = Function::Create(ftype, GlobalValue::InternalLinkage, id.name.c_str(), context.module);
    BasicBlock *bblock = BasicBlock::Create(&quot;entry&quot;, function, 0);

    context.pushBlock(bblock);

    for (it = arguments.begin(); it != arguments.end(); it++) {
        (**it).codeGen(context);
    }

    block.codeGen(context);
    ReturnInst::Create(bblock);

    context.popBlock();
    std::cout &lt;&lt; &quot;Creating function: &quot; &lt;&lt; id.name &lt;&lt; endl;
    return function;
}
</pre>
<p>上述罗列很多的代码，然而这部份代码的含义需要你自己去探索。我在这里只会提及一下你需要注意的内容：</p>
<ul>
<li>我们在CodeGenContext类中使用一个语句块的栈来保存最后进入的block(因为语句都被增加到blocks中)
<li>我们同样用个堆栈来保存每组语句块中的<a href="http://en.wikipedia.org/wiki/Symbol_table">符号表</a>  </li>
<li>我们设计的语言只会知道他当前范围内的内容.要支持“全局”上下的做法，你必须向上搜索整个堆栈中每一个语句块，知道你最后发现你匹配的符号(现在我们只是简单地搜索堆栈中最顶层的符号表)。 </li>
<li>在我们进入一个语句块之前，我们需要将语句块压栈，离开语句块时将语句块出栈 </li>
</ul>
<p>剩下的内容都LLVM相关了，在这个主题上我并不是专家。但是迄今为止，我们已经有了编译和运行我们语言的所有代码。</p>
<p><h2>7、编译和运行我们的语言</h2>
</p>
<p><h3>7.1、编译我们的语言</h3>
</p>
<p>我们已经有了代码，现在我们怎么运行它？LLVM有着非常复杂的联接link，幸运的是，如果你是自己安装的LLVM，那么你就应该有一个llvm-config二进制程序，这个程序返回你需要的所有编译和联接选项。<br />
我们也要同时更新我们的main.cpp的内容使之可以编译和运行我们的代码，这次我们main.cpp的内容如下：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &lt;iostream&gt;
#include &quot;codegen.h&quot;
#include &quot;node.h&quot;

using namespace std;

extern int yyparse();
extern NBlock* programBlock;

int main(int argc, char **argv)
{
    yyparse();
    std::cout &lt;&lt; programBlock &lt;&lt; endl;

    CodeGenContext context;
    context.generateCode(*programBlock);
    context.runCode();

    return 0;
}
</pre>
<p>现在我们需要这样来编译这些代码<br />
$ g++ -o parser <code data-enlighter-language="raw" class="EnlighterJSRAW">llvm-config --libs core jit native --cxxflags --ldflags</code> *.cpp<br />
你也可以编写一个Makefile来进行编译</p>
<pre>
all: parser

clean:
	rm parser.cpp parser.hpp parser tokens.cpp

parser.cpp: parser.y
	bison -d -o $@ $^

parser.hpp: parser.cpp

tokens.cpp: tokens.l parser.hpp
	lex -o $@ $^

parser: parser.cpp codegen.cpp main.cpp tokens.cpp
	g++ -o $@ <code data-enlighter-language="raw" class="EnlighterJSRAW">llvm-config --libs core jit native --cxxflags --ldflags</code> *.cpp
</pre>
<p><h3>7.2、运行我们的语言</h3>
</p>
<p>假设上述所有工作都圆满完成，那么现在你将有一个名为parser的二进制程序。运行它，还记得我们那个典型例子吗？让我们看看我们的编译器工作的如何。</p>
<pre>
$ echo 'int do_math(int a) { int x = a * 5 + 3 } do_math(10)' | ./parser
0x100a00f10
Generating code...
Generating code for 20NFunctionDeclaration
Creating variable declaration int a
Generating code for 20NVariableDeclaration
Creating variable declaration int x
Creating assignment for x
Creating binary operation 276
Creating binary operation 274
Creating integer: 3
Creating integer: 5
Creating identifier reference: a
Creating block
Creating function: do_math
Generating code for 20NExpressionStatement
Generating code for 11NMethodCall
Creating integer: 10
Creating method call: do_math
Creating block
Code is generated.
; ModuleID = 'main'

define internal void @main() {
entry:
	%0 = call i64 @do_math(i64 10)		;  [#uses=0]
	ret void
}

define internal i64 @do_math(i64) {
entry:
	%a = alloca i64		;  [#uses=1]
	%x = alloca i64		;  [#uses=1]
	%1 = add i64 5, 3	;  [#uses=1]
	%2 = load i64* %a	;  [#uses=1]
	%3 = mul i64 %2, %1	;  [#uses=1]
	store i64 %3, i64* %x
	ret void
}
Running code...
Code was run.
</pre>
<p><h2>8、结论</h2>
</p>
<p>是不是非常的酷？我同意如果你只是从这篇文章中拷贝粘贴的话，你可能会对运行得到的结果感觉有点失望，但是这点结果可能也会激发你更大的兴趣。此外，这就是本文的意义，这不是本篇指导文章的结束，这只是一个开始。因为有了这篇文章的介绍，你可以在文法分析，语法分析和装配语言的时候附加上一些疯狂的特性，然后创造出一个你自己命名的语言。你现在已经可以编译语句块了，那么你现在应该已经有如何继续下去的基本想法。<br />
本文完整的代码在Github<a href="http://github.com/lsegal/my_toy_compiler">这里</a>。我一直都在避免提到这个代码，因为这个代码不是本文的重点，而仅仅是带过这部分代码。</p>
<p>我意识到这是一篇非常长的文章，并且这篇文章中难免会有出错的地方，如果你找到了任何问题，在你觉得有空的时候，欢迎你给我发电子邮件，我将会调整我的文章。你如果向想我们共享一些信息，你也可以在你觉得有空的时候写信给我们。</p>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/19307.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/04/2019-Dev-Survey-Blog-360x200-1-150x150.png" alt="StackOverflow 2019 程序员调查" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19307.html" class="wp_rp_title">StackOverflow 2019 程序员调查</a></li><li ><a href="https://coolshell.cn/articles/1954.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg" alt="Java读写Excel" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1954.html" class="wp_rp_title">Java读写Excel</a></li><li ><a href="https://coolshell.cn/articles/4891.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="Bob大叔和Jim Coplien对TDD的论战" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4891.html" class="wp_rp_title">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href="https://coolshell.cn/articles/179.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2-300x179-1-150x150.jpg" alt="OMG, Windows 7 来自未来" width="150" height="150" /></a><a href="https://coolshell.cn/articles/179.html" class="wp_rp_title">OMG, Windows 7 来自未来</a></li><li ><a href="https://coolshell.cn/articles/3585.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg" alt="SOAP的S是Simple" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3585.html" class="wp_rp_title">SOAP的S是Simple</a></li><li ><a href="https://coolshell.cn/articles/8115.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg" alt="GCC 用 C++ 来编译" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8115.html" class="wp_rp_title">GCC 用 C++ 来编译</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1547.html">使用Flex Bison 和LLVM编写自己的编译器</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1547.html/feed</wfw:commentRss>
			<slash:comments>31</slash:comments>
		
		
			</item>
		<item>
		<title>TCP网络关闭的状态变换时序图</title>
		<link>https://coolshell.cn/articles/1484.html</link>
					<comments>https://coolshell.cn/articles/1484.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Sun, 27 Sep 2009 08:11:19 +0000</pubDate>
				<category><![CDATA[网络安全]]></category>
		<category><![CDATA[TCP]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1484</guid>

					<description><![CDATA[<p>TCP共有11个网路状态，其中涉及到关闭的状态有5个。 在我们编写网络相关程序的时候，这5个状态经常出现。因为这5个状态相互关联，相互纠缠，而且状态变化触发都是...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1484.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1484.html">TCP网络关闭的状态变换时序图</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>TCP共有11个网路状态，其中涉及到关闭的状态有5个。</p>
<p>在我们编写网络相关程序的时候，这5个状态经常出现。因为这5个状态相互关联，相互纠缠，而且状态变化触发都是由应用触发，但是又涉及操作系统和网络，所以正确的理解TCP 在关闭时网络状态变化情况，为我们诊断网络中各种问题，快速定位故障有着非常重要的作用和意义。</p>
<p style="text-align: left;">下是是根据W.Richard Stevens的《TCP/IP详解》一书的TCP状态转换图。</p>
<p><img decoding="async" loading="lazy" class="aligncenter  wp-image-6310" title="tcp 状态转换图" src="https://coolshell.cn/wp-content/uploads/2009/09/tcp1.jpg" alt="" width="585" height="826" /></p>
<p style="text-align: left;"><span id="more-1484"></span></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-6309" title="tcp 状态转换图 （注释）" src="https://coolshell.cn/wp-content/uploads/2009/09/tcp2.jpg" alt="" width="727" height="746" /> <img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6308" title="tcp 连接建立关闭图" src="https://coolshell.cn/wp-content/uploads/2009/09/tcp3.jpg" alt="" width="469" height="529" srcset="https://coolshell.cn/wp-content/uploads/2009/09/tcp3.jpg 469w, https://coolshell.cn/wp-content/uploads/2009/09/tcp3-265x300.jpg 265w" sizes="(max-width: 469px) 100vw, 469px" /> <img decoding="async" loading="lazy" class="aligncenter  wp-image-6307" title="tcp 连接建图" src="https://coolshell.cn/wp-content/uploads/2009/09/tcp3.jpg" alt="" width="469" height="529" /> <img decoding="async" loading="lazy" class="aligncenter  wp-image-6306" title="tcp 连接关闭图" src="https://coolshell.cn/wp-content/uploads/2009/09/tcp5.jpg" alt="" width="631" height="239" /></p>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/22263.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg" alt="从一次经历谈 TIME_WAIT 的那些事" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22263.html" class="wp_rp_title">从一次经历谈 TIME_WAIT 的那些事</a></li><li ><a href="https://coolshell.cn/articles/19840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg" alt="HTTP的前世今生" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19840.html" class="wp_rp_title">HTTP的前世今生</a></li><li ><a href="https://coolshell.cn/articles/11609.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg" alt="TCP 的那些事儿（下）" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11609.html" class="wp_rp_title">TCP 的那些事儿（下）</a></li><li ><a href="https://coolshell.cn/articles/11564.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone-150x150.jpg" alt="TCP 的那些事儿（上）" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11564.html" class="wp_rp_title">TCP 的那些事儿（上）</a></li><li ><a href="https://coolshell.cn/articles/9859.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg" alt="Alan Cox：单向链表中prev指针的妙用" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9859.html" class="wp_rp_title">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg" alt="性能调优攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_title">性能调优攻略</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1484.html">TCP网络关闭的状态变换时序图</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1484.html/feed</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
		<item>
		<title>C++的std::string的“读时也拷贝”技术！</title>
		<link>https://coolshell.cn/articles/1443.html</link>
					<comments>https://coolshell.cn/articles/1443.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Sat, 19 Sep 2009 13:19:33 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[C++]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1443</guid>

					<description><![CDATA[<p>C++的std::string的读时也拷贝技术！ 嘿嘿，你没有看错，我也没有写错，是读时也拷贝技术。什么?我的错，你之前听说写过时才拷贝，嗯，不错的确有这门技术...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1443.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1443.html">C++的std::string的“读时也拷贝”技术！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>C++的std::string的读时也拷贝技术！</p>
<p>嘿嘿，你没有看错，我也没有写错，是读时也拷贝技术。什么?我的错，你之前听说写过时才拷贝，嗯，不错的确有这门技术，英文是Copy On Write，简写就是COW,非常&#8217;牛&#8217;！那么我们就来看看这个&#8217;牛&#8217;技术的效果吧。</p>
<p>我们先编写一段程序<br />
<span id="more-1443"></span></p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
#include &lt;string&gt;
#include &lt;iostream&gt;
#include &lt;sys/time.h&gt;

static long getcurrenttick()
{
    long tick ;
    struct timeval time_val;
    gettimeofday(&amp;time_val , NULL);
    tick = time_val.tv_sec * 1000 + time_val.tv_usec / 1000 ;
    return tick;
}


int main( )
{
    string the_base(1024 * 1024 * 10, &#039;x&#039;);
    long begin =  getcurrenttick();
    for( int i = 0 ;i&lt; 100 ;++i ) {
       string the_copy = the_base ;
    }
    fprintf(stdout,&quot;耗时[%d] \n&quot;,getcurrenttick() - begin );
}
</pre>
<p>嗯，一个非常大的字符串，有10M字节的x，并且执行了100此拷贝。编译执行它，非常快，在我的虚拟机甚至不要1个毫秒。</p>
<p>现在我们来对这个string加点料！</p>
<pre class="brush:c; highlight:6">
int main(void) {
    string the_base(1024 * 1024 * 10, 'x');
    long begin =  getcurrenttick();
    for (int i = 0; i < 100; i++) {
        string the_copy = the_base;
        the_copy[0] = 'y';
    }
    fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );
}
</pre>
<p>现在我们再编译并执行这断程序，居然需要4~5秒！哇！非常美妙的写时才拷贝技术，性能和功能的完美统一。</p>
<p>我们再来看看另外一种情况！</p>
<pre class="brush:c">
string original = "hello";
char & ref = original[0];
string clone = original;
ref = 'y';
</pre>
<p>我们生成了一个string，并保留了它首字符的引用，然后复制这个string，修改string中的首字符。因为写操作只是直接的修改了内存中的指定位置，这个string就根本不能感知到有写发生，如果写时才拷贝是不成熟的，那么我们将同时会修改original和clone两个string。那岂不是灾难性的结果？幸好上述问题不会发生。clone的值肯定是没有被修改的。看来COW就是非常的牛！</p>
<p>以上都证明了我们的COW技术非常牛！</p>
<p>有太阳就有黑暗，这句说是不是有点耳熟？</p>
<pre class="brush:c; highlight:3">
int main(void) {
    string the_base(1024 * 1024 * 10, 'x');
    fprintf(stdout,"the_base's first char is [%c]\n",the_base[0] );
    long begin =  getcurrenttick();
    for (int i = 0; i < 100; i++) {
        string the_copy = the_base;
    }
    fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );
}
</pre>
<p>啊，居然也是4~5秒！你可能在想，我只是做了一个读，没有写嘛，这到底是怎么回事？难道还有读时也拷贝的技术！。</p>
<p>不错，为了避免了你通过[]操作符获取string内部指针而直接修改字符串的内容，在你使用了the_base[0]后，这个字符串的写时才拷贝技术就失效了。</p>
<p>C++标准的确就是这样的，C++标准认为，当你通过迭代器或[]获取到string的内部地址的时候，string并不知道你将是要读还是要写。这是它无法确定，为此，当你获取到内部引用后，为了避免不能捕获你的写操作，它在此时废止了写时才拷贝技术！</p>
<p>这样看来我们在使用COW的时候，一定要注意，如果你不需要对string的内部进行修改，那你就千万不要使用通过[]操作符和迭代器去获取字符串的内部地址引用，如果你一定要这么做，那么你就必须要付出代价。当然，string还提供了一些使迭代器和引用失效的方法。比如说push_back，等， 你在使用[]之后再使用迭代器之后，引用就有可能失效了。那么你又回到了COW的世界！比如下面的一个例子</p>
<pre class="brush:c; highlight:7">
int main( )
{
    struct timeval time_val;
    string the_base(1024 * 1024 * 10, 'x');
    long begin = 0 ;
    fprintf(stdout,"the_base's first char is [%c]\n",the_base[0] );
    the_base.push_back('y');
    begin = getcurrenttick();
    for( int i = 0 ;i< 100 ;++i ) {
        string the_copy = the_base ;
    }
    fprintf(stdout,"耗时[%d] \n",getcurrenttick() - begin );
}
</pre>
<p>一切又恢复了正常！如果对[]返回引用进行了操作又会发生情况呢，有兴趣的朋友可以试试！结果非常令人惊讶。</p>
<p>另外：上述例子是在linux环境下编译的，使用STL是GNU的STL。windows上我用的是vs2003，但是非常明显vs2003一点都不支持COW。</p>
<p>这篇文章出自<a href="http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/" target=_blank>http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/</a> 这里，我使用了它的例子。但是我重新自己组织了内容。</p>
<p>编写这篇文章的同时，我还参考了耗子的<a href="http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx">《标准C＋＋类string的Copy-On-Write技术》</a>一文<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 - CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg" alt="Rust语言的编程范式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_title">Rust语言的编程范式</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg" alt="API设计原则 &#8211; Qt官网的设计实践总结" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_title">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/12012.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif" alt="State Threads 回调终结者" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12012.html" class="wp_rp_title">State Threads 回调终结者</a></li><li ><a href="https://coolshell.cn/articles/11466.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg" alt="C语言的整型溢出问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11466.html" class="wp_rp_title">C语言的整型溢出问题</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1443.html">C++的std::string的“读时也拷贝”技术！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1443.html/feed</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title>Oracle的战书！</title>
		<link>https://coolshell.cn/articles/1426.html</link>
					<comments>https://coolshell.cn/articles/1426.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Fri, 11 Sep 2009 08:24:47 +0000</pubDate>
				<category><![CDATA[编程语言]]></category>
		<category><![CDATA[IBM]]></category>
		<category><![CDATA[Oracle]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1426</guid>

					<description><![CDATA[<p>http://www.oracle.com/features/suncustomers.html （转载本站文章请注明作者和出处 酷 壳 &#8211; Coo...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1426.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1426.html">Oracle的战书！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script></p>
<p style="text-align: center;"><a href="http://www.oracle.com/features/suncustomers.html">http://www.oracle.com/features/suncustomers.html</a><img decoding="async" loading="lazy" title="sun customers" src="https://coolshell.cn/wp-content/uploads/2009/09/sun_customers_lg.gif" alt="sun customers" width="370" height="552" /></p>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/962.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg" alt="【原创】SQL栏目树的代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/962.html" class="wp_rp_title">【原创】SQL栏目树的代码</a></li><li ><a href="https://coolshell.cn/articles/595.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg" alt="Oracle成功收购Sun" width="150" height="150" /></a><a href="https://coolshell.cn/articles/595.html" class="wp_rp_title">Oracle成功收购Sun</a></li><li ><a href="https://coolshell.cn/articles/203.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg" alt="IBM收购Sun，这是一种什么样的精神？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/203.html" class="wp_rp_title">IBM收购Sun，这是一种什么样的精神？</a></li><li ><a href="https://coolshell.cn/articles/309.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="十个最好的PDF生成库" width="150" height="150" /></a><a href="https://coolshell.cn/articles/309.html" class="wp_rp_title">十个最好的PDF生成库</a></li><li ><a href="https://coolshell.cn/articles/1901.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/viemu-movie-150x150.gif" alt="Visual Studio的Vim插件" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1901.html" class="wp_rp_title">Visual Studio的Vim插件</a></li><li ><a href="https://coolshell.cn/articles/1619.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg" alt="Windows 7 的新粉丝 Linus Torvalds" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1619.html" class="wp_rp_title">Windows 7 的新粉丝 Linus Torvalds</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1426.html">Oracle的战书！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1426.html/feed</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
