对象, 变量, 常量和类

在ruby中表现数据的基本单位称为对象。

常见对象与其所属的类

对象
数值 Numeric
字符串 String
散列 Hash
正则表达式 Regex
文件 File
符号 Symbol
数组 Array

此外还有range和execption对象等

变量分为以下几种

  • 全局变量: 以$来表示
  • 实例变量: 以@开头来表示
  • 类变量: 以@@开头来表示
  • 局部变量: 以英文字母或者_来表示

除了以上几种变量类型 还有伪变量类型(一种在ruby里面预先定义的变量类型)

Ruby中,nil、true、false、self等都是伪变量

全局变量与局部变量示例
新建两个文件,一个为scopetest.rb, 一个为variable.rb
variable.rb内容
$x = 1
x = 1scopetest.rb内容
$x = 200
require_relative 'sub'x =  3
p $x
p x
结果为:1  3
常量以大写字母命名
且一旦赋值以后就不在可以被更改,如果赋值会产生异常。

ruby中的保留字

LINE ENCODING FILE BEGIN
END alias and begin
break case def defined?
do else elsif end
ensure false for while
if in module next
nil not or redo
rescue retry return self
super then true undef
unless until when while
yield

类和模块

当想知道某个对象属于哪个类时,我们可以使用class方法ary = []
str = "hello world"
p ary.class #=> Array
p str.class #=> String当想知道某个对象是否属于某个类时,我们可以使用instance_of?方法
ary=[]
str="Helloworld."
p ary.instance_of?(Array)  #=>true
p str.instance_of?(String) #=>true
p ary.instance_of?(String) #=>false
p str.instance_of?(Array)  #=>false

继承

通过扩展已定义的类来创建新类称为继承。
继承后创建的新类称为子类(subclass),被继承的类称为父类2(superclass)。通过继承可以实现以下操作。

  • 在不影响原有功能的前提下追加新功能
  • 重新定义原有功能, 使名称相同的方法产生不同的效果
  • 在已有功能的基础上追加处理,扩展已有功能
BasicObject类是Ruby中所有类的父类,它定义了作为Ruby对象的最基本功能。备注BasicObject类是最最基础的类,甚至连一般对象需要的功能都没有定义。因此普通对象所需要的类一般都被定义为Object类。字符串、数组等都是Object类的子类。子类与父类的关系称为“isa关系”3。例如,String类与它的父类Object就是isa关系。
str = "hello" ;
p str.is_a?(String) #=> true
p str.is_a?(Object) #=> true
p str.is_a?(BasicObject) #=> true
由于instance_of?方法与is_a?方法都已经在Object类中定义过了,因此普通的对象都可以使用这两个方法。示例:创建一个类
class HelloWorld                   # class 语句def initialize(myname = "Ruby") # 类方法@name = myname               # 初始化实例变量enddef hello                       # 实例方法puts "hello, world. I am #{@name}"end
endbob = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby = HelloWorld.new("Ruby")
bob.hello
alice.hello
ruby.helloclass 类名
类的定义
end类名的首字母必须大写。名为initialize的方法比较特别。使用new方法生成新的对象时,initialize方法会被调用,同时new方法的参数也会被原封不动地传给initialize方法。因此初始化对象时需要的处理一般都写在这个方法中。而只要在同一个实例中,程序就可以超越方法定义,任意引用、修改实例变量的值。另外,引用未初始化的实例变量时的返回值为nil。

存取器

在Ruby中,从对象外部不能直接访问实例变量或对实例变量赋值,需要通过方法来访问对象的内部。
对类进行扩充,增加存取方法:class HelloWorld                   # class 语句def initialize(myname = "Ruby") # 类方法@name = myname               # 初始化实例变量enddef hello                       # 实例方法puts "hello, world. I am #{@name}"enddef name=(name) # 存@name = nameenddef name       # 取return @nameend
endbob = HelloWorld.new("Bob")
bob.hellobob.name =("kobe")
p bob.name
bob.name =("kobe") #=> 实例变量@name的值此时为kobe
p bob.name

当对象越来越多时,需要定义的存取器越来越多,导致代码越来越复杂。因此ruby提供了存取器的快捷方式

