Ruby 中的对象
在 Ruby 教程的这一部分中,我们将简要介绍 Ruby 语言中对象的概念。 我们将在 OOP 一章中了解有关对象的更多信息。 我写了有关对象的本章,因为许多 Ruby 功能可能会使新手感到困惑,特别是如果他们已经知道任何其他编程语言的话。
Ruby 是一种面向对象的编程语言。 这意味着在 Ruby 程序中,我们使用对象。 从语言程序员的角度来看,Ruby 程序是令牌流。 这些标记是 Ruby 关键字,运算符,各种分隔符或字面值。 从语义的角度来看,Ruby 程序由对象组成。 这些对象是在 Ruby 脚本的生存期内创建和修改的。
有两种对象:内置对象和自定义对象。 内置对象是所有程序员都可以使用的预定义对象。 它们以 Ruby 语言的核心或各种库提供。 定制对象是由应用员为其应用域创建的。
必须先创建所有对象,然后才能使用它们。 我们经常使用术语对象实例化。 它是对象创建的同义词。 对象由数据和方法组成。 数据是对象的静态部分。 方法构成对象的动态部分。 对象被修改并通过方法彼此通信。
#!/usr/bin/ruby
puts "Ruby language"
我们有一个简单的 Ruby 脚本。 如果我们熟悉某些程序语言(例如 Pascal 或 C),我们可能会看到一个名为puts
的关键字或函数及其参数"Ruby language"
(它是一个字符串)。
Ruby 是一种纯粹的面向对象的语言,情况有所不同。 "Ruby language"
确实是一个字符串,它是一种常见的数据类型。 但这也是一个对象。 与所有对象一样,我们可以调用其方法。 这与其他语言有点不同。 puts
是一种方法。 方法是在对象中定义的函数。 方法本身并不存在。 实际上,puts
方法是Kernel
模块的一部分。
#!/usr/bin/ruby
Kernel.puts "Ruby language"
Kernel.puts "Ruby language".size
在上面的脚本中,我们有两个代码行。
Kernel.puts "Ruby language"
在第一个示例中,我们调用的puts
方法没有Kernel
部分,可以将其省略。 这样可以节省时间和一些打字。 实际上,这是Kernel.puts
正式电话的简写。 在 C# 中,我们有Console.writeln
在 Java 中System.println
。 想法是一样的。 方法必须与某个对象关联,或者,如果是类方法,则必须与一个类关联。
Kernel.puts "Ruby language".size
在此代码行中,我们将"Ruby language"
字符串的大小打印到控制台。 这可能会使使用其他语言编写代码的程序员感到困惑。 在其他语言中,字符串是无法修改的原始数据类型,并且缺少其自己的方法。 在 Ruby 中,字符串是完整对象,并且具有自己的方法。 size
方法就是其中一种。 它以字符为单位返回字符串的大小。
$ ./simple2.rb
Ruby language
13
代码示例的输出。
在下面的示例中,我们将看一个整数。 与字符串类似,整数值也是 Ruby 对象。
#!/usr/bin/ruby
puts 6.object_id
puts 6.even?
puts 6.zero?
puts 6.class
在示例中,我们有一个整数 6。我们在数字上调用了一些方法。
puts 6.object_id
6 是一个对象。 object_id
是一种方法。 该方法返回与该对象关联的 ID。 每个对象都有一个 ID。 如果我们在对象上调用方法,则必须始终在两者之间放置点字符。
puts 6.even?
puts 6.zero?
在这里,我们在 6 个对象上调用两个方法。 如果数字为偶数,则even?
返回true
。 如果数字等于零,则zero?
方法返回true
。 请注意,这两种方法都以问号结尾。 这是 Ruby 约定。 返回布尔值的方法以问号结尾。
puts 6.class
class
方法告诉我们正在处理哪种对象。 在我们的例子中 6 是Fixnum
$ ./objectnumber.rb
13
true
false
Fixnum
代码示例输出。
Ruby 对象创建
我们已经提到必须先创建 Ruby 对象,然后才能使用它们。 可以隐式或显式创建对象。 隐式对象创建是通过字面值符号创建对象。 显式对象的创建通过使用new
关键字进行。 始终使用new
关键字创建自定义对象。 必须从特定的类创建自定义对象。 类是对象的模板。 一个类可以用来创建许多对象。
#!/usr/bin/ruby
class Being
end
puts 67
puts "ZetCode"
s = String.new "ZetCode"
puts s
# n1 = Fixnum.new 67
# puts n1
b = Being.new
puts b
该代码示例演示了如何在 Ruby 中创建对象。
class Being
end
这是名为Being
的自定义对象的模板。 使用class
关键字创建模板。 自定义对象的模板通常放在源文件的顶部或单独的 Ruby 文件中。
puts 67
puts "ZetCode"
在这两行中,我们使用两个对象。 Fixnum
类型的 67 对象和String
类型的"ZetCode"
字符串。 67 和"ZetCode"
是我们所谓的字面值。 字面值是类型的特定值的字面值表示。 这两个对象是由 Ruby 解释器在后台创建的。 Ruby 中的某些对象是通过在源代码中指定其字面值来创建的。
s = String.new "ZetCode"
puts s
这是创建String
对象的正式方法。 它等于之前使用字符串字面值的隐式创建。
# n1 = Fixnum.new 67
# puts n1
并非所有内置对象都可以使用new
方法创建。 此代码无法编译。 到目前为止,Fixnum
数字只能用字面值表示法创建。
b = Being.new
puts b
在这里,我们创建了自定义对象的实例。 puts
方法为我们简要介绍了该对象。
$ ./ocreation.rb
67
ZetCode
ZetCode
#<Being:0x9944d9c>
示例的输出。
我们将继续进行一些正式的对象创建。
#!/usr/bin/ruby
s1 = String.new "Ruby"
puts s1.size
puts s1.downcase
a1 = Array.new
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?
r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4
在示例中,我们创建了三个内置对象并调用了其中的一些方法。
s1 = String.new "Ruby"
puts s1.size
puts s1.downcase
创建一个String
对象。 我们调用对象的两种方法。 size
方法返回字符串的大小。 downcase
方法将字符串的字符小写。
a1 = Array.new
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?
在这里,我们创建一个Array
对象,并向其添加三个数字。 稍后我们调用两个数组方法。 include?
方法检查特定值(在我们的例子中为 3)是否是数组的一部分。 empty?
方法返回一个布尔值,指示该数组是否为空。
r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4
创建Range
类的实例。 它包含从 1 到 6 的数字。class
方法返回对象的名称。 include?
方法检查数字 4 是否在范围内。 就我们而言。
$ ./formal.rb
4
ruby
true
false
Range
true
运行示例将给出此输出。
Ruby 对象字面值
正如我们已经提到的,可以使用对象字面值来创建一些内置对象。 以下示例显示了几个对象字面值。
#!/usr/bin/ruby
4.times { puts "Ruby" }
puts "Ruby".size
puts "Ruby".downcase
puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?
puts :name.class
puts :name.frozen?
puts (1..6).class
puts (1..6).include? 4
在上面的示例中,我们使用字面值符号创建一个Fixnum
,字符串,数组,符号和范围。
4.times { puts "Ruby" }
我们可以立即在整数字面值上调用方法。 该行向终端打印四次"Ruby"
字符串。
puts "Ruby".size
puts "Ruby".downcase
我们在用字符串字面值创建的String
对象上调用两个方法。
puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?
在这里,我们使用数组字面值符号创建两个Array
对象。 我们使用include?
方法检查特定的数字是否是数组的一部分。 empty?
方法检查数组对象是否为空。
puts :name.class
puts :name.frozen?
调用 Symbol 对象的两种方法。 该符号是用一个以冒号开头的符号字面值创建的。
puts (1..6).class
puts (1..6).include? 4
使用范围字面值创建两个Range
对象。 我们在那些对象上调用两个方法。 class
方法返回类的名称,include?
方法检查给定数字是否在范围内。
$ ./literals.rb
Ruby
Ruby
Ruby
Ruby
4
ruby
true
false
Symbol
false
Range
true
示例输出。
Ruby 对象层次结构
在许多面向对象的语言中,对象形成层次结构。 Ruby 也具有对象层次结构。 它是一个树状的层次结构,其中有父对象和子对象。 对象从其父对象继承数据和行为。 在层次结构的顶部,有根对象。 它称为Object
。 Ruby 中的每个对象都有至少一个父对象。 换句话说,每个对象都继承自基本的Object
对象。
根据 Ruby 的官方文档,Object
是 Ruby 类层次结构的根。 除非明确重写,否则它的方法可用于所有类。
#!/usr/bin/ruby
puts 4.is_a? Object
puts "Ruby".is_a? Object
puts [2, 3].is_a? Object
puts :name.is_a? Object
puts (1..2).is_a? Object
在上面的代码示例中,我们演示了所有对象均从根Object
继承
puts 4.is_a? Object
我们使用is_a?
方法检查数字是否为特定类型:换句话说,数字是否继承自给定的对象类型。
$ ./mother.rb
true
true
true
true
true
所有方法均返回true
,这意味着所有对象均从母对象继承。
即使对于非常基本的 Ruby 对象,继承层次结构也可能非常复杂。
#!/usr/bin/ruby
puts 6.class
puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum
puts 6.is_a? Bignum
puts 6.is_a? String
在此示例中,我们对较小数值的继承层次结构有所了解。
puts 6.class
我们找出数字值为 6 的对象是什么。该行将Fixnum
打印到控制台。
puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum
以上所有行均返回true
。 数字 6 是Fixnum
。 从 Ruby 文档中,我们发现其他四个对象是Fixnum
对象的父对象。
puts 6.is_a? Bignum
puts 6.is_a? String
以上两个对象不是 6 值的父对象。
$ ./inheritance.rb
Fixnum
true
true
true
true
true
false
false
示例的输出。
我们将以一个示例结束本节,该示例演示自定义用户对象的继承。
#!/usr/bin/ruby
class Being
def to_s
"This is Being"
end
def get_id
9
end
end
class Living < Being
def to_s
"This is Living"
end
end
l = Living.new
puts l
puts l.get_id
puts l.is_a? Being
puts l.is_a? Object
puts l.is_a? BasicObject
在示例中,我们创建两个对象Being
和Living
。 Living
对象继承自Being
。 第一个是父对象,第二个是子对象。
class Being
def to_s
"This is Being"
end
def get_id
9
end
end
这是自定义 Ruby 对象的定义。 定义位于class
和end
关键字之间。 在定义内部,我们创建两个方法。 当puts
方法将对象作为参数时,它将调用其to_s
方法。 它通常给出对象的字符串表示/描述。
class Living < Being
def to_s
"This is Living"
end
end
我们创建Living
对象的定义。 该对象继承自Being
对象。 <
运算符用于创建继承关系。 to_s
方法被覆盖。
l = Living.new
从上面的Living
对象模板,我们创建Living
对象的实例。 使用new
关键字创建自定义对象的实例。
puts l
puts
方法调用Living
对象的to_s
方法。 如果Living
类中未定义to_s
方法,则将调用Being
类中的to_s
方法。
puts l.get_id
Living
对象未定义get_id
方法。 在这种情况下,将检查父类是否存在这种方法。 在我们的情况下,Being
方法具有这种方法,因此被称为。
puts l.is_a? Being
该行返回true
。 Living
是Being
的一种; 例如它继承自Being
类。
puts l.is_a? Object
puts l.is_a? BasicObject
对于我们的Living
自定义对象,我们没有明确指定与Object
或BasicObject
对象的任何关系。 但是这两行返回true
。 这是因为 Ruby 中的每个对象自动都是这两个对象的后代。 这是由 Ruby 解释器在后台完成的。
$ ./custominher.rb
This is Living
9
true
true
true
Output of the example.
Ruby 顶级
Ruby 有一个特定的对象,称为 Ruby 顶级。 这是在任何其他上下文(例如类或模块定义)之外定义的默认执行环境。 顶级名称为main
。 它是Object
类型的实例。 有一个与main
关联的局部空间,所有局部变量都驻留在该空间中。
#!/usr/bin/ruby
n1 = 3
n2 = 5
puts local_variables
Kernel.puts self
puts self.class
这是描述 Ruby 顶层的第一个示例。
n1 = 3
n2 = 5
在这里,我们定义了两个数值变量。 这些变量是本地变量。
puts local_variables
在这里,我们列出了所有局部变量。 local_variables
是Kernel
模块的一种方法,已混入每个Object
(包括顶层对象)中。
Kernel.puts self
self
是 Ruby 伪变量。 它返回当前的对象接收者。 该行将"main"
打印到控制台。 它是 Ruby 顶层的名称。 Kernel.puts
代码的Kernel
部分可以省略。 通过完全指定名称,我们显示puts
方法属于Kernel
模块。
puts self.class
该行显示顶层的class
。 我们得到顶层的对象类型。 它是Object
,它是 Ruby 类层次结构的根。
$ ./toplevel.rb
n1
n2
main
Object
这是示例的输出。 n1
和n2
是与顶层关联的局部变量。 主要是为 Ruby 顶级执行环境指定的名称。 最后,Object
是顶层的类型。
我们将有另一个与 Ruby 顶层相关的示例。
#!/usr/bin/ruby
@name = "Jane"
@age = 17
def info
"#{@name} is #{@age} years old"
end
puts self.instance_variables
puts self.private_methods.include? :info
puts info
我们显示了属于顶级环境的实例变量和方法。
@name = "Jane"
@age = 17
我们定义了两个实例变量。 实例变量以 Ruby 中的@
字符开头。 实例变量属于特定的对象实例。 在这种情况下,它们属于 Ruby 顶级。
def info
"#{@name} is #{@age} years old"
end
这是一个方法定义。 每个方法必须属于某个对象。 此方法属于顶级对象。 所有顶级方法都是私有的。 私有方法的访问受到限制。
puts self.instance_variables
instance_variables
方法打印出self
的所有实例变量,在此上下文中指向 Ruby 顶层。
puts self.private_methods.include? :info
所有顶级方法都是自动私有的。 private_methods
返回对象的所有私有方法。 由于方法很多,我们调用include?
方法来检查info
方法是否在其中。 请注意,我们通过符号名称来引用info
方法。
$ ./toplevel2.rb
@name
@age
true
Jane is 17 years old
示例输出。
本章介绍了 Ruby 语言中对象的一些基础知识。