- Ruby 基础
- Ruby - 首页
- Ruby - 概述
- Ruby - 环境搭建
- Ruby - 语法
- Ruby - 类和对象
- Ruby - 变量
- Ruby - 运算符
- Ruby - 注释
- Ruby - IF...ELSE
- Ruby - 循环
- Ruby - 方法
- Ruby - 块
- Ruby - 模块
- Ruby - 字符串
- Ruby - 数组
- Ruby - 哈希表
- Ruby - 日期和时间
- Ruby - 范围
- Ruby - 迭代器
- Ruby - 文件 I/O
- Ruby - 异常
Ruby - 面向对象
Ruby 是一种纯面向对象的语言,在 Ruby 中,一切皆对象。Ruby 中的每个值都是对象,即使是最原始的东西:字符串、数字,甚至是真假值。甚至类本身也是一个对象,它是 Class 类的实例。本章将带您了解与面向对象 Ruby 相关的所有主要功能。
类用于指定对象的形态,它将数据表示和操作该数据的方法组合成一个简洁的包。类中的数据和方法称为类的成员。
Ruby 类定义
定义类时,您定义了数据类型的蓝图。这实际上并没有定义任何数据,但它确实定义了类名的含义,即类的对象将包含什么以及可以对这样的对象执行哪些操作。
类定义以关键字 **class** 后跟 **类名** 开始,并以 **end** 结束。例如,我们使用关键字 class 定义了 Box 类,如下所示:
class Box code end
名称必须以大写字母开头,按照惯例,包含多个单词的名称将连在一起,每个单词的首字母大写,并且没有分隔符(驼峰命名法)。
定义 Ruby 对象
类为对象提供蓝图,因此基本上对象是从类创建的。我们使用 **new** 关键字声明类的对象。以下语句声明了两个 Box 类的对象:
box1 = Box.new box2 = Box.new
initialize 方法
**initialize 方法** 是标准的 Ruby 类方法,其工作方式与其他面向对象编程语言中的 **构造函数** 几乎相同。当您想要在创建对象时初始化一些类变量时,initialize 方法很有用。此方法可以接受参数列表,并且像任何其他 ruby 方法一样,它前面都会加上 **def** 关键字,如下所示:
class Box
def initialize(w,h)
@width, @height = w, h
end
end
实例变量
**实例变量** 是一种类属性,一旦使用类创建对象,它们就成为对象的属性。每个对象的属性都是单独分配的,与其他对象不共享任何值。它们在类中使用 @ 运算符访问,但要在类外部访问它们,我们使用 **public** 方法,这些方法称为 **访问器方法**。如果我们采用上面定义的类 **Box**,则 @width 和 @height 是 Box 类的实例变量。
class Box
def initialize(w,h)
# assign instance variables
@width, @height = w, h
end
end
访问器和设置器方法
为了使变量能够从类外部访问,必须在 **访问器方法** 中定义它们,这些访问器方法也称为 getter 方法。下面的例子展示了访问器方法的使用:
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# accessor methods
def printWidth
@width
end
def printHeight
@height
end
end
# create an object
box = Box.new(10, 20)
# use accessor methods
x = box.printWidth()
y = box.printHeight()
puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"
执行上述代码时,会产生以下结果:
Width of the box is : 10 Height of the box is : 20
与用于访问变量值的访问器方法类似,Ruby 提供了一种方法,可以使用 **设置器方法** 从类外部设置这些变量的值,定义如下:
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# accessor methods
def getWidth
@width
end
def getHeight
@height
end
# setter methods
def setWidth=(value)
@width = value
end
def setHeight=(value)
@height = value
end
end
# create an object
box = Box.new(10, 20)
# use setter methods
box.setWidth = 30
box.setHeight = 50
# use accessor methods
x = box.getWidth()
y = box.getHeight()
puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"
执行上述代码时,会产生以下结果:
Width of the box is : 30 Height of the box is : 50
实例方法
**实例方法** 也与我们使用 **def** 关键字定义任何其他方法的方式相同,并且只能使用类实例来使用它们,如下所示。它们的功能不限于访问实例变量,还可以根据您的需求执行更多操作。
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method
def getArea
@width * @height
end
end
# create an object
box = Box.new(10, 20)
# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"
执行上述代码时,会产生以下结果:
Area of the box is : 200
类方法和变量
**类变量** 是一个变量,它在类的所有实例之间共享。换句话说,该变量只有一个实例,并且可以通过对象实例访问它。类变量以两个 @ 字符 (@@) 为前缀。类变量必须在类定义中初始化,如下所示。
类方法使用 **def self.methodname()** 定义,它以 end 分隔符结尾,并将使用类名作为 **classname.methodname** 来调用,如以下示例所示:
#!/usr/bin/ruby -w
class Box
# Initialize our class variables
@@count = 0
def initialize(w,h)
# assign instance avriables
@width, @height = w, h
@@count += 1
end
def self.printCount()
puts "Box count is : #@@count"
end
end
# create two object
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)
# call class method to print box count
Box.printCount()
执行上述代码时,会产生以下结果:
Box count is : 2
to_s 方法
您定义的任何类都应该具有 **to_s** 实例方法以返回对象的字符串表示形式。以下是一个简单的示例,用于根据宽度和高度表示 Box 对象:
#!/usr/bin/ruby -w
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# define to_s method
def to_s
"(w:#@width,h:#@height)" # string formatting of the object.
end
end
# create an object
box = Box.new(10, 20)
# to_s method will be called in reference of string automatically.
puts "String representation of box is : #{box}"
执行上述代码时,会产生以下结果:
String representation of box is : (w:10,h:20)
访问控制
Ruby 在实例方法级别提供了三个级别的保护,可以是 **public、private 或 protected**。Ruby 对实例变量和类变量不应用任何访问控制。
**公共方法** - 公共方法可以被任何人调用。方法默认情况下是公共的,除了 initialize,它始终是私有的。
**私有方法** - 私有方法无法从类外部访问,甚至无法查看。只有类方法可以访问私有成员。
**受保护的方法** - 受保护的方法只能由定义类及其子类的对象调用。访问权限保持在家族内部。
以下是一个简单的示例,用于演示所有三种访问修饰符的语法:
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method by default it is public
def getArea
getWidth() * getHeight
end
# define private accessor methods
def getWidth
@width
end
def getHeight
@height
end
# make them private
private :getWidth, :getHeight
# instance method to print area
def printArea
@area = getWidth() * getHeight
puts "Big box area is : #@area"
end
# make it protected
protected :printArea
end
# create an object
box = Box.new(10, 20)
# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"
# try to call protected or methods
box.printArea()
执行上述代码时,会产生以下结果。这里,第一个方法成功调用,但第二个方法出现了问题。
Area of the box is : 200 test.rb:42: protected method `printArea' called for # <Box:0xb7f11280 @height = 20, @width = 10> (NoMethodError)
类继承
面向对象编程中最重要的概念之一是继承。继承允许我们根据另一个类来定义类,这使得创建和维护应用程序更加容易。
继承还提供了重用代码功能和加快实现时间的机会,但不幸的是,Ruby 不支持多级继承,但 Ruby 支持 **mixin**。mixin 类似于多重继承的专门实现,其中只继承接口部分。
创建类时,程序员可以指定新类应该继承现有类的成员,而不是编写全新的数据成员和成员函数。这个现有类称为 **基类或超类**,新类称为 **派生类或子类**。
Ruby 还支持子类化,即继承,以下示例解释了这个概念。扩展类的语法很简单。只需将 < 字符和超类的名称添加到您的类语句中即可。例如,以下将定义一个类 *BigBox* 作为 *Box* 的子类:
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method
def getArea
@width * @height
end
end
# define a subclass
class BigBox < Box
# add a new instance method
def printArea
@area = @width * @height
puts "Big box area is : #@area"
end
end
# create an object
box = BigBox.new(10, 20)
# print the area
box.printArea()
执行上述代码时,会产生以下结果:
Big box area is : 200
方法重写
虽然您可以在派生类中添加新功能,但有时您可能想要更改父类中已定义方法的行为。您可以简单地保持方法名称相同并重写方法的功能,如下面的示例所示:
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method
def getArea
@width * @height
end
end
# define a subclass
class BigBox < Box
# change existing getArea method as follows
def getArea
@area = @width * @height
puts "Big box area is : #@area"
end
end
# create an object
box = BigBox.new(10, 20)
# print the area using overriden method.
box.getArea()
运算符重载
我们希望 + 运算符使用 + 执行两个 Box 对象的向量加法,* 运算符将 Box 的宽度和高度乘以标量,而一元 - 运算符则对 Box 的宽度和高度取反。这是一个具有定义的数学运算符的 Box 类的版本:
class Box
def initialize(w,h) # Initialize the width and height
@width,@height = w, h
end
def +(other) # Define + to do vector addition
Box.new(@width + other.width, @height + other.height)
end
def -@ # Define unary minus to negate width and height
Box.new(-@width, -@height)
end
def *(scalar) # To perform scalar multiplication
Box.new(@width*scalar, @height*scalar)
end
end
冻结对象
有时,我们希望防止对象被更改。Object 中的 freeze 方法允许我们做到这一点,有效地将对象转换为常量。可以通过调用 **Object.freeze** 来冻结任何对象。冻结的对象可能不会被修改:您不能更改其实例变量。
您可以使用 **Object.frozen?** 方法检查给定对象是否已冻结,如果对象已冻结,则返回 true,否则返回 false 值。以下示例阐明了这个概念:
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# accessor methods
def getWidth
@width
end
def getHeight
@height
end
# setter methods
def setWidth=(value)
@width = value
end
def setHeight=(value)
@height = value
end
end
# create an object
box = Box.new(10, 20)
# let us freez this object
box.freeze
if( box.frozen? )
puts "Box object is frozen object"
else
puts "Box object is normal object"
end
# now try using setter methods
box.setWidth = 30
box.setHeight = 50
# use accessor methods
x = box.getWidth()
y = box.getHeight()
puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"
执行上述代码时,会产生以下结果:
Box object is frozen object test.rb:20:in `setWidth=': can't modify frozen object (TypeError) from test.rb:39
类常量
您可以通过将直接数值或字符串值赋值给变量来定义类中的常量,该变量是在不使用 @ 或 @@ 的情况下定义的。按照惯例,我们将常量名称保留为大写。
定义常量后,您无法更改其值,但您可以像变量一样直接在类内访问常量,但是如果您想在类外访问常量,则必须使用 **classname::constant**,如下例所示。
#!/usr/bin/ruby -w
# define a class
class Box
BOX_COMPANY = "TATA Inc"
BOXWEIGHT = 10
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method
def getArea
@width * @height
end
end
# create an object
box = Box.new(10, 20)
# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"
执行上述代码时,会产生以下结果:
Area of the box is : 200 TATA Inc Box weight is: 10
类常量是继承的,并且可以像实例方法一样被重写。
使用 Allocate 创建对象
可能存在您想要在不调用其构造函数 **initialize**(即不使用 new 方法)的情况下创建对象的情况,在这种情况下,您可以调用 *allocate*,它将为您创建一个未初始化的对象,如下例所示:
#!/usr/bin/ruby -w
# define a class
class Box
attr_accessor :width, :height
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method
def getArea
@width * @height
end
end
# create an object using new
box1 = Box.new(10, 20)
# create another object using allocate
box2 = Box.allocate
# call instance method using box1
a = box1.getArea()
puts "Area of the box is : #{a}"
# call instance method using box2
a = box2.getArea()
puts "Area of the box is : #{a}"
执行上述代码时,会产生以下结果:
Area of the box is : 200 test.rb:14: warning: instance variable @width not initialized test.rb:14: warning: instance variable @height not initialized test.rb:14:in `getArea': undefined method `*' for nil:NilClass (NoMethodError) from test.rb:29
类信息
如果类定义是可执行代码,这意味着它们在某个对象的上下文中执行:self 必须引用某些内容。让我们找出它是什么。
#!/usr/bin/ruby -w
class Box
# print class information
puts "Type of self = #{self.type}"
puts "Name of self = #{self.name}"
end
执行上述代码时,会产生以下结果:
Type of self = Class Name of self = Box
这意味着类定义是使用该类作为当前对象执行的。这意味着元类及其超类中的方法在方法定义的执行过程中将可用。