定义 意义
attr_reader :name 只读(定义name方法)
attr_writer :name 只写(定义 name=方法)
attr_accessor :name 读写(定义以上两个方法)
在实例方法中,可以用self这个特殊的变量来引用方法的接收者class HelloWorld                   # class 语句attr_accessor :namedef initialize(myname = "Ruby") # 类方法@name = myname               # 初始化实例变量enddef greetputs "Hi, I am #{self.name}"enddef greet_againputs "Hi, I am #{name}"endbob = HelloWorld.new("Bob")
bob.greet #=> Hi, I am Bob
bob.greet_again #=> Hi, I am Bob
调用方法时,如果省略了接收者,Ruby就会默认把self作为该方法的接收者。因此,即使省略了self,也还是可以调用name方法但是有一点需要注意:
在调用像name=方法这样的以=结束的方法时,即使实例方法中已经有了name="Ruby"这样的定义,但如果仅在方法内部定义名为name的局部变量,也不能以缺省接收者的方式调用name=方法。这种情况下,我们需要用self.name="Ruby"的形式来显式调用name方法。备注 虽然self本身与局部变量形式相同,但由于它是引用对象本身时的保留字,因此即使对它进行赋值,也不会对其本身的值有任何影响。像这样,已经被系统使用且不能被我们自定义的变量名还有nil、true、false、__FILE__、__LINE__、__ENCODING__等。

如何在已经定义的类中追加新的类方法?

方法的接收者就是类本身(类对象)的方法称为类方法。第一种:
class << 类名 ~ end 这个特殊的类定义中,以定义实例方法的形式来定义类方法。class << HelloWorlddef hello(name)puts "#{name} said hello"end
endHelloWorld.hello("Ruby")第二种:
在class语句中使用self时,引用的对象是该类本身,因此,我们可以使用class << self ~ end 这样的形式,在class语句中定义类方法。这种方式很常见class HelloWorldclass << selfdef greet(name)puts "#{name} said Hello"endendendHelloWorld.greet("Perl")第三种:
我们还可以使用 def 类名.方法名 ~ end 这样的形式来定义类方法。
class HelloWorlddef self.speak(name)puts "#{name} said hello"end
endHelloWorld.speak("Matz")备注 class << 类名 ~ end 这种写法的类定义称为单例类定义,单例类定义中定义的方法称为单例方法。常量
常量就是指拥有固定值,且值一旦被定义就无法更改的变量,通常用大写字母来命名。
对于在类中定义的常量,我们可以像下面这样使用::通过类名来实现外部访问。类变量
以@@开头的变量称为类变量。类变量是该类所有实例的共享变量,这一点与常量类似,不同的是我们可以多次修改类变量的值。另外,与实例变量一样,从类的外部访问类变量时也需要存取器。不过,由于attr_accessor等存取器都不能使用,因此需要直接定义。class HelloCount@@count = 0def self.hello@@count += 1puts "hello #{@@count}"enddef self.counts@@countend
endHelloCount.hello
HelloCount.hello
HelloCount.hello
p HelloCount.counts

限制方法的调用

到目前为止,我们定义的方法都能作为实例方法被任意调用,但是有时候我们可能并不希望这样。例如,只是为了汇总多个方法的共同处理而定义的方法,一般不会公开给外部使用。

Ruby提供了3种方法的访问级别,我们可以按照需要来灵活调整。

  • public…以实例方法的形式向外部公开该方法
  • private…在指定接收者的情况下不能调用该方法(只能使用默认接收者的形式调用)
  • protected…在同一个类中时可将该方法作为实例方法调用
public 和 private 的例子class AccTestdef pubputs "pub is a public meth!"endpublic :pubdef privputs "priv is a private meth"endprivate :privendacc = AccTest.new
acc.pub #=> 正常调用
acc.priv #=> 报错希望统一定义多个方法的访问级别时,可以使用下面的语法。
class AccTestpublicdef pubputs "pub is a public meth!"endprivatedef privputs "priv is a private meth"end
end没有指定访问级别的放马默认为public,但initialize方法是列外,它通常被定义为private。定义为protected的方法,在同一个类(及其子类)中可作为实例方法使用,而在除此以外的地方则无法使用。
示例:
class Pointattr_accessor :x, :yprotected :x=, :y=def initialize(x = 0.0, y = 0.0)@x, @y = x, yenddef swap(other)tmp_x, tmp_y = @x, @yself.x, self.y = other.x, other.yother.x, other.y = tmp_x, tmp_yreturn selfend
endobj = Point.new
obj2 = Point.new(30, 40)obj.swap(obj2)
p [obj.x, obj.y]
p [obj2.x, obj2.y]
p obj2.x=20 #=> 错误❌,因为方法不是public方法,无法在类外部调用

