image.jsp-------------------生成随即验证码图片的jsp页面
代码如下:
<%@ page contentType="image/jpeg" import="java.awt.*,
java.awt.image.*,java.util.*,javax.imageio.*" %>
<%!
Color getRandColor(int fc,int bc)
{
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
%>
<%
out.clear();//这句针对resin服务器,如果是tomacat可以不要这句
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
Random random = new Random();
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x,y,x+xl,y+yl);
}
String sRand="";
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10));
sRand+=rand;
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
g.drawString(rand,13*i+6,16);
}
// 将认证码存入SESSION
session.setAttribute("rand",sRand);
g.dispose();
ImageIO.write(image, "JPEG", response.getOutputStream());
%>
login.jsp--------------------登录页面,在这里我是提供输入验证码然后提交进行验证
代码如下:
<%@ page language="java" import="java.sql.*" errorPage="" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>用户登录</title>
<script language="javascript">
function loadimage(){
document.getElementById("randImage").src = "image.jsp?"+Math.random();
}
</script>
</head>
<body>
<table width="256" border="0" cellpadding="0" cellspacing="0">
<!--DWLayoutTable-->
<form action="validate.jsp" method="post" name="loginForm">
<tr>
<td width="118" height="22" valign="middle" align="center"><input type="text" name="rand" size="15"></td>
<td width="138" valign="middle" align="center"><img alt="code..." name="randImage" id="randImage" src="image.jsp" width="60" height="20" border="1" align="absmiddle"></td>
</tr>
<tr>
<td height="36" colspan="2" align="center" valign="middle"><a href="javascript:loadimage();"><font class=pt95>看不清点我</font></a></td>
</tr>
<tr>
<td height="36" colspan="2" align="center" valign="middle"><input type="submit" name="login" value="提交"></td>
</tr>
</form>
</table>
</body>
</html>
validate.jsp-------------用来验证输入的验证码是否正确
代码如下:
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
<%
String rand = (String)session.getAttribute("rand");
String input = request.getParameter("rand");
if(rand.equals(input)){
out.print("<script>alert('验证通过!');</script>");
} else{
out.print("<script>alert('请输入正确的验证码!');location.href='login.jsp';</script>");
}
%>
上面生成的是纯数字的验证码,我们对其进行一下修改就可以生成数字字母混合的验证码了。
修改部分的代码如下:
<%
......
......
......
for (int i=0;i<155;i++)
{
......
......
}
//-----------------以下是要添加的代码--------------
char c[] = new char[62];
for (int i = 97, j = 0; i < 123; i++, j++) {
c[j] = (char) i;
}
for (int o = 65, p = 26; o < 91; o++, p++) {
c[p] = (char) o;
}
for (int m = 48, n = 52; m < 58; m++, n++) {
c[n] = (char) m;
}
String sRand="";
for (int i=0;i<4;i++){
int x = random.nextInt(62);
String rand=String.valueOf(c[x]);
sRand+=rand;
//---------------------------------------------
%>
据有部分朋友反应,在测试过程中出现java.lang.IllegalStateException: getOutputStream() has already been called for this response的异常,经过测试发现,使用tomcat5一下的版本确实会出现该问题,解决的办法就是把tomcat换成高点版本就可以了。
作者:Cloves Carneiro;
simmone
原文地址:
http://www.javaworld.com/javaworld/jw-06-2005/jw-0620-dwr.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43926_DWR_AJAX.html
关键词: DWR AJAX
概述
这篇文章阐述了使用开源项目DWR(直接Web远程控制)和AJAX(异步JavaScript和XML)的概念来提高Web应用的可用性。作者一步步来展示DWR如何使得AJAX的应用既简单又快捷。(1600字;2005年6月20日)
AJAX,或者说是异步JavaScript和XML,描述了一种使用混合了HTML(或XHTML)和层叠样式表作为表达信息,来创建交互式的Web应用的开发技术;文档对象模型(DOM),JavaScript,动态地显示和与表达信息进行交互;并且,XMLHttpRequest对象与Web服务器异步地交换和处理数据。
因特网上许多例子展示了在一个HTML文件内部使用XMLHttpRequest与服务器端进行交互的必要的步骤。当手工地编写和维护XMLHttpRequest代码时,开发者必须处理许多潜在的问题,特别是类似于跨浏览器的DOM实现的兼容性这样的问题。这将会导致在编码和调试Javascript代码上面花费数不清的时间,这显然对开发者来说很不友好。
DWR(直接Web远程控制)项目是在Apache许可下的一个开源的解决方案,它供给那些想要以一种简单的方式使用AJAX和XMLHttpRequest的开发者。它具有一套Javascript功能集,它们把从HTML页面调用应用服务器上的Java对象的方法简化了。它操控不同类型的参数,并同时保持了HTML代码的可读性。
DWR不是对一个设计的插入,也不强迫对象使用任何种类的继承结构。它和servlet框架内的应用配合的很好。对缺少DHTML编程经验的开发者来说,DWR也提供了一个JavaScript库包含了经常使用的DHTML任务,如组装表,用item填充select下拉框,改变HTML元素的内容,如<div>和<span>
DWR网站是详尽的并且有大量的文档,这也是这篇文章的基础。一些例子用来展示DWR如何使用和用它的库可以完成什么样的工作。
这篇文章让读者看到了一个使用了DWR的Web应用是如何一步步建立的。我会展示创建这个简单的示例应用的必要的细节,这个应用是可下载的并且可以在你的环境中布署来看看DWR如何工作。
注意:找到有关AJAX的信息并不困难;网页上有几篇文章和博客的条目涵盖了这个主题,每一个都试图指出和评论这个概念的不同的方面。在资源部分,你会找到一些有趣的指向示例和文章的链接,来学习AJAX的更多的内容。
示例应用
这篇文章使用的示例应用模拟了多伦多的一个公寓出租搜索引擎。用户可以在搜索前选择一组搜索标准。为了提高交互性,AJAX中以下两种情况下使用:
·应用通告用户配合他的选择会返回多少搜索结果。这个数字是实时更新的-使用AJAX-当用户选择的卧室和浴室的数量,或者价格范围变化时。当符合标准的搜索结果没有或太多时,用户就没有必要点击搜索按纽。
·数据库查询并取回结果是由AJAX完成的。当用户按下显示结果按钮时,数据库执行搜索。这样,应用看起来更具响应了,而整个页面不需要重载来显示结果。
数据库
我们使用的数据库是HSQL,它是一种占用资源很小的Java SQL数据库引擎,可以不需要安装和配置的与Web应用捆绑在一起。一个SQL文件被用来在Web应用的上下文启动时创建一个内存中的表并添加一些记录。
Java类
应用包含了两个主要的类叫Apartment和ApartmentDAO。Apartment.java类是一个有着属性和getter/setter方法的简单的Java类。ApartmentDAO.java是数据访问类,用来查询数据库并基于用户的搜索标准来返回信息。ApartmentDAO类的实现的直接了当的;它直接使用了Java数据库联接调用来得到公寓的总数和符合用户请求的可用公寓的列表。
DWR配置和使用
设置DWR的使用是简单的:将DWR的jar文件拷入Web应用的WEB-INF/lib目录中,在web.xml中增加一个servlet声明,并创建DWR的配置文件。DWR的分发中需要使用一个单独的jar文件。你必须将DWR servlet加到应用的WEB-INF/web.xml中布署描述段中去。
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<description>Direct Web Remoter Servlet</description>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
一个可选的步骤是设置DWR为调试模式—象上面的例子那样—在servlet描述段中将debug参数设为true。当DWR在调试模式时,你可以从HTMl网页中看到所有的可访问的Java对象。包含了可用对象列表的网页会出现在/WEBAPP/dwr这个url上,它显示了对象的公共方法。所列方法可以从页面中调用,允许你,第一次,运行服务器上的对象的方法。下图显示了调试页的样子:
调试页
现在你必须让DWR知道通过XMLHttpRequest对象,什么对象将会接收请求。这个任务由叫做dwr.xml的配置文件来完成。在配置文件中,定义了DWR允许你从网页中调用的对象。从设计上讲,DWR允许访问所有公布类的公共方法,但在我们的例子中,我们只允许访问几个方法。下面是我们示例的配置文件:
<dwr>
<allow>
<convert converter="bean" match="dwr.sample.Apartment"/>
<create creator="new" javascript="ApartmentDAO" class="dwr.sample.ApartmentDAO">
<include method="findApartments"/>
<include method="countApartments"/>
</create>
</allow>
</dwr>
上面的文件实现了我们例子中的两个目标。首先,<convert>标记告诉DWR将dwr.sample.Apartment对象的类型转换为联合数组,因为,出于安全的原因,DWR默认的不会转换普通bean。第二,<create>标记让DWR暴露出dwr.sample.ApartmentDAO类给JavaScript调用;我们在页面中使用JavaScript文件被javascript属性定义。我们必须注意<include>标记,它指明了dwr.sample.ApartmentDAO类的哪些方法可用。
HTML/JSP代码
配置完成后,你就可以启动你的Web应用了,这时DWR会为从你的HTML或Java服务器端页面(JSP)上调用所需方法作好准备,并不需要你创建JavaScript文件。在search.jsp文件中, 我们必须增加由DWR提供的JavaScript接口,还有DWR引擎,加入以下三行到我们的代码中:
<script src='dwr/interface/ApartmentDAO.js'></script>
<script src='dwr/engine.js'></script>
<script src='dwr/util.js'></script>
我们注意到当用户改变搜索标准时,这是AJAX在示例程序中的首次应用;正如他所看到的,当标准改变时,可用的公寓数量被更新了。我创建了两个JavaScript函数:当某一个选择下拉框中的值变化时被调用。ApartmentDAO.countApartments()函数是最重要的部分。最有趣的是第一个参数, loadTotal()函数,它指明了当接收到服务端的返回时DWR将会调用的JavaScript方法。loadTotal于是被调用来在HTML页面的<div>中显示结果。下面是在这个交互场景中所使用到的JavaScript函数:
function updateTotal() {
$("resultTable").style.display = 'none';
var bedrooms = document.getElementById("bedrooms").value;
var bathrooms = document.getElementById("bathrooms").value;
var price = document.getElementById("price").value;
ApartmentDAO.countApartments(loadTotal, bedrooms, bathrooms, price);
}
function loadTotal(data) {
document.getElementById("totalRecords").innerHTML = data;
}
很明显,用户想看到符合他的搜索条件的公寓列表。那么,当用户对他的搜索标准感到满意,并且总数也是有效的话,他会按下显示结果的按纽,这将会调用updateResults() JavaScript方法:
function updateResults() {
DWRUtil.removeAllRows("apartmentsbody");
var bedrooms = document.getElementById("bedrooms").value;
var bathrooms = document.getElementById("bathrooms").value;
var price = document.getElementById("price").value;
ApartmentDAO.findApartments(fillTable, bedrooms, bathrooms, price);
$("resultTable").style.display = '';
}
function fillTable(apartment) {
DWRUtil.addRows("apartmentsbody", apartment, [ getId, getAddress, getBedrooms, getBathrooms, getPrice ]);
}
updateResults()方法清空了存放搜索返回结果的表域,从用户界面上获取所需参数,并且将这些参数传给DWR创建的ApartmentDAO对象。然后数据库查询将被执行,fillTable()将会被调用,它解析了DWR返回的对象(apartment),然后将其显示到页面中(apartmentsbody)。
安全因素
为了保持示例的简要,ApartmentDAO类尽可能的保持简单,但这样的一个类通常有一组设置方法来操作数据,如insert(), update()和delete()。DWR暴露了所有公共方法给所有的HTML页面调用。出于安全的原因,像这样暴露你的数据访问层是不明智的。开发者可以创建一个门面来集中所有JavaScript函数与底层业务组件之间的通信,这样就限制了过多暴露的功能。
结论
这篇文章仅仅让你在你的项目中使用由DWR支持的AJAX开了个头。DWR让你集中注意力在如何提高你的应用的交互模型上面,消除了编写和调试JavaScript代码的负担。使用AJAX最有趣的挑战是定义在哪里和如何提高可用性。DWR负责了操作Web页面与你的Java对象之间的通信,这样就帮助你完全集中注意力在如何让你的应用的用户界面更加友好,
我想感谢Mircea Oancea和Marcos Pereira,他们阅读了这篇文章并给予了非常有价值的返匮。
资源
·javaworld.com:
javaworld.com
·Matrix-Java开发者社区:
http://www.matrix.org.cn/
·onjava.com:
onjava.com
·下载示例程序的全部源码:
http://www.javaworld.com/javaworld/jw-06-2005/dwr/jw-0620-dwr.war
·DWR: http://www.getahead.ltd.uk/dwr/index.html
·HSQL:http://hsqldb.sourceforge.net/
·AJAX的定义:http://en.wikipedia.org/wiki/AJAX
· “AJAX:通向Web应用的新途径": Jesse James Garrett (Adaptive Path, 2005.2): http://www.adaptivepath.com/publications/essays/archives/000385.php
· “非常动态的Web界面” Drew McLellan (xml.com, 2005.2): http://www.xml.com/pub/a/2005/02/09/xml-http-request.html
·XMLHttpRequest & AJAX 工作范例: http://www.fiftyfoureleven.com/resources/programming/xmlhttprequest/examples
· “可用的XMLHttpRequest实践” Thomas Baekdal (Baekdal.com, 2005.3): http://www.baekdal.com/articles/Usability/usable-XMLHttpRequest/
·"XMLHttpRequest使用导引" Thomas Baekdal (Baekdal.com, 2005.2):http://www.baekdal.com/articles/Usability/XMLHttpRequest-guidelines/
·AJAX实质:http://www.ajaxmatters.com/
(看完后个人感觉:有了DWR就JAVA开发而言,完全可以与AJAX匹敌啦,省了在JS上对XMLHTTP以及对DOM的处理,不可以避免地在后台对应的IO处理;然后就DWR来说,它增加了对XML中对应的配置--在开源框架中似乎一直不曾停止过。还有对一些DWR自有用法如DWRUtil.addRows得参考其相关文档---当然这样的功能我们自己也可以用JS来解决,并且它显然很实用。add by jkallen)
摘要: 制作环境:
Windows 2003 + IIS6、jre1.5.0_06、apache-tomcat-5.5.17
首先需要做以下准备工作
...
阅读全文
文件上传在web应用中非常普遍,要在servlet/jsp环境中实现文件上传功能非常容易,因为网上已经有许多用java开发的组件用于文件上传,本文以commons-fileupload组件为例,为servlet/jsp应用添加文件上传功能。
common-fileupload组件是apache的一个开源项目之一,可以从
http://jakarta.apache.org/commons/fileupload/
下载。该组件简单易用,可实现一次上传一个或多个文件,并可限制文件大小。
下载后解压zip包,将commons-fileupload-1.0.jar复制到tomcat的webapps\你的webapp\WEB-INF\lib\下,如果目录不存在请自建目录。
新建一个servlet: Upload.java用于文件上传:
1 import java.io.*
;
2 import java.util.*
;
3 import javax.servlet.*
;
4 import javax.servlet.http.*
;
5 import org.apache.commons.fileupload.*
;
6
7 public class Upload extends
HttpServlet {
8
9 private String uploadPath = "C:\\upload\\"; // 用于存放上传文件的目录
10 private String tempPath = "C:\\upload\\tmp\\"; // 用于存放临时文件的目录
11
12 public void
doPost(HttpServletRequest request, HttpServletResponse response)
13 throws
IOException, ServletException
14
{
15
}
16
}
17
18 //当servlet收到浏览器发出的Post请求后,在doPost()方法中实现文件上传。以下是示例代码:
19
20 public void
doPost(HttpServletRequest request, HttpServletResponse response)
21 throws
IOException, ServletException
22
{
23 try
{
24 DiskFileUpload fu = new
DiskFileUpload();
25 // 设置最大文件尺寸,这里是4MB
26 fu.setSizeMax(4194304
);
27 // 设置缓冲区大小,这里是4kb
28 fu.setSizeThreshold(4096
);
29 // 设置临时目录:
30
fu.setRepositoryPath(tempPath);
31
32 // 得到所有的文件:
33 List fileItems =
fu.parseRequest(request);
34 Iterator i =
fileItems.iterator();
35 // 依次处理每一个文件:
36 while
(i.hasNext()) {
37 FileItem fi =
(FileItem)i.next();
38 // 获得文件名,这个文件名包括路径:
39 String fileName =
fi.getName();
40 if(fileName!=null
) {
41 //
在这里可以记录用户和文件信息
42 //
43 // 写入文件a.txt,你也可以从fileName中提取文件名:
44 fi.write(new File(uploadPath + "a.txt"
));
45
}
46
}
47 // 跳转到上传成功提示页面
48
}
49 catch
(Exception e) {
50 // 可以跳转出错页面
51
}
52
}
53
54 //如果要在配置文件中读取指定的上传文件夹,可以在init()方法中执行:
55
56 public void init() throws
ServletException {
57 uploadPath =
.
58 tempPath =
.
59 // 文件夹不存在就自动创建:
60 if(!new
File(uploadPath).isDirectory())
61 new
File(uploadPath).mkdirs();
62 if(!new
File(tempPath).isDirectory())
63 new
File(tempPath).mkdirs();
64
}
65
编译该servlet,注意要指定classpath,确保包含commons-upload-1.0.jar和tomcat\common\lib\servlet-api.jar。
配置servlet,用记事本打开tomcat\webapps\你的webapp\WEB-INF\web.xml,没有的话新建一个。典型配置如下:
1 <?xml version="1.0" encoding="ISO-8859-1"?>
2 <!
DOCTYPE web-app
3
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
4 "http://java.sun.com/dtd/web-app_2_3.dtd">
5
6 <web-app>
7 <servlet>
8 <servlet-name>Upload</servlet-name>
9 <servlet-class>Upload</servlet-class>
10 </servlet>
11
12 <servlet-mapping>
13 <servlet-name>Upload</servlet-name>
14 <url-pattern>/fileupload</url-pattern>
15 </servlet-mapping>
16 </web-app>
17
配置好servlet后,启动tomcat,写一个简单的html测试:
1 <form action="fileupload" method="post"
2 enctype="multipart/form-data" name="form1">
3 <input type="file" name="file">
4 <input type="submit" name="Submit" value="upload">
5 </form>
注意action="fileupload"其中fileupload是配置servlet时指定的url-pattern。
摘自:
http://www.j2medev.com/Article/Class10/j2eeopensource/200409/62.html
输入输出
在所有的本系列文章中,在编写Ruby代码时都使用了大量的Ruby标准输出方法。其中,最为常用的是print和puts方法,有关其使用细节不再赘述。
所有这些和其它处理输入和输出的方法都定义于Kernel模块中。这个Kernel模块又被包含在Object类中。因此,Kernel的方法出现在每一个对象中。在输出方面,Kernel定义了print,printf,putc和IO类和两个子类(File和BasicSocket)-它们允许读写文件和套接字。BasicSocket是套接字库的一部分并且将在以后讨论它。包含了FileTest模块的File类,提供了许多方法来操作系统文件和目录。从Kernel中使用的用于读写到标准输入/输出机制的方法被进一步重用于File实例中的读写操作。下面是一个代码示例-它把一些名字写入一个新建的文件中,然后再把这些名字读回到一个数组中。
customers=%w[Jim Kevin Davin Andrew] outFile = File.new("c:\\examples\\test\\customers.txt", "w") customers.each{|customer| outFile.puts(customer)} outFile.close inFile= File.new("c:\\examples\\customers.txt", "r") readCustomers=inFile.readlines readCustomers.each{|customer| puts customer} inFile.close |
标准库
Ruby除了提供大量内置的类和模块外,它还提供了一定数目的标准库。这些库不是自动地成为你可以利用的Ruby类,模块,方法的一部分。你必须先在你的文件的顶部使用require(或load)关键字来使用库中的类或模块。在前一节中,我曾提到一个库-套接字库,它包含了大量的Ruby类(包括BasicSocket),以便于访问网络服务。但是在Ruby下载中一同提供了一整套其它的库。你可以查看一下你的Ruby下载中的lib目录,这个目录下应该存在大量的你的Ruby程序需要使用的库。
有关这些库的不好的一点是,没有大量的有关于这些类的参考文档。你可以在网站http://www.ruby-doc.org/stdlib/上找到一系列标准库及其包含类和模块的文件。即使是这些文档也指出:
"你需要明白,在表格中粗体的库具有良好的文档,而斜体的库没有文档。"
这就是Ruby的现状。你可能想说,Ruby是一个不可思议地丰富而有力的语言并且内置了许多构建我们的应用程序需要的功能,但是文档仍然有点不足。还好,现在已经有不少的人在努力改进Ruby的文档和支持。现在有不少的Ruby论坛已经倔起,并且随着每一个新版本的发行,文档都将有一定的改进-当然,这也是最近它备受关注的结果。然而,帮助文档仍然会成为这种语言挫败人心的一个因素。
在Ruby中,一切都是对象。更精确地说,Ruby中的一切都是一个具有完整功能的对象。因此,在Ruby中,数字4,定点数3.14和字符串"Hi"都是对象。显然,它们是有点"特殊"的,因为你不必使用new方法来创建它们。代之的是,你使用例如"literal 4"这样的形式来创建一个代表数字4的对象的实例。
然而,对于绝大多数人来说,学习一种新的编程语言时,首先理解该语言提供的"标准"类型是非常有用的。所以,在这一节,我们先探讨数字类型,字符串类型,布尔类型和另外一些基本的Ruby数据类型。
数字类型
实质上,Ruby中的数字被分为整数和浮点数两大类。其中,整数又被进一步细分为"常规大小"的整数和大型整数。因为在Ruby中一切都是对象,所以整数和浮点数都是按类来定义的(见图1)。从图1看出,Numeric是所有数字类型的基类,Float和Integer类是Numeric的子类。Fixnum和Bignum都是Integer的子类型-它们分别定义了"常规大小"的整数和大型整数。
图1.Ruby的数字类型类继承图。
Literal用来描述这些类的实例。下面的在交互式Ruby外壳(irb)中的代码显示了Float,Fixnum和Bignum的literal实例。注意,可以在literal上进行方法调用(在此,是指类方法)。
irb(main):001:0> 3.class => Fixnum irb(main):002:0> 3.4.class => Float irb(main):003:0> 10000000000000000000.class => Bignum |
还有另外一些语法用来创建数字类型,显示于下面的代码列表中。字母E可以用来描述以指数标志的数字。数字的前面加上0代表这是一个八进制数,加上0x代表这是一个十六进制数,而0b代表是一个二进制数。为清晰起见,下划线可以用作数字中的分隔符号。注意,当写literal时,不要用逗号作为分隔符号。在一些情况中,这实际上能生成一个数组,我们将在后面讨论。最后,在一个字符(或Ctrl或元字符的组合)前面的一个问号将会创建一个Fixnum的实例,相应于字符的ASCII字符/逃逸序列值。
< irb(main):001:0> 3.14E5 #指数标志 => 314000.0 irb(main):002:0> 054 #八进制 => 44 irb(main):003:0> 0x5A #十六进制 => 90 irb(main):004:0> 0b1011 #二进制 => 11 irb(main):005:0> 10_000 #10,000,用下划线隔开 => 10000 irb(main):006:0> i=10,000 #创建一个数组而不是10000 Fixnum => [10, 0] irb(main):007:0> i.class => Array irb(main):008:0> ?Z #Fixnum ASCII值 => 90 irb(main):009:0> ?Z.class => Fixnum irb(main):010:0> ?\C-s #Control-s的值ASCII => 19 |
Fixnum和Bignum实例间的真实差别是什么?Fixnum整数可以被存储在机器中的一个字(通常16,32或64位)中,但减去1个位;而Bignum实例是超出固定存储空间的整数。当然,作为开发者,你不必担心整数的大小(见下面的例子),由Ruby负责为你实现Fixnum和Bignum之间的自动转换!
irb(main):001:0> i=4 => 4 irb(main):002:0> i.class => Fixnum irb(main):003:0> i=i+100000000000000 => 100000000000004 irb(main):004:0> i.class => Bignum irb(main):005:0> i=i-100000000000000 => 4 irb(main):006:0> i.class => Fixnum |
字符串 在Ruby中,字符串是任意顺序的字节。通常,它们是一个字符序列。在Ruby中,可以使用一个literal或new方法来创建String类的实例。
irb(main):001:0> s1="Hello World" => "Hello World" irb(main):002:0> s2=String.new("Hello World") => "Hello World" |
当然,String中定义了许多方法(和操作符)。另外,可以使用单引号或双引号来指定一个字符串。双引号情况下允许串中加入逃逸字符并能够嵌入待计算的表达式。在单引号串情况下,你看到的就是串中的实际内容。为了更好的理解,请看下列例子。
irb(main):001:0> str1='a \n string' => "a \\n string" irb(main):002:0> str2="a \n string" => "a \n string" irb(main):003:0> puts str1 a \n string => nil irb(main):004:0> puts str2 a string => nil irb(main):005:0> 'try to add #{2+2}' => "try to add \#{2+2}" irb(main):006:0> "try to add #{2+2}" => "try to add 4" irb(main):007:0> this="that" => "that" irb(main):008:0> 'when single quote rights #{this}' => "when single quote rights \#{this}" irb(main):009:0> "double quote rights #{this}" => "double quote rights that" |
请注意,在显示之前,双引号中的文本是如何被计算的,其中包括了逃逸符号(\n)和表达式(#{2+2})。
除了使用单引号和双引号来定义一个字符串literal外,在Ruby中,还有另外的方法可以表达literal。一个百分号和小写或大写字母Q可以用来表达一个字符串,分别相应于单引号或双引号风格。
irb(main):001:0> %q@this is a single quote string #{2+2} here@ => "this is a single quote string \#{2+2} here" irb(main):002:0> %Q@this is a double quote string #{2+2} here@ => "this is a double quote string 4 here" |
注意,跟随在q%或Q%后面的字符分别定义了字符串literal的开始和结束。在本例中,@符号用作字符串开始与结束的限界符号。
还应该注意,Ruby并没有区分一个字符串和一个字符。也就是说,没有适用于单个字符的特定的类-它们仅是一些小的字符串。
布尔类型
最后,让我们再看一下布尔类型。在Ruby中,有两个类用于表达布尔类型:TrueClass和FalseClass。每个这些类仅有一个实例(一个singleton):也就是true和false。这些是可在Ruby的任何地方存取的全局值。还有一个类NilClass。NilClass也仅有一个实例nil-表示什么也没有。然而,在布尔逻辑中,nil是false的同义词。
irb(main):001:0> true|false => true irb(main):002:0> true&false => false irb(main):003:0> true|nil => true irb(main):004:0> true&nil => false |
正规表达式
大多数程序语言中都使用正规表达式。基于许多脚本语言的Ruby也广泛地使用正规表达式。我的一个同事曾经说"正规表达式太复杂了。"换句话说,你需要花费一些时间来学习正规表达式。在本文中,你仅能一瞥Ruby正规表达式的威力。在程序开发中,你不必一定使用正规表达式,但是如果使用这种工具,你的编码将更为紧凑而容易。而且,如果你想成为一名Ruby大师,你必须要花费其它时间来研究它。
在下面的例子中,Ruby中的正规表达式是在Tiger或菲Phil之间定义的。
现在你可以在一个条件或循环语句中使用带有一个匹配操作符("=~")的正规表达式来匹配或查找其它的字符串。
irb(main):001:0> golfer="Davis" if golfer =~ /Tiger|Phil/ puts "This is going to be a long drive." else puts "And now a drive by " + golfer end => "Davis" |
下面是另一个稍微复杂些的正规表达式:
/[\w._%-]+@[\w.-]+.[a-zA-Z]{2,4}/ |
你能够猜出这个表达式代表什么意思吗?它相应于一个电子邮件地址。这个正规表达式可以用来校验电子邮件地址。
irb(main):001:0> emailRE= /[\w._%-]+@[\w.-]+.[a-zA-Z]{2,4}/ email = "jwhite@interechtraining.com" if email =~ emailRE puts "This is a valid email address." else puts "this is not a valid email address." end 这是一个有效的电子邮件地址。 irb(main):002:0> email = "###@spammer&&&.333" if email =~ emailRE puts "This is a valid email address." else puts "this is not a valid email address." end |
这不是一个有效的电子邮件地址
图2把电子邮件正规表达式分解开来。你已看到,正规表达式语言是相当丰富的,但在此不多详述。有关正规表达式的更多信息请参考http://www.regular-expressions.info。
图2.电子邮件正规表达式
注意,在Ruby中正规表达式也是一种对象。在下面的代码示例中,一个正规表达式实例(派生自类Regexp)作为String方法的一个参数(gsub)以达到使用"glad"来替换和"happy"与"joy"之目的。
irb(main):001:0> quote = "I am so happy. Happy, happy, joy, joy!" regx = /(h|H)appy|joy/ quote.gsub(regx, "glad") => "I am so happy. Happy, happy, joy, joy!" => /(h|H)appy|joy/ => "I am so glad. glad, glad, glad, glad!" |
当你在正规表达式对象上使用=~操作符时,你能够得到例如匹配模式串的索引等信息。
irb(main):001:0> /Tiger|Phil/=~"EyeOfTheTiger" => 8 |
如果你曾编写过大量有关字符串的程序,你就会知道Ruby中的正规表达式是非常有力量的。因此,我建议在较深入地用Ruby开发你的第一个程序前,你应该全面地探讨一下Ruby中的正规表达式。当然,你可以参考本文相应的源码文件,其中包含了大量的正规表达式。
范围
在Ruby中,一个很不平常但是非常有用的概念就是范围(range)。一个范围是一个值序列。例如,字符a到z就可以定义在英语字母表中的所有的小写字母。另外一个范围的例子是整数1到10。一个范围能从任何类型的对象中创建,假定对象的类型允许使用Ruby的操作符(<=>)和succ方法进行比较。根据<=>操作符左边的操作数是否小于,等于或大于<=>操作符右边的操作数,<=>操作符将分别返回-1,0或+1。例如,"A"<=>"B"将返回-1。运行于整数4上的succ方法(4.succ)将返回5。
可以使用Range类的new方法或特殊的标志来创建一个范围。下面是在irb中分别使用括号和点速记标志创建的两个相同的范围(表示所有的大写字母)。
irb(main):001:0> r1=Range.new("A","Z") => "A".."Z" irb(main):002:0> r2=("A".."Z") => "A".."Z" |
当创建一个范围时,必须指定开始值和结束值。在上面的情况中,A作为开始值,Z作为结束值。当创建一个范围时,你还可以指示是否这个范围应该包括或不包括末尾元素。默认情况下,如上例所示,范围包括末尾元素。为了排除结束元素,你可以使用new方法的排除参数(置为true)或如下所示的3个点的速记标志。
irb(main):001:0> r1=Range.new("A","Z",true) => "A"..."Z" irb(main):002:0> r2=("A"..."Z") => "A"..."Z" irb(main):003:0> r1.include?"Z" => false irb(main):004:0> r2.include?"Z" => false |
上面的示例中在范围上调用的include?方法显示是否其参数是范围的一个成员。上例中,"Z"不是范围的一个元素。这个方法还有一个与之等价的操作符"==="-它实现相同的功能。
irb(main):005:0> r2==="Z" =≫ false |
范围被应用在Ruby编程的许多方面。它们有两种特定的使用:作为生成器(generator)和谓词(predicate)。作为一个生成器,在范围上的每个方法允许你遍历该范围中的每个元素;例如,你想确定在一个K字节范围中的实际字节数。下面在irb中运行的代码使用了一个范围作为K字节到字节的生成器。
irb(main):008:0> kilobytes=(1..10) kilobytes.each{|x| puts x*1024} => 1..10 1024 2048 3072 4096 5120 6144 7168 8192 9216 10240 |
在Ruby中,条件逻辑范围可以被用作谓语(predicate),通常借助于操作符===的帮助。例如,你可以使用一个范围谓词来测试一个相对于有效的端口号(0~65535)和保留的端口号(0~1024,不包括1024)的整数参考。
irb(main):001:0> proposedPort = 8080 validPorts=(0..65535) reservedPorts=(0...1024) if (validPorts === proposedPort) & !(reservedPorts === proposedPort) puts "Proposed port is ok to use." else puts "Proposed port is not allowed to be used." end => 8080 => 0..65535 => 0...1024 |
上例的结果是,建议的端口号可以使用。
另外,范围也可以用于存取数据结构(如数组和哈希表)中的元素。
一些程序语言(如C++和CLOS)提供了多重继承机制:一个类可以继承自多个超类。例如,一个House可能继承自一个Building类(连同Office和Hospital类一起)和Residence类(连同Apartment类一起)。尽管多重继承可能成为一种语言强有力的特征,但是由于它会增加该语言的复杂性和歧义,因此许多面向对象语言都没有加入它。
Ruby支持单继承。然而,它还提供了mixin-它提供了多继承的许多特征。一个mixin是一种"模块"类型。为此,你必须首先理解在Ruby中模块的含义。
在Ruby中,模块是一种把方法和常数分组的方式。它与类相似,但是一个模块没有实例并且也没有子类。也许解释模块的最好方法是举一个例子。假定你正在开发一个制造业应用程序。该程序需要存取大量的科学公式和常数,那么你可以或者是创建一个通用类来把这些放在其中或者是创建一个模块。模块的优点在于,在存取其中的公式时,根本没有任何实例扰乱所带来的烦恼。
module Formulas ACCELERATION = 9.8 LIGHTSPEED = 299792458 def energy (mass) mass*(LIGHTSPEED**2) end def force (mass) mass*ACCELERATION end end |
现在,这些公式方法和常数可以被任何数目的其它类或其自身所使用:
irb(main):046:0> Formulas.force(10) => 98.0 irb(main):047:0≫ Formulas::ACCELERATION =≫ 9.8 |
注意,为了调用一个模块方法或使用一个模块常数,你必须使用类似于调用一个类方法的标志。为了调用一个模块方法,你需要使用模块类名,后面跟着一个点,再跟着模块方法名。为了引用模块常数,你可以使用模块名,后面跟着两个冒号,再跟着常数名。
除了作为方法和常数的"公共"应用以外,模块还可以帮助定义多重继承。一个mixin是一个简单的"包括"有一个类定义的模块。当一个类包括一个模块时,所有的模块中的方法和常数都成为类的实例方法和常数。例如,假定上面定义的Formula模块作为一个mixin被添加到Rectangle类。为此,你要使用"include"关键字:
class Rectangle include Formulas end |
现在,Rectangle的实例具有了它们可以使用的force和energy方法,并且Rectangle类能够访问常数ACCELERATION和LIGHTSPEED:
irb(main):044:0> class Rectangle irb(main):045:1> include Formulas irb(main):046:1> end => Rectangle irb(main):047:0> Rectangle.new(4,5).force(10) => 98.0 irb(main):048:0> Rectangle::LIGHTSPEED => 299792458 |
这意味着,mixin给Ruby中的类带来了许多多重继承的优点,却避开了多重继承中存在的问题。
十一、 控制流
象所有的程序语言一样,Ruby提供了一组控制流命令,这包括条件语句(if/else结构),case语句和循环语句(do,while和for),还提供了象Ada和Java等语言中的异常处理能力。下面是Ruby中的一些控制流语句的示例:
ifarea > 100 "big" else "small" end case height | when 1 | print "stubby\n" | when 2..10 #高度范围为2~10 | print "short\n" | when 10..20 #高度范围为2~10 | print "tall\n" | end aRect = Rectangle.new(4,6) while aRect.area < 100 and aRect.height < 10 aRect.doubleSize() end for element in [2, 9.8, "some string", Math::PI] #遍历对象集合 print "The type is: " + element.type.to_s + "\n&" end |
控制语句通常非常直接,但是如前面的case语句和for循环语句所显示的,Ruby借鉴了其它一些语言特征和普通的面向对象的特性。
异常处理类似于Java中的"try...catch...finally"语句。在Ruby中,它们更改为"begin...rescue...ensure"语句:
begin #实现一些事情 rescue ##处理错误 ensure #做一些清理工作,如关闭一个打开的文件等 end |
为了在你的代码中引发一个异常,你只需要简单地调用raise方法:
if area < 0 raise else if area > 0 and area < 10 raise "Rectangle too small" else if area > 100 raise TooBigException "Rectangle too big" end |
第一个raise调用创建一个RuntimeError。第二个raise创建一个显示一条消息的RuntimeError。最后一个raise调用一个TooBigException的新实例(由它创建一个粗略定义的错误),并设置它的适当消息。
一个Ruby小程序
为了帮助你更好地掌握Ruby的基础知识,我在本文中提供了一个小程序供你学习之用。为了使这个程序工作,你可以下载并把该文件解压到你的文件系统。之后,它将创建一个examples文件夹,在这个目录下共有9个Ruby代码文件(.rb文件)。本文中的代码就包含在这些文件中。另外,你会找到一个testShapes.rb文件,它是测试Ruby的Rectangle,Square和Circle对象的主文件。只需简单地打开一个命令提示符并运行testShapes.rb文件即可。
你将注意到,在testShapes.rb和另外一些代码中,文件以"require"再加上一个文件名开头(象rectangle.rb)。这是在你的Ruby程序中加入或使用来自于其它文件代码的Ruby标志。
总结
Ruby能否接管Java或C#而成为业界领先的现代软件开发语言?尽管Ruby可能变得十分流行,但我对此仍抱有怀疑态度。作为一名在业界摸爬滚打多年的专业人员,我对其偶然性并不感到惊讶,但我还是比较注重实效的。例如,我发现Smalltalk是一种比Java优越的高级语言,然而优越并不会总会赢。现代语言背后总存在大量的技术和市场方面的支持。库,开发工具包,框架,架构,连接器,适配器,支持平台,服务,知识库,能干的开发团队,等等,都会被配置到位以支持象Java这样的程序语言。并且,无论你喜欢与否,Sun和Microsoft主宰下的市场在未来一段时间内肯定还是开发环境的胜者。
那么,为什么还要探讨Ruby呢?作为一种Perl或Python脚本语言的代替(这正是它的最初目的)或快速原型开发工具,Ruby可能特别有用。一些人也已经看到了Ruby的威力,并且开始把Ruby作为一种伟大的方法进行编程教学。根据我的本地Ruby用户组的成员提供的信息,有一些人正在把它应用于测试生产系统。不仅如此,我将邀请你,就象Bruce Tate和Dave Thomas邀请我一样来探讨一下这种语言的力量和美丽之处。即使Ruby不会得到广泛使用,但是随着人们对它的逐渐认识和试用,它一定会找到适应自己的编程环境。
-
· Private:只能为该对象所存取的方法。
-
· Protected:可以为该对象和类实例和直接继承的子类所存取的方法。
-
· Public:可以为任何对象所存取的方法(Public是所有方法的默认设置)。
这些关键字被插入在两个方法之间的代码中。所有从private关键字开始定义的方法都是私有的,直到代码中出现另一个存取控制关键字为止。例如,在下面的代码中,accessor和area方法默认情况下都是公共的,而grow方法是私有的。注意,在此doubleSize方法被显式指定为公共的。一个类的initialize方法自动为私有的。
class Rectangle attr_accessor :height, :width def initialize (hgt, wdth) @height = hgt @width = wdth end def area () @height*@width end private #开始定义私有方法 def grow (heightMultiple, widthMultiple) @height = @height * heightMultiple @width = @width * widthMultiple return "New area:" + area().to_s end public #再次定义公共方法 def doubleSize () grow(2,2) end end |
如下所示,doubleSize可以在对象上执行,但是任何对grow的直接调用都被拒绝并且返回一个错误。
irb(main):075:0> rect2=Rectangle.new(3,4) => #<Rectangle:0x59a3088 @width=4, @height=3> irb(main):076:0> rect2.doubleSize() => "New area: 48" irb(main):077:0> rect2.grow() NoMethodError: private method 'grow' called for #<Rectangle:0x59a3088 @width=8, @height=6> from (irb):77 from :0 |
默认情况下,在Ruby中,实例和类变量都是私有的,除非提供了属性accessor和mutator。
象大多数面向对象语言一样,Ruby类也允许定义类变量和方法。一个类变量允许在一个类的所有实例间共享单个变量。在Ruby中,两个@@号用于指示类变量。例如,如果你想要使一个BankAccount类的所有实例共享相同的利息率,那么该类可能被如下定义:
class BankAccount @@interestRate = 6.5 def BankAccount.getInterestRate() @@interestRate end attr_accessor :balance def initialize (bal) @balance = bal end end |
如你所见,类变量必须在使用前初始化,并且就象实例变量一样,如果你想存取类变量的话,你需要编写存取器方法。在此,我定义了一个类方法来返回利息率。注意,类名和在getInterestRate前面的句号表示一个类方法。一个类方法,不管对于任何实例,其工作方式都是相同的-在此,是把相同的利息率返回到所有的BankAccount实例。为了调用类方法,你需要使用类名,就象它使用于类方法定义中一样:
irb(main):045:0> BankAccount.getInterestRate => 6.5 |
事实上,用于创建类实例的"new"方法就是一个类方法。因此,当你在程序中输入"Rectangle.new"时,你实际在调用new类方法-这是Ruby默认情况下所提供的。
继承
面向对象编程的原则之一是支持类层次结构。就象自然界中的事物分类一样,类允许从更为通用的类进行继承。面向对象编程的特征主要体现在方法和变量的使用上。例如,一个Square类继承Rectangle类的一些特征,如方法和变量。一个Square是一种更具体类型的Rectangle(高度和宽度相等的Rectangle实例),但是它仍然有一个高度和宽度,也有一个面积(而且与矩形的计算方法相同)。在Ruby中,Square类可以使用下列定义创建:
class Square < Rectangle end |
"<Rectangle"意味着,Square是Rectangle的一个子类,或反过来说,Rectangle是Square的一个超类。默认情况下,一个Square实例自动地拥有所有一个Rectangle所拥有的相同的属性和方法,包括height,width和area方法。为了确保Square实例的边长相等,你可以重载现有的Square的initialize方法:
class Square < Rectangle def initialize (size) @height = size @width = size end end |
因为在Ruby中一切都是对象,所以Ruby中的一切几乎都派生自Object类。尽管这在所有类定义中都不是显式的(你不会看到<Object出现在定义中),但是的确所有的类都派生自Ruby的基类Object。知道这个事实后,你就会更容易地理解接下来要讨论的内容。
当编写你的应用程序时,你可以在一个类定义外定义方法。在本文开始,你已看到了一个并不是任何一个类的一部分的摄氏到华氏转换器方法。作为另外一个示例,下面是一个位于任何类之外的方法:
def feel? return "I feel fine." end |
为了执行这个方法,只要输入该方法名,而不需要类或实例:
irb(main):042:0> feel? => "I feel fine." |
此方法看似另外一种语言(如C)中的函数或过程。事实上,尽管这些方法看上去好象不属于任何类,但是这些方法却都是你已经添加到Object类上的方法,它(因为Object是所有类的超类)反过来也把这一方法添加到你的继承类上。因此,现在你可以在任何对象(如Square和Rectangle的实例)甚至一个类(如Rectangle类)上调用这个方法。
irb(main):043:0> sq1=Square.new(4) => #<Square:0x5a18b50 @width=4, @height=4> irb(main):044:0> rect1=Rectangle.new(5,7) => #<Rectangle:0x5a139a8 @width=7, @height=5> irb(main):045:0> sq1.feel? => "I feel fine." irb(main):046:0> rect1.feel? => "I feel fine." irb(main):047:0> Rectangle.feel? => "I feel fine." |
变量与赋值
至此,你是否注意到前面所有的示例代码中都缺少某种东西?难道你必须输入常数,实例变量或类变量?绝对不是!这正是Ruby的真正面向对象的天性的一部分。为此,首先让我们看一下Ruby中以前的普通变量。至此,你已经创建了很多Rectangle实例,但是你并没有把它们保留多长时间。比方说,你想要把一个变量赋值给你创建的一个Rectangle实例:
myRectangle = Rectangle.new(4,5) |
在Ruby中这是完全有效的代码,而且根本不需要另一行代码来把myRectangle类型化或声明为引用Rectangle的某种东西。在执行这一行代码以后,变量myRectangle就引用一个Rectangle的实例(高度和宽度值分别为4,5)。但是,这仅是一种在任何时刻都可以更改的对象引用,而与对象的类型无关(在Ruby中一切都是对象)。因此,下面的命令提示符行中,你可以容易地把myRectangle赋值给一个字符串:
irb(main):049:0< myRectangle=Rectangle.new(4,5) => #<Rectangle:0x587c758 @width=5, @height=4> irb(main):050:0< myRectangle="Jim's Rectangle" => "Jim's Rectangle"
|
你可以在许多其它程序语言(甚至包括象Java这样的面向对象的语言)中试验一下,并观察从你的IDE所产生的编译错误。
变量,实例变量,类变量,甚至还有"常量"其实都只是对象引用。它们引用对象,但是它们并不是对象本身。因此,它们可以被动态地改变,甚至引用另一种不同类型的对象。
因为这一灵活性,所以必须在Ruby中进行一些约定以帮助每个人都知道某个变量正为代码所使用。其实,你已经看到了其中之一(@符号,它意味着这是一个实例变量)。其它的变量,方法和类命名约定列于下表1中。
- · 局部变量和方法参数以一个小写字母开头。
- · 方法名字以一个小写字母开头。
- · 全局变量以一个$开头。
- · 实例变量以一个@开头。
- · 类变量以两个@开头。
- · 常数以一个大写字母开头(它们经常被指定全部大写)。
- · 类和模块名以一个大写字母开头。
局部变量 |
全局变量 |
实例变量 |
类变量 |
常数 |
类名 |
方法名 |
aVar |
$Var |
@var |
@@var |
VAR |
MyClassmy |
Method |
name |
$debug |
@lastName |
@@interest |
PI |
Rectangle |
area |
表1.这个表包含了在Ruby编码约定下的相关示例。
操作符方法
现在,假定你想实现合并Rectangle类的实例或把它们添加到另一个Rectangle实例。你当然可以定义另外一个称为"add"的方法,这种选择利用了Ruby真正的面向对象的特征之一。然而,你还可以重载"+"运算符来适当地把两个Rectangle实例加起来。这个"+"方法(如4+5),对Ruby来说,只是另外一个方法而已。由于只是"另外一个方法",所以你可以给它增加一些功能来满足Rectangle类的需要。例如,你还可以定义"+"运算符来实现一个矩形面积加上另一个矩形面积。
def + (anotherRectangle) totalArea = area() + anotherRectangle.area() Rectangle.new(@height,totalArea/@height) end
|
在把这个方法添加到Rectangle类以后,你可以使用+方法调用来把两个Rectangle的实例相加:
irb(main):001:0> rect1=Rectangle.new(2,3) => #<Rectangle:0x58aa688 @width=3, @height=2> irb(main):002:0> rect2=Rectangle.new(3,4) => #<Rectangle:0x58a6ef0 @width=4, @height=3> irb(main):003:0> rect1+rect2 => #<Rectangle:0x58a4a60 @width=9, @height=2>
|
这正是操作符重载,对于那些使用过Agol,C++,Python和其它语言的用户来说,可能已经非常熟悉这个特征。
方法参数
至此,我们一直假定,传递给一个方法的参数个数是已知的。也许在其它语言中不可思议,但是Ruby的确允许你传递可变个数的参数并且以单个参数来捕获它们。为了创建一个可变长度的参数,只需要把一个星号(*)放在最后一个参数前面即可。这样,你就可以在Ruby中编写一个如下的多边形定义。
class Polygon def initialize (s1,s2,s3,*others) @sideOne = s1 @sideTwo = s2 @sideThree = s3 @otherSides = others end end |
如下所示,你可以使用这个定义来创建一个三角形或一个六边形。
irb(main):009:0> poly1=Polygon.new(2,4,5) => #<Polygon:0x594db10 @otherSides=[], @sideThree=5, @sideTwo=4, @sideOne=2> irb(main):010:0> poly2=Polygon.new(2,18,4,5,7,9) => #<Polygon:0x5948d58 @otherSides=[5, 7, 9], @sideThree=4, @sideTwo=18, @sideOne=2>
|
在支持可变长度参数的同时,Ruby还允许定义一个方法参数的默认值(在调用者没有提供的情况下使用)。例如,下面是Rectangle类的一个更好的初始化表达。
def initialize (hgt = 1, wdth = 1) @height = hgt @width = wdth end
|
现在,在调用时如果省略了某参数,那么在定义中参数紧邻的赋值运算符担当一个缺省的赋值器。现在,当创建一新的矩形时,如果在调用时省略了宽度,那么一个适当的宽度也会被默认地提供:
irb(main):090:0> rect=Rectangle.new(2) => #<Rectangle:0x5873f68 @width=1, @height=2>
|