给原有的类添加方法

Ruby允许我们在已经定义好的类中添加方法。
class Stringdef count_strary = self.split(/\s+/)return ary.sizeend
endstr = "Just another ruby newbie"
p str.count_str #=> 4

继承

class 类名 < 父类名
类定义
end示例:重新定义[]运算符
其中super来调用父类中的[]方法
class RingArray < Arraydef [](i)idx = i % self.sizesuper (idx)end
endobj = RingArray[1, 2, 3, 4]
p obj[4]定义类时没有指定父类的情况下,Ruby会默认该父类为Object类。
Object类提供了很多便于实际编程的方法,但在某些情况下希望使用更加轻量的类,
而这时就可以使用BasicObject类。
BasicObject 类只提供了作为Ruby对象的最低限度的方法。类对象调用 instance_methods 方法后,就会以符号的形式返回该类的实例方法列表。下面我们就用这个方法来对比一下Object类和BasicObject类的实例方法。
定义BasicObject的子类时,与Object类不同,需要明确指定BasicObject类为父类。

alias与undef

有时我们会希望给已经存在的方法设置别名,这种情况下就需要使用alias方法。alias方法的参数为方法名或者符号名。alias 别名 原名   # 直接使用方法名
alias :别名 :原名  # 使用符号名class C1def helloputs "hello"end
endclass C2 < C1alias old_hello hellodef helloputs "hello again"end
endclass C2 < C1alias :old_hello :hellodef helloputs "hello again"end
endC2.new.hello
C2.new.old_helloundef  方法名   # 直接使用方法名
undef  :方法名  # 使用符号名例如,在子类中希望删除父类定义的方法时,可以使用undef。

单例类

在下面的例子中,我们分别将"Ruby"赋值给str1对象和str2对象,然后只对str1对象添加hello方法。这样一来,两个对象分别调用hello方法时,str1对象可以正常调用,但str2对象调用时程序就会发生错误。class << strdef helloputs self.sizeend
endstr2 = ""
str.hello #=> 5
str2.hello #=>  No method error到目前为止,我们已经多次只在特定的某个类中添加类方法。Ruby中所有的类都是Class类的对象,因此,Class类的实例方法以及类对象中所添加的单例方法都是类方法。

模块

模块是Ruby的特色功能之一。如果说类表现的是事物的实体(数据)及其行为(处理),那么模块表现的就只是事物的行为部分。模块与类有以下两点不同。

  • 模块不能拥有实例
  • 模块无法被继承、

Mixin就是将模块混合到类中。在定义类时使用include,模块中的方法、常量就都能被类使用。

  • 虽然两个类拥有相似的功能,但是不希望把它们作为相同的种类(Class)来考虑
  • Ruby不支持父类的多重继承,因此无法对已经继承的类添加共通的功能
module MyMoudledef helloputs "hello I am Moudle"end
endclass MyClass1include MyMoudle
endclass MyClass2include MyMoudle
endMyClass1.new.hello
MyClass2.new.hello模块可以提供独立的命名空间
所谓命名空间(namespace),就是对方法、常量、类等名称进行区分及管理的单位。由于模块提供各自独立的命名空间,因此A模块中的foo方法与B模块中的foo方法就会被程序认为是两个不同的方法。同样,A模块中的FOO常量与B模块的FOO常量也是两个不同的常量。
例如,在FileTest模块中存在与获取文件信息相关的方法。我们使用“模块名.方法名”的形式来调用在模块中定义的方法,这样的方法称为模块函数。p FileTest.exist?('case.rb')
p FileTest.size("case.rb")
p Math::PI
p Math.sqrt(2)如果没有定义与模块内的方法、常量等同名的名称,那么引用时就可以省略模块名。通过include可以把模块内的方法名、常量名合并到当前的命名空间。下面是刚才提到的Math模块的例子。我们使用module语句来创建模块。语法与创建类时几乎相同,模块名的首字母必须大写。
module 模块名  模块定义
endmodule HelloModuleVersion = "1.0"def hello(name)puts "Hello, #{name}."endmodule_function :hello
endp HelloModule::Version
HelloModule.hello("Ruby")
HelloModule.__send__(:hello, "Perl")
备注:如果只是定义了方法,但是并没有通过 module_function symbol 方法将模块函数公开给外部使用的话,无法通过模块名.方法名的形式调用。 一旦通过module_function symbol 公开方法以后,就能以这种方法调用方法。以“模块名.方法名”的形式调用时,如果在方法中调用self(接收者),就会获得该模块的对象。
def myselfselfend
p HelloModule.myself #=> HelloModule 模块名Mix-in
Note: 关于Mix-in
当我们想要知道一个类的继承关系时可以使用xxx.ancestors 来获取
p Regexp.ancestors #=> [Regexp, Object, Kernel, BasicObject]当我们想要知道一个类的父类可以用 Regexp.superclass方法来获取假设有个类C, 类C的实例在调用方法时,Ruby会按类C、模块M、类C的父类Object这个顺序查找该方法,并执行第一个找到的方法。被包含的模块的作用就类似于虚拟的父类。
虽然Ruby采用的是不允许具有多个父类的单一继承模型,但是通过利用Mixin,就既可以保持单一继承的关系,又可以同时让多个类共享功能。
单一继承的优点就是简单,不会因为过多的继承而导致类之间的关系变得复杂。但是另一方面,有时我们又会希望更加积极地重用已有的类,或者把多个类的特性合并为更高级的类,在那样的情况下,灵活使用单一继承和Mixin,既能使类结构简单易懂,又能灵活地应对各种需求。查找方法的规则
1.同继承关系一样,原类中已经定义了同名的方法时,优先使用该方法。
2.在同一个类中包含多个模块时,优先使用最后一个包含的模块。
3.嵌套include时,查找顺序也是线性的.
4.相同的模块被包含两次以上时,第二次以后的会被省略。extend方法
extend方法可以使单例类包含模块,并把模块的功能扩展到对象中。
module Adef nameputs "I am A"end
endstr = "Hello"
str.extend(A)
str.nameclass VendV.extend(A)
V.name
extend既可以用来扩展实例方法,也可以扩展类方法。见上面的示例。module Adef nameputs "I am A"end
endmodule Bdef nameputs "I am B"end
endmodule Cinclude Bdef nameputs "I am C"end
endclass TestModuleinclude Cinclude A
endp TestModule.ancestors #=> [TestModule, A, C, B, Object, Kernel, BasicObject]class TestModuleinclude C, A
endp TestModule.ancestors #=> [TestModule, C, B, A, Object, Kernel, BasicObject]
注意这里导入模块时,第一种方式是分行,第二种是用逗号隔开,先后顺序有区别,参考结果。
逗号之前的模块会被优先查找,而分行形式,最后倒入的模块会被优先查找。如果类 Mix-in 了模块,就相当于为该类添加了实例方法,这种情况下self代表的就是被mix-in的类的对象。即使是相同的方法,在不同的上下文调用时,其含义也会不一样,因此对于Mixin的模块,我们要注意根据实际情况判断是否使用模块函数功能。一般不建议在定义为模块函数的方法中使用self。除了之前介绍的定义类方法的语法外,使用 extend 方法也同样能为类对象追加类方法。下面是使用 extend 方法追加类方法,并使用 include 方法追加实例方法的一个例子。module ClassMethoddef cmethod"class method"end
endmodule InstanceMethodsdef imethod"instance method"end
endclass MyClassextend ClassMethodinclude InstanceMethods
endp MyClass.cmethod
p MyClass.new.imethod在这里,extend 与 include的区别在于 extend 用于扩展类方法,而include是扩展实例方法,这个不能搞错。
其实不管是类也好,实例也好,本质上都是对象,因为对于Class类来说,String等类,都是它的实例,因此类本质上也是对象,因此在调用方法时,只是为了区分接收者的对象类型,才有类方法和实例方法之区别。

Ruby中的类和模块和变量使用说明相关推荐

  1. Python中的类、模块和包究竟是什么?

    Python培训教程:Python中的类.模块和包究竟是什么?在Python和其他编程语言中,都有类似或相同的概念,如对象.类.模块.包,名称都是一样的,只不过会有细微的一些区别,正是因为有这些存在, ...

  2. Ruby中的类与对象(一)

    一些约定: 类名首字母大写 方法名首字母小写 end在Ruby中无处不在. 声明类 class BookInStock end 创建类对象 a_book = BookInStock.new anoth ...

  3. 在python中同一个类下不同方法之间变量的调用

    栗子:(方法a中的b变量被方法c调用) class A: def a(self): self.b = 1 def c(self): # 要self.被调用方法的方法名 self.a() d = 2+s ...

  4. ruby 变量类中范围_Ruby中的类

    ruby 变量类中范围 Ruby类 (Ruby Classes) In the actual world, we have many objects which belong to the same ...

  5. 04/28/2010 类,对象,变量

    表示歌曲的Ruby类,歌曲(名字,演唱者,时长) 在Ruby程序中,initialize是一个特殊的方法. 当调用Song.new创建一个新的Song对象时,Ruby首先分配一些内存来保存未初始化的对 ...

  6. 如何初始化类的static成员变量?

    如何初始化类的static成员变量? 类的static成员变量不被某一个对象所独有,而是被所有同类型的对象所共有. 只能在头文件中声明类的static成员变量,不可在头文件中初始化,否则会造成重定义. ...

  7. python中的类的属性是什么_python中类属性和数据属性的解释

    python中的类叫class object,类的实例叫instance object. 类 Class Objects 类拥有两种操作,1.类属性 attribute references 2.实例 ...

  8. python 如何查看模块所有方法-Python查看模块(变量、函数、类)方法

    前面章节中,详细介绍了模块和包的创建和使用(严格来说,包本质上也是模块),有些读者可能有这样的疑问,即正确导入模块或者包之后,怎么知道该模块中具体包含哪些成员(变量.函数或者类)呢? 查看已导入模块( ...

  9. python 调用另一个py_跟我自学python 第9章 类(9.4.6 在一个模块中导入另一个模块)...

    大家好今天学习9.4.6 在一个模块中导入另一个模块 .今天我们继续学习python.马上类就学完了. 喜欢python就关注冠山巡山使. 我将跟大家一块自学完python语言. 9.4.6 在一个模 ...

  10. python一个函数调用另一个函数中的变量_在Python 中同一个类两个函数间变量的调用方法...

    在Python 中同一个类两个函数间变量的调用方法 更新时间:2019年01月31日 15:44:54 作者:wxy_summer 今天小编就为大家分享一篇在Python 中同一个类两个函数间变量的调 ...

最新文章

  1. 参数等效模型可以用于_等效媒质理论(介电参数反演)
  2. inline-block 间距
  3. 第二十三周微职位elk日志系统
  4. java rmi 还有用吗_java rmi使用后的感想
  5. Vue报错Module not found: Error: Can‘t resolve ‘less-loader‘
  6. MySQL卸干净与下载安装(2020最新版+亲测踩坑可用)
  7. 关于验证码的DEMO
  8. java应用程序无法启动,Apache Tomcat / 6.0.32 - Web应用程序无法启动
  9. python编程( 第一份Windows平台运行的python代码)
  10. 有个码龄10年的程序员跟我说:“他编程从来不用鼠标”,我说:
  11. 终极人机大战要来了?AI打星际2即将直播,DeepMind暴雪发出神秘预告
  12. L1-080 乘法口诀数列 (20 分)-PAT 团体程序设计天梯赛 GPLT
  13. IntentService用法小结
  14. 多功能时钟电路的设计框图_OLED显示屏,行驱动电路设计,单片机AT89C51与和显示屏的硬件接线...
  15. 分享5款提高工作效率的CAD插件,总有一款适合你
  16. MAX485芯片收发详解 实现485通信
  17. 【位操作笔记】位合并 通过掩码
  18. 农村信用社改革试点专项借款管理办法(银发[2003]181号)
  19. RGB 转换至 YCbCr (YUV) 的计算公式
  20. 2019牛客暑期多校训练营(第八场) CDMA

热门文章

  1. 代码随想录第四天 leetcode 24、19、206
  2. node scripts/install.js 安装失败解决办法
  3. linux uvc 拍照程序,Linux UVC driver
  4. 【有奖征询】可查询商票及企业境外债软件有奖征询
  5. 英语的计算机软件如何拼写,软的英文单词
  6. 符号常量和常变量的区别
  7. Docker 基础之Dockerfile命令详解
  8. oracle ndb,NDB语法 - ivaneeo's blog - BlogJava
  9. “三权分立”模型之角色模型
  10. java workflow 教程_建议收藏,从零开始创建一个Activiti工作流,手把手教你完